Skip to content

Commit 00bb181

Browse files
committed
feat: implement iterate_annotation_chain to allow subclassing in type structs
1 parent 2268862 commit 00bb181

4 files changed

Lines changed: 23 additions & 10 deletions

File tree

libdestruct/common/struct/struct_impl.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from libdestruct.common.obj import obj
1414
from libdestruct.common.struct import struct
1515
from libdestruct.common.type_registry import TypeRegistry
16+
from libdestruct.common.utils import iterate_annotation_chain
1617

1718
if TYPE_CHECKING: # pragma: no cover
1819
from libdestruct.backing.resolver import Resolver
@@ -52,7 +53,7 @@ def _inflate_struct_attributes(
5253
) -> None:
5354
current_offset = 0
5455

55-
for name, annotation in reference_type.__annotations__.items():
56+
for name, annotation in iterate_annotation_chain(reference_type, terminate_at=struct):
5657
if name in reference_type.__dict__:
5758
# Field associated with the annotation
5859
attrs = getattr(reference_type, name)
@@ -97,7 +98,7 @@ def compute_own_size(cls: type[struct_impl], reference_type: type) -> None:
9798
"""Compute the size of the struct."""
9899
size = 0
99100

100-
for name, annotation in reference_type.__annotations__.items():
101+
for name, annotation in iterate_annotation_chain(reference_type, terminate_at=struct):
101102
if name in reference_type.__dict__:
102103
# Field associated with the annotation
103104
attrs = getattr(reference_type, name)

libdestruct/common/struct/struct_inflater.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def inflate_struct_type(reference_type: type[struct]) -> type[struct_impl]:
1818
if issubclass(reference_type, struct_impl):
1919
return reference_type
2020

21-
type_impl = type(reference_type.__name__, (struct_impl,), {"_members": {}})
21+
type_impl = type(reference_type.__name__, (struct_impl, reference_type), {"_members": {}})
2222
type_impl._reference_struct = reference_type
2323

2424
reference_type._type_impl = type_impl

libdestruct/common/type_registry.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,14 @@ def inflater_for(
7575
def _inflater_for_type(self: TypeRegistry, item: type[obj]) -> type[obj]:
7676
parent = item.__base__
7777

78-
for handler in self.type_handlers.get(parent, []):
79-
result = handler(item)
80-
81-
if result is not None:
82-
self.mapping[item] = result
83-
return result
78+
while parent:
79+
for handler in self.type_handlers.get(parent, []):
80+
result = handler(item)
81+
82+
if result is not None:
83+
self.mapping[item] = result
84+
return result
85+
parent = parent.__base__
8486

8587
raise ValueError(f"No applicable inflater found for {item}")
8688

libdestruct/common/utils.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77
from __future__ import annotations
88

99
from types import MethodType
10-
from typing import TYPE_CHECKING
10+
from typing import TYPE_CHECKING, Any
1111

1212
from libdestruct.common.field import Field
1313

1414
if TYPE_CHECKING: # pragma: no cover
15+
from collections.abc import Generator
16+
1517
from libdestruct.backing.resolver import Resolver
1618
from libdestruct.common.obj import obj
1719

@@ -32,3 +34,11 @@ def size_of(item_or_inflater: obj | callable[[Resolver], obj]) -> int:
3234
return field_object.get_size()
3335

3436
raise ValueError(f"Cannot determine the size of {item_or_inflater}")
37+
38+
39+
def iterate_annotation_chain(item: obj, terminate_at: object | None = None) -> Generator[tuple[str, Any]]:
40+
"""Iterate over the annotation chain of the provided item."""
41+
current_item = item
42+
while current_item is not terminate_at:
43+
yield from current_item.__annotations__.items()
44+
current_item = current_item.__base__ if hasattr(current_item, "__base__") else None

0 commit comments

Comments
 (0)