Bump ruff from 0.15.22 to 0.16.0 (#17818)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
This commit is contained in:
dependabot[bot]
2026-07-27 12:08:51 -04:00
committed by GitHub
co-authored by dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Jonathan Swoboda
parent 071ef5016d
commit 345bd11a2c
9 changed files with 88 additions and 39 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ ci:
repos: repos:
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version. # Ruff version.
rev: v0.15.15 rev: v0.16.0
hooks: hooks:
# Run the linter. # Run the linter.
- id: ruff - id: ruff
+45 -18
View File
@@ -191,11 +191,14 @@ This document provides essential context for AI models interacting with this pro
my_component_ns = cg.esphome_ns.namespace("my_component") my_component_ns = cg.esphome_ns.namespace("my_component")
MyComponent = my_component_ns.class_("MyComponent", cg.Component) MyComponent = my_component_ns.class_("MyComponent", cg.Component)
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(): cv.declare_id(MyComponent), {
cv.Required(CONF_KEY): cv.string, cv.GenerateID(): cv.declare_id(MyComponent),
cv.Optional(CONF_PARAM, default=42): cv.int_, cv.Required(CONF_KEY): cv.string,
}).extend(cv.COMPONENT_SCHEMA) cv.Optional(CONF_PARAM, default=42): cv.int_,
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
@@ -229,7 +232,12 @@ This document provides essential context for AI models interacting with this pro
- **Sensor:** - **Sensor:**
```python ```python
from esphome.components import sensor from esphome.components import sensor
CONFIG_SCHEMA = sensor.sensor_schema(MySensor).extend(cv.polling_component_schema("60s"))
CONFIG_SCHEMA = sensor.sensor_schema(MySensor).extend(
cv.polling_component_schema("60s")
)
async def to_code(config): async def to_code(config):
var = await sensor.new_sensor(config) var = await sensor.new_sensor(config)
await cg.register_component(var, config) await cg.register_component(var, config)
@@ -238,7 +246,10 @@ This document provides essential context for AI models interacting with this pro
- **Binary Sensor:** - **Binary Sensor:**
```python ```python
from esphome.components import binary_sensor from esphome.components import binary_sensor
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema().extend({ ... })
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema().extend({...})
async def to_code(config): async def to_code(config):
var = await binary_sensor.new_binary_sensor(config) var = await binary_sensor.new_binary_sensor(config)
``` ```
@@ -246,7 +257,10 @@ This document provides essential context for AI models interacting with this pro
- **Switch:** - **Switch:**
```python ```python
from esphome.components import switch from esphome.components import switch
CONFIG_SCHEMA = switch.switch_schema().extend({ ... })
CONFIG_SCHEMA = switch.switch_schema().extend({...})
async def to_code(config): async def to_code(config):
var = await switch.new_switch(config) var = await switch.new_switch(config)
``` ```
@@ -263,10 +277,13 @@ This document provides essential context for AI models interacting with this pro
```python ```python
from esphome import automation from esphome import automation
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(): cv.declare_id(MyComponent), {
cv.Optional(CONF_ON_STATE): automation.validate_automation({}), cv.GenerateID(): cv.declare_id(MyComponent),
}).extend(cv.COMPONENT_SCHEMA) cv.Optional(CONF_ON_STATE): automation.validate_automation({}),
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
@@ -316,11 +333,14 @@ This document provides essential context for AI models interacting with this pro
```python ```python
TurnOnTrigger = my_ns.class_("TurnOnTrigger", automation.Trigger.template()) TurnOnTrigger = my_ns.class_("TurnOnTrigger", automation.Trigger.template())
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = cv.Schema(
cv.Optional(CONF_ON_TURN_ON): automation.validate_automation( {
{cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TurnOnTrigger)} cv.Optional(CONF_ON_TURN_ON): automation.validate_automation(
), {cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TurnOnTrigger)}
}) ),
}
)
async def to_code(config): async def to_code(config):
for conf in config.get(CONF_ON_TURN_ON, []): for conf in config.get(CONF_ON_TURN_ON, []):
@@ -617,6 +637,7 @@ This document provides essential context for AI models interacting with this pro
_component_state = [] _component_state = []
_use_feature = None _use_feature = None
def enable_feature(): def enable_feature():
global _use_feature global _use_feature
_use_feature = True _use_feature = True
@@ -636,20 +657,24 @@ This document provides essential context for AI models interacting with this pro
DOMAIN = "my_component" DOMAIN = "my_component"
@dataclass @dataclass
class MyComponentData: class MyComponentData:
feature_enabled: bool = False feature_enabled: bool = False
item_count: int = 0 item_count: int = 0
items: list[str] = field(default_factory=list) items: list[str] = field(default_factory=list)
def _get_data() -> MyComponentData: def _get_data() -> MyComponentData:
if DOMAIN not in CORE.data: if DOMAIN not in CORE.data:
CORE.data[DOMAIN] = MyComponentData() CORE.data[DOMAIN] = MyComponentData()
return CORE.data[DOMAIN] return CORE.data[DOMAIN]
def request_feature() -> None: def request_feature() -> None:
_get_data().feature_enabled = True _get_data().feature_enabled = True
def add_item(item: str) -> None: def add_item(item: str) -> None:
_get_data().items.append(item) _get_data().items.append(item)
``` ```
@@ -707,7 +732,9 @@ This document provides essential context for AI models interacting with this pro
```python ```python
# Remove before 2026.6.0 # Remove before 2026.6.0
if CONF_OLD_KEY in config: if CONF_OLD_KEY in config:
_LOGGER.warning(f"'{CONF_OLD_KEY}' deprecated, use '{CONF_NEW_KEY}'. Removed in 2026.6.0") _LOGGER.warning(
f"'{CONF_OLD_KEY}' deprecated, use '{CONF_NEW_KEY}'. Removed in 2026.6.0"
)
config[CONF_NEW_KEY] = config.pop(CONF_OLD_KEY) # Auto-migrate config[CONF_NEW_KEY] = config.pop(CONF_OLD_KEY) # Auto-migrate
``` ```
## 9. English Language ## 9. English Language
+1
View File
@@ -151,6 +151,7 @@ ignore = [
"PLR0912", # Too many branches ({branches} > {max_branches}) "PLR0912", # Too many branches ({branches} > {max_branches})
"PLR0913", # Too many arguments to function call ({c_args} > {max_args}) "PLR0913", # Too many arguments to function call ({c_args} > {max_args})
"PLR0915", # Too many statements ({statements} > {max_statements}) "PLR0915", # Too many statements ({statements} > {max_statements})
"PLR0917", # Too many positional arguments ({c_pos} > {max_pos})
"PLW1641", # Object does not implement `__hash__` method "PLW1641", # Object does not implement `__hash__` method
"PLR2004", # Magic value used in comparison, consider replacing {value} with a constant variable "PLR2004", # Magic value used in comparison, consider replacing {value} with a constant variable
"PLW2901", # Outer {outer_kind} variable {name} overwritten by inner {inner_kind} target "PLW2901", # Outer {outer_kind} variable {name} overwritten by inner {inner_kind} target
+1 -1
View File
@@ -1,6 +1,6 @@
pylint==4.0.6 pylint==4.0.6
flake8==7.3.0 # also change in .pre-commit-config.yaml when updating flake8==7.3.0 # also change in .pre-commit-config.yaml when updating
ruff==0.15.22 # also change in .pre-commit-config.yaml when updating ruff==0.16.0 # also change in .pre-commit-config.yaml when updating
pyupgrade==3.21.2 # also change in .pre-commit-config.yaml when updating pyupgrade==3.21.2 # also change in .pre-commit-config.yaml when updating
pre-commit pre-commit
+19 -13
View File
@@ -345,9 +345,11 @@ def lint_const_ordered(fname, content):
( (
mi, mi,
1, 1,
f"Constant {highlight(mline)} is not ordered, please make sure all " (
f"constants are ordered. See line {mi} (should go to line {target}, " f"Constant {highlight(mline)} is not ordered, please make sure all "
f"{target_text})", f"constants are ordered. See line {mi} (should go to line {target}, "
f"{target_text})"
),
) )
) )
return errs return errs
@@ -990,12 +992,14 @@ def lint_log_multiline_continuation(fname, content):
( (
lineno, lineno,
col, col,
"Multi-line log message has a continuation line that does " (
"not start with a space. The log viewer uses leading " "Multi-line log message has a continuation line that does "
"whitespace to detect continuation lines and re-add the " "not start with a space. The log viewer uses leading "
f"log tag prefix (e.g. {highlight('[C][component:042]:')}).\n" "whitespace to detect continuation lines and re-add the "
"Either start the continuation with a space/indent, or " f"log tag prefix (e.g. {highlight('[C][component:042]:')}).\n"
"split into separate ESP_LOG* calls.", "Either start the continuation with a space/indent, or "
"split into separate ESP_LOG* calls."
),
) )
) )
return errs return errs
@@ -1073,10 +1077,12 @@ def lint_test_package_key_matches_bus(fname, content):
( (
lineno, lineno,
1, 1,
f"Package key {highlight(pkg_key)} does not match bus directory " (
f"{highlight(bus_dir)}. The package key must match the directory " f"Package key {highlight(pkg_key)} does not match bus directory "
f"name under tests/test_build_components/common/. " f"{highlight(bus_dir)}. The package key must match the directory "
f"Change {highlight(pkg_key)} to {highlight(bus_dir)}.", f"name under tests/test_build_components/common/. "
f"Change {highlight(pkg_key)} to {highlight(bus_dir)}."
),
) )
) )
return errs return errs
+3
View File
@@ -28,6 +28,7 @@ create an `__init__.py` in your component's test directory and define `override_
```python ```python
from tests.testing_helpers import ComponentManifestOverride from tests.testing_helpers import ComponentManifestOverride
def override_manifest(manifest: ComponentManifestOverride) -> None: def override_manifest(manifest: ComponentManifestOverride) -> None:
# Re-enable the component's own to_code (needed when the component must # Re-enable the component's own to_code (needed when the component must
# emit C++ setup code that the test binary depends on at link time). # emit C++ setup code that the test binary depends on at link time).
@@ -39,6 +40,7 @@ Or supply a lightweight stub instead of the real `to_code`:
```python ```python
from tests.testing_helpers import ComponentManifestOverride from tests.testing_helpers import ComponentManifestOverride
def override_manifest(manifest: ComponentManifestOverride) -> None: def override_manifest(manifest: ComponentManifestOverride) -> None:
async def to_code_testing(config): async def to_code_testing(config):
# Only emit what the C++ tests actually need # Only emit what the C++ tests actually need
@@ -54,6 +56,7 @@ e.g. `tests/components/my_sensor/sensor/__init__.py`):
```python ```python
from tests.testing_helpers import ComponentManifestOverride from tests.testing_helpers import ComponentManifestOverride
def override_manifest(manifest: ComponentManifestOverride) -> None: def override_manifest(manifest: ComponentManifestOverride) -> None:
manifest.enable_codegen() manifest.enable_codegen()
``` ```
+8
View File
@@ -187,6 +187,7 @@ loop = asyncio.get_running_loop()
states: dict[int, EntityState] = {} states: dict[int, EntityState] = {}
state_future: asyncio.Future[EntityState] = loop.create_future() state_future: asyncio.Future[EntityState] = loop.create_future()
def on_state(state: EntityState) -> None: def on_state(state: EntityState) -> None:
"""This callback only receives NEW state changes, not initial states.""" """This callback only receives NEW state changes, not initial states."""
states[state.key] = state states[state.key] = state
@@ -195,6 +196,7 @@ def on_state(state: EntityState) -> None:
if not state_future.done(): if not state_future.done():
state_future.set_result(state) state_future.set_result(state)
# Get entities and set up state synchronization # Get entities and set up state synchronization
entities, services = await client.list_entities_services() entities, services = await client.list_entities_services()
initial_state_helper = InitialStateHelper(entities) initial_state_helper = InitialStateHelper(entities)
@@ -228,6 +230,7 @@ loop = asyncio.get_running_loop()
states: dict[int, EntityState] = {} states: dict[int, EntityState] = {}
state_future: asyncio.Future[EntityState] = loop.create_future() state_future: asyncio.Future[EntityState] = loop.create_future()
def on_state(state: EntityState) -> None: def on_state(state: EntityState) -> None:
states[state.key] = state states[state.key] = state
# Check for specific condition using isinstance # Check for specific condition using isinstance
@@ -235,6 +238,7 @@ def on_state(state: EntityState) -> None:
if not state_future.done(): if not state_future.done():
state_future.set_result(state) state_future.set_result(state)
client.subscribe_states(on_state) client.subscribe_states(on_state)
# Wait for state with timeout # Wait for state with timeout
@@ -263,11 +267,13 @@ entity_count = 50
received_states: set[int] = set() received_states: set[int] = set()
all_states_future: asyncio.Future[bool] = loop.create_future() all_states_future: asyncio.Future[bool] = loop.create_future()
def on_state(state: EntityState) -> None: def on_state(state: EntityState) -> None:
received_states.add(state.key) received_states.add(state.key)
if len(received_states) >= entity_count and not all_states_future.done(): if len(received_states) >= entity_count and not all_states_future.done():
all_states_future.set_result(True) all_states_future.set_result(True)
client.subscribe_states(on_state) client.subscribe_states(on_state)
await asyncio.wait_for(all_states_future, timeout=10.0) await asyncio.wait_for(all_states_future, timeout=10.0)
``` ```
@@ -367,6 +373,7 @@ service_future = loop.create_future()
connected_pattern = re.compile(r"Client .* connected from") connected_pattern = re.compile(r"Client .* connected from")
service_pattern = re.compile(r"Service called") service_pattern = re.compile(r"Service called")
def check_output(line: str) -> None: def check_output(line: str) -> None:
"""Check log output for expected messages.""" """Check log output for expected messages."""
if not connected_future.done() and connected_pattern.search(line): if not connected_future.done() and connected_pattern.search(line):
@@ -374,6 +381,7 @@ def check_output(line: str) -> None:
elif not service_future.done() and service_pattern.search(line): elif not service_future.done() and service_pattern.search(line):
service_future.set_result(True) service_future.set_result(True)
async with run_compiled(yaml_config, line_callback=check_output): async with run_compiled(yaml_config, line_callback=check_output):
async with api_client_connected() as client: async with api_client_connected() as client:
# Wait for specific log message # Wait for specific log message
+6 -4
View File
@@ -71,10 +71,12 @@ def test_branch_manifest_targets_ghcr_only(
) )
assert commands == [ assert commands == [
"docker buildx imagetools create " (
"--tag ghcr.io/esphome/esphome-hassio:my-branch " "docker buildx imagetools create "
"ghcr.io/esphome/esphome-hassio-amd64:my-branch " "--tag ghcr.io/esphome/esphome-hassio:my-branch "
"ghcr.io/esphome/esphome-hassio-aarch64:my-branch" "ghcr.io/esphome/esphome-hassio-amd64:my-branch "
"ghcr.io/esphome/esphome-hassio-aarch64:my-branch"
)
] ]
+4 -2
View File
@@ -1433,8 +1433,10 @@ def test_importing_framework_helpers_does_not_import_requests() -> None:
[ [
sys.executable, sys.executable,
"-c", "-c",
"import sys\nimport esphome.framework_helpers\n" (
"print('\\n'.join(sys.modules))", "import sys\nimport esphome.framework_helpers\n"
"print('\\n'.join(sys.modules))"
),
], ],
capture_output=True, capture_output=True,
text=True, text=True,