Skip to content

Commit 790eb75

Browse files
authored
Merge pull request #17 from xoeye/bugfix/VS-1557-unstructure-typing-any
VS-1557: Allow unstructuring of attrs classes in typing.Any-typed fields
2 parents c648e65 + af165ba commit 790eb75

File tree

5 files changed

+114
-1
lines changed

5 files changed

+114
-1
lines changed

CHANGES.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## v2.0.2
2+
3+
- Fixes regression in post cattrs-1.1.2 where attrs objects in `typing.Any` fields are
4+
returned as-is and not unstructured
5+
16
## v2.0.1
27

38
- Fixes error in structuring parameterized generic wildcats

tests/test_try_struc.py

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from typecats import Cat, try_struc
2+
3+
4+
def test_with_no_default():
5+
@Cat
6+
class Thing(dict):
7+
name: str
8+
9+
assert try_struc(Thing, None) is None
10+
assert Thing.try_struc(None) is None
11+
12+
13+
def test_with_default():
14+
@Cat
15+
class Thing(dict):
16+
name: str = ""
17+
18+
assert try_struc(Thing, None) is None
19+
assert Thing.try_struc(None) is None

tests/test_unstruc_any.py

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import typing as ty
2+
3+
from typecats import Cat, register_unstruc_hook_func
4+
5+
6+
@Cat
7+
class Container:
8+
one: ty.Any
9+
many: ty.List[ty.Any]
10+
11+
12+
@Cat
13+
class SecondContainer:
14+
thing: ty.Any
15+
16+
17+
@Cat
18+
class SomeCat:
19+
field: str
20+
21+
22+
class HasUnstrucHook:
23+
def __init__(self, unstruc_field):
24+
self.unstruc_field = unstruc_field
25+
26+
27+
class WithoutUnstrucHook:
28+
def __init__(self, some_field):
29+
self.some_field = some_field
30+
31+
32+
def test_unstruc_cat_types_in_any_fields():
33+
container = Container(
34+
one=SecondContainer(
35+
thing=SomeCat(field="a"),
36+
),
37+
many=[
38+
SecondContainer(
39+
thing=SomeCat(field="b"),
40+
),
41+
SomeCat(field="c"),
42+
],
43+
)
44+
45+
expected = {
46+
"one": {"thing": {"field": "a"}},
47+
"many": [{"thing": {"field": "b"}}, {"field": "c"}],
48+
}
49+
50+
assert container.unstruc() == expected
51+
52+
53+
def test_unstruc_of_non_cat_in_any_field_with_unstruc_hook():
54+
register_unstruc_hook_func(
55+
lambda t: t == HasUnstrucHook, lambda x: {"unstruc_field": x.unstruc_field}
56+
)
57+
58+
# This has no unstruc hook registered and should therefore be returned as-is
59+
no_unstruc = WithoutUnstrucHook("some_str_0")
60+
61+
container = Container(
62+
one=HasUnstrucHook("some_str_1"),
63+
many=[no_unstruc, HasUnstrucHook("some_str_2")],
64+
)
65+
66+
expected = {
67+
"one": {"unstruc_field": "some_str_1"},
68+
"many": [no_unstruc, {"unstruc_field": "some_str_2"}],
69+
}
70+
71+
assert container.unstruc() == expected

typecats/__about__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
"""typecats"""
2-
__version__ = "2.0.1"
2+
__version__ = "2.0.2"
33
__author__ = "Peter Gaultney"
44
__author_email__ = "pgaultney@xoi.io"

typecats/patch.py

+18
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,20 @@ def unstructure_strip_defaults(obj):
5858
return unstructure_strip_defaults
5959

6060

61+
def _is_any(cl: ty.Type) -> bool:
62+
return cl == ty.Any
63+
64+
65+
def unstructure_any_from_runtime_type(
66+
gen_converter: GenConverter, obj: ty.Any
67+
) -> ty.Union[ty.Any, dict]:
68+
cls = type(obj)
69+
if is_attrs_class(cls):
70+
return gen_converter.unstructure(obj, cls)
71+
else:
72+
return gen_converter.unstructure(obj)
73+
74+
6175
def patch_converter_for_typecats(converter: GenConverter) -> GenConverter:
6276
if converter in __patched_converters:
6377
return converter
@@ -70,5 +84,9 @@ def patch_converter_for_typecats(converter: GenConverter) -> GenConverter:
7084
has_with_generic, partial(unstructure_strip_defaults_factory, converter)
7185
)
7286

87+
converter.register_unstructure_hook_func(
88+
_is_any, partial(unstructure_any_from_runtime_type, converter)
89+
)
90+
7391
__patched_converters.append(converter)
7492
return converter

0 commit comments

Comments
 (0)