mirror of
https://github.com/esphome/esphome.git
synced 2026-09-22 04:28:43 +00:00
[core] Replace mutable-list-flag pattern with _ComponentGroup dataclass
The mutable-list-of-bool trick for rebinding-safe flag mutation works but reads poorly. Replace with a _ComponentGroup dataclass carrying lines + unsafe + no_split fields. cpp_main_section now reads as a straight iteration over typed groups; no apologetic comments needed.
This commit is contained in:
+34
-26
@@ -1,5 +1,6 @@
|
||||
from collections import defaultdict
|
||||
from contextlib import contextmanager
|
||||
from dataclasses import dataclass, field
|
||||
import logging
|
||||
import math
|
||||
import os
|
||||
@@ -539,6 +540,24 @@ class Library:
|
||||
IIFE_MAX_STATEMENTS = 50
|
||||
|
||||
|
||||
@dataclass
|
||||
class _ComponentGroup:
|
||||
"""A contiguous run of statements emitted by one component's to_code."""
|
||||
|
||||
lines: list[str] = field(default_factory=list)
|
||||
# True when the group contains a statement that must affect setup()'s
|
||||
# own control flow (e.g. safe_mode's `return`). Emit the group flat,
|
||||
# bypassing IIFE wrapping entirely.
|
||||
unsafe: bool = False
|
||||
# True when the group contains a statement that may declare a
|
||||
# function-local whose lifetime extends past the current statement
|
||||
# (scope-brace RawStatement, direct RawExpression, typed
|
||||
# AssignmentExpression). Wrap the group in a single IIFE without
|
||||
# sub-splitting so the declaration and any later references stay
|
||||
# in the same lambda.
|
||||
no_split: bool = False
|
||||
|
||||
|
||||
def _emits_bare_local(exp: "Statement") -> bool:
|
||||
"""True if ``exp`` emits a scope brace or bare-raw construct that may
|
||||
declare a function-local whose lifetime extends past the current
|
||||
@@ -1124,43 +1143,32 @@ class EsphomeCore:
|
||||
# sub-split so the declaration and any later references stay
|
||||
# together.
|
||||
prefix: list[str] = []
|
||||
# Flags are stored as 1-element lists so they can be mutated by
|
||||
# reference after the tuple has been pushed into ``components``
|
||||
# (assignment to a local ``unsafe_flag`` inside the loop would
|
||||
# rebind the local without updating the stored tuple entry).
|
||||
# A small dataclass would read cleaner but the pattern is
|
||||
# localized to this one property.
|
||||
components: list[tuple[list[str], list[bool], list[bool]]] = []
|
||||
components: list[_ComponentGroup] = []
|
||||
current: list[str] = prefix
|
||||
unsafe_flag: list[bool] = [False] # unused for prefix
|
||||
no_split_flag: list[bool] = [False] # unused for prefix
|
||||
group: _ComponentGroup | None = None
|
||||
for exp in self.main_statements:
|
||||
if isinstance(exp, ComponentMarker):
|
||||
current = []
|
||||
unsafe_flag = [False]
|
||||
no_split_flag = [False]
|
||||
components.append((current, unsafe_flag, no_split_flag))
|
||||
group = _ComponentGroup()
|
||||
components.append(group)
|
||||
current = group.lines
|
||||
continue
|
||||
if isinstance(exp, IIFEUnsafeStatement):
|
||||
unsafe_flag[0] = True
|
||||
if _emits_bare_local(exp):
|
||||
no_split_flag[0] = True
|
||||
if group is not None:
|
||||
if isinstance(exp, IIFEUnsafeStatement):
|
||||
group.unsafe = True
|
||||
if _emits_bare_local(exp):
|
||||
group.no_split = True
|
||||
current.append(str(statement(exp)).rstrip())
|
||||
|
||||
if not components:
|
||||
return "\n".join(prefix) + "\n\n"
|
||||
|
||||
pieces: list[str] = list(prefix)
|
||||
for body, group_unsafe, group_no_split in components:
|
||||
if group_unsafe[0]:
|
||||
pieces.extend(body)
|
||||
for g in components:
|
||||
if g.unsafe:
|
||||
pieces.extend(g.lines)
|
||||
else:
|
||||
# A group containing raw scope braces or bare-local
|
||||
# declarations must stay in one IIFE so the declarations
|
||||
# remain visible to subsequent uses. ``max_statements=None``
|
||||
# disables sub-splitting for the group.
|
||||
cap = None if group_no_split[0] else IIFE_MAX_STATEMENTS
|
||||
pieces.extend(_wrap_in_iifes(body, max_statements=cap))
|
||||
cap = None if g.no_split else IIFE_MAX_STATEMENTS
|
||||
pieces.extend(_wrap_in_iifes(g.lines, max_statements=cap))
|
||||
return "\n".join(pieces) + "\n\n"
|
||||
|
||||
@property
|
||||
|
||||
Reference in New Issue
Block a user