Skip to content

Commit c3393ce

Browse files
committed
Added overrides argument in include_subclasses
Allows to propagate overrides easily to child classes.
1 parent 1447935 commit c3393ce

File tree

2 files changed

+25
-13
lines changed

2 files changed

+25
-13
lines changed

src/cattrs/strategies/_subclasses.py

+24-7
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from typing import Dict, Optional, Tuple, Type, Union, List, Callable
44

55
from ..converters import Converter
6+
from ..gen import AttributeOverride, make_dict_structure_fn, make_dict_unstructure_fn
67

78

89
def _make_subclasses_tree(cl: Type) -> List[Type]:
@@ -18,7 +19,10 @@ def _has_subclasses(cl: Type, given_subclasses: Tuple[Type]):
1819

1920

2021
def include_subclasses(
21-
cl: Type, converter: Converter, subclasses: Optional[Tuple[Type]] = None
22+
cl: Type,
23+
converter: Converter,
24+
subclasses: Optional[Tuple[Type]] = None,
25+
overrides: Optional[Dict[str, AttributeOverride]] = None,
2226
) -> None:
2327
"""
2428
Modify the given converter so that the attrs/dataclass `cl` is un/structured as if
@@ -27,6 +31,9 @@ def include_subclasses(
2731
2832
Subclasses are detected using the `__subclasses__` method, or they can be explicitly
2933
provided.
34+
35+
overrides is a mapping of some or all the parent class field names to attribute
36+
overrides instantiated with :func:`cattrs.gen.override`
3037
"""
3138
# Due to https://github.com/python-attrs/attrs/issues/1047
3239
collect()
@@ -36,28 +43,35 @@ def include_subclasses(
3643
else:
3744
parent_subclass_tree = tuple(_make_subclasses_tree(cl))
3845

46+
if overrides is None:
47+
overrides = {}
48+
3949
for cl in parent_subclass_tree:
4050
if not _has_subclasses(cl, parent_subclass_tree):
4151
continue
4252

4353
# Unstructuring ...
4454
can_handle_unstruct, unstructure_a = gen_unstructure_handling_pair(
45-
converter, cl
55+
converter, cl, overrides
4656
)
4757
# This needs to use function dispatch, using singledispatch will again
4858
# match A and all subclasses, which is not what we want.
4959
converter.register_unstructure_hook_func(can_handle_unstruct, unstructure_a)
5060

5161
# Structuring...
5262
can_handle_struct, structure_a = gen_structure_handling_pair(
53-
converter, cl, parent_subclass_tree
63+
converter, cl, parent_subclass_tree, overrides
5464
)
5565
converter.register_structure_hook_func(can_handle_struct, structure_a)
5666

5767

58-
def gen_unstructure_handling_pair(converter: Converter, cl: Type):
68+
def gen_unstructure_handling_pair(
69+
converter: Converter,
70+
cl: Type,
71+
overrides: Optional[Dict[str, AttributeOverride]] = None,
72+
):
5973
# This hook is for instances of A, but not instances of subclasses.
60-
base_hook = converter.gen_unstructure_attrs_fromdict(cl)
74+
base_hook = make_dict_unstructure_fn(cl, converter, **overrides)
6175

6276
def unstructure_a(val: cl, c=converter) -> Dict:
6377
"""
@@ -74,13 +88,16 @@ def unstructure_a(val: cl, c=converter) -> Dict:
7488

7589

7690
def gen_structure_handling_pair(
77-
converter: Converter, cl: Type, given_subclasses_tree: Tuple[Type]
91+
converter: Converter,
92+
cl: Type,
93+
given_subclasses_tree: Tuple[Type],
94+
overrides: Optional[Dict[str, AttributeOverride]] = None,
7895
) -> Tuple[Callable]:
7996
actual_subclass_tree = tuple(_make_subclasses_tree(cl))
8097
class_tree = tuple(set(actual_subclass_tree) & set(given_subclasses_tree))
8198
subclass_union = Union[class_tree]
8299
dis_fn = converter._get_dis_func(subclass_union)
83-
base_struct_hook = converter.gen_structure_attrs_fromdict(cl)
100+
base_struct_hook = make_dict_structure_fn(cl, converter, **overrides)
84101

85102
def structure_a(val: dict, _, c=converter, cl=cl) -> cl:
86103
dis_cl = dis_fn(val)

tests/strategies/test_include_subclasses.py

+1-6
Original file line numberDiff line numberDiff line change
@@ -227,12 +227,7 @@ def test_structuring_with_subclasses_argument():
227227

228228
def test_overrides():
229229
c = Converter()
230-
overrides = {"p": override(rename="u")}
231-
c.register_unstructure_hook(
232-
Parent, make_dict_unstructure_fn(Parent, c, **overrides)
233-
)
234-
c.register_structure_hook(Parent, make_dict_structure_fn(Parent, c, **overrides))
235-
include_subclasses(Parent, c)
230+
include_subclasses(Parent, c, overrides={"p": override(rename="u")})
236231

237232
assert c.unstructure(Parent(1)) == {"u": 1}
238233
assert c.structure({"u": 1}, Parent) == Parent(1)

0 commit comments

Comments
 (0)