Harden track_presence generator option and fix test fixtures

Set the presence flag after the submessage decode, dump it in verbose
protobuf dumps, and raise at generation time when track_presence is set
on a field where it has no effect. Retire the absent-field expectation
in the integration test so the UTC phase sees the first matching state,
and align the Sydney TZ strings with the 03:00 fall-back the struct
helper uses.
This commit is contained in:
J. Nick Koston
2026-08-21 15:24:12 -05:00
parent 061f90822c
commit 5354499724
5 changed files with 28 additions and 7 deletions
+19 -2
View File
@@ -498,6 +498,15 @@ def create_field_type_info(
needs_encode: bool = True,
) -> TypeInfo:
"""Create the appropriate TypeInfo instance for a field, handling repeated fields and custom options."""
if get_field_opt(field, pb.track_presence, False) and (
field.label == FieldDescriptorProto.LABEL_REPEATED
or field.type != 11
or not needs_decode
):
raise ValueError(
f"track_presence on field '{field.name}' has no effect; it requires "
"a non-repeated message field in a message that is decoded"
)
if field.label == FieldDescriptorProto.LABEL_REPEATED:
# Check if this is a packed_buffer field (zero-copy packed repeated)
if get_field_opt(field, pb.packed_buffer, False):
@@ -541,6 +550,8 @@ def create_field_type_info(
return PointerToStringBufferType(field, None)
validate_field_type(field.type, field.name)
if field.type == 11:
return MessageType(field, needs_decode, needs_encode)
return TYPE_INFO[field.type](field)
@@ -955,10 +966,13 @@ class MessageType(TypeInfo):
def decode_length_content(self) -> str:
# Custom decode that doesn't use templates
if self._track_presence:
# decode_to_message() cannot report failure, so setting the flag
# afterwards only documents intent; a status-returning decode could
# gate it for real without touching callers.
return (
f"case {self.number}:\n"
f" this->has_{self.name} = true;\n"
f" value.decode_to_message(this->{self.field_name});\n"
f" this->has_{self.name} = true;\n"
f" break;"
)
return f"case {self.number}: value.decode_to_message(this->{self.field_name}); break;"
@@ -968,7 +982,10 @@ class MessageType(TypeInfo):
@property
def dump_content(self) -> str:
o = f'out.append(2, \' \').append_p(ESPHOME_PSTR("{self.name}")).append(": ");\n'
o = ""
if self._track_presence:
o += f'dump_field(out, ESPHOME_PSTR("has_{self.name}"), this->has_{self.name});\n'
o += f'out.append(2, \' \').append_p(ESPHOME_PSTR("{self.name}")).append(": ");\n'
o += f"this->{self.field_name}.dump_to(out);\n"
o += 'out.append("\\n");'
return o