[core] Add cg.add_optional for optional plain config values (#21180)

This commit is contained in:
J. Nick Koston
2026-10-20 08:38:02 -04:00
committed by GitHub
parent fd902a646a
commit 6304411341
4 changed files with 35 additions and 2 deletions
+4 -2
View File
@@ -80,13 +80,15 @@ file does, and it is the authority when they disagree. The most useful starting
```python
# Bad - looks up CONF_BLAH twice
if CONF_BLAH in config:
cg.add(var.set_blah(config[CONF_BLAH]))
cg.add(var.set_blah_ms(config[CONF_BLAH].total_milliseconds))
# Good - single lookup, value bound inline
if (blah := config.get(CONF_BLAH)) is not None:
cg.add(var.set_blah(blah))
cg.add(var.set_blah_ms(blah.total_milliseconds))
```
The same applies to `while` loops and comprehensions where it avoids recomputing a value. Don't contort code to use it — reach for `:=` only when it genuinely cuts repetition or an extra assignment line.
When the setter takes the value unchanged, use `cg.add_optional(var.set_blah, config.get(CONF_BLAH))`
instead; it is not awaited, and it skips only `None`, so `0` and `False` are still set.
* **C++ Field Visibility:**
* **Prefer `protected`:** Use `protected` for most class fields to enable extensibility and testing. Fields should be `lower_snake_case_with_trailing_underscore_`.
+1
View File
@@ -53,6 +53,7 @@ from esphome.cpp_generator import ( # noqa: F401
with_local_variable,
)
from esphome.cpp_helpers import ( # noqa: F401
add_optional,
add_pin,
add_variable,
build_registry_entry,
+7
View File
@@ -251,6 +251,13 @@ async def add_variable(
return var
def add_optional[T](setter: Callable[[T], Expression], value: T | None) -> T | None:
"""Pass ``value`` to ``setter`` and return it; skip None."""
if value is not None:
add(setter(value))
return value
def set_setup_priority(var, priority: float) -> None:
"""Emit a setup-priority override for the given component.
+23
View File
@@ -339,3 +339,26 @@ async def test_add_helpers_pass_value_to_setter(
lookup_mock.assert_awaited_once_with(arg)
setter.assert_called_once_with(value)
add_mock.assert_called_once_with(setter.return_value)
@pytest.mark.parametrize("value", [0, False, "", 5])
def test_add_optional_passes_value_to_setter(
monkeypatch: pytest.MonkeyPatch, value: Any
) -> None:
add_mock = Mock()
monkeypatch.setattr(ch, "add", add_mock)
setter = Mock()
assert ch.add_optional(setter, value) is value
setter.assert_called_once_with(value)
add_mock.assert_called_once_with(setter.return_value)
def test_add_optional_skips_none(monkeypatch: pytest.MonkeyPatch) -> None:
add_mock = Mock()
monkeypatch.setattr(ch, "add", add_mock)
setter = Mock()
assert ch.add_optional(setter, None) is None
setter.assert_not_called()
add_mock.assert_not_called()