88from hypothesis .strategies import just , one_of
99
1010from cattrs import BaseConverter , UnstructureStrategy
11+ from cattrs .errors import StructureHandlerNotFoundError
1112
1213from ._compat import is_py310_plus
1314from .typed import nested_typed_classes , simple_typed_attrs , simple_typed_classes
@@ -28,7 +29,7 @@ def test_simple_roundtrip(cls_and_vals, strat):
2829
2930
3031@given (
31- simple_typed_attrs (defaults = True , newtypes = False , allow_nan = False ),
32+ simple_typed_attrs (defaults = "always" , newtypes = False , allow_nan = False ),
3233 unstructure_strats ,
3334)
3435def test_simple_roundtrip_defaults (attr_and_strat , strat ):
@@ -58,7 +59,7 @@ def test_nested_roundtrip(cls_and_vals):
5859 assert inst == converter .structure (converter .unstructure (inst ), cl )
5960
6061
61- @given (nested_typed_classes (kw_only = False , newtypes = False , allow_nan = False ))
62+ @given (nested_typed_classes (kw_only = "never" , newtypes = False , allow_nan = False ))
6263def test_nested_roundtrip_tuple (cls_and_vals ):
6364 """
6465 Nested classes with metadata can be unstructured and restructured.
@@ -71,24 +72,24 @@ def test_nested_roundtrip_tuple(cls_and_vals):
7172 assert inst == converter .structure (converter .unstructure (inst ), cl )
7273
7374
74- @settings (suppress_health_check = [HealthCheck .filter_too_much , HealthCheck . too_slow ])
75+ @settings (suppress_health_check = [HealthCheck .too_slow ])
7576@given (
76- simple_typed_classes (defaults = False , newtypes = False , allow_nan = False ),
77- simple_typed_classes (defaults = False , newtypes = False , allow_nan = False ),
78- unstructure_strats ,
77+ simple_typed_classes (
78+ defaults = "never" , newtypes = False , allow_nan = False , min_attrs = 2
79+ ),
80+ simple_typed_classes (
81+ defaults = "never" , newtypes = False , allow_nan = False , min_attrs = 1
82+ ),
7983)
80- def test_union_field_roundtrip (cl_and_vals_a , cl_and_vals_b , strat ):
84+ def test_union_field_roundtrip_dict (cl_and_vals_a , cl_and_vals_b ):
8185 """
8286 Classes with union fields can be unstructured and structured.
8387 """
84- converter = BaseConverter (unstruct_strat = strat )
88+ converter = BaseConverter ()
8589 cl_a , vals_a , kwargs_a = cl_and_vals_a
86- assume (strat is UnstructureStrategy .AS_DICT or not kwargs_a )
87- cl_b , vals_b , _ = cl_and_vals_b
90+ cl_b , _ , _ = cl_and_vals_b
8891 a_field_names = {a .name for a in fields (cl_a )}
8992 b_field_names = {a .name for a in fields (cl_b )}
90- assume (a_field_names )
91- assume (b_field_names )
9293
9394 common_names = a_field_names & b_field_names
9495 assume (len (a_field_names ) > len (common_names ))
@@ -99,25 +100,55 @@ class C:
99100
100101 inst = C (a = cl_a (* vals_a , ** kwargs_a ))
101102
102- if strat is UnstructureStrategy .AS_DICT :
103- assert inst == converter .structure (converter .unstructure (inst ), C )
104- else :
105- # Our disambiguation functions only support dictionaries for now.
106- with pytest .raises (ValueError ):
107- converter .structure (converter .unstructure (inst ), C )
103+ unstructured = converter .unstructure (inst )
104+ assert inst == converter .structure (converter .unstructure (unstructured ), C )
108105
109- def handler (obj , _ ):
110- return converter .structure (obj , cl_a )
111106
112- converter .register_structure_hook (Union [cl_a , cl_b ], handler )
113- assert inst == converter .structure (converter .unstructure (inst ), C )
107+ @settings (suppress_health_check = [HealthCheck .too_slow ])
108+ @given (
109+ simple_typed_classes (
110+ defaults = "never" , newtypes = False , allow_nan = False , kw_only = "never" , min_attrs = 2
111+ ),
112+ simple_typed_classes (
113+ defaults = "never" , newtypes = False , allow_nan = False , kw_only = "never" , min_attrs = 1
114+ ),
115+ )
116+ def test_union_field_roundtrip_tuple (cl_and_vals_a , cl_and_vals_b ):
117+ """
118+ Classes with union fields can be unstructured and structured.
119+ """
120+ converter = BaseConverter (unstruct_strat = UnstructureStrategy .AS_TUPLE )
121+ cl_a , vals_a , _ = cl_and_vals_a
122+ cl_b , _ , _ = cl_and_vals_b
123+
124+ @define
125+ class C :
126+ a : Union [cl_a , cl_b ]
127+
128+ inst = C (a = cl_a (* vals_a ))
129+
130+ # Our disambiguation functions only support dictionaries for now.
131+ raw = converter .unstructure (inst )
132+ with pytest .raises (StructureHandlerNotFoundError ):
133+ converter .structure (raw , C )
134+
135+ def handler (obj , _ ):
136+ return converter .structure (obj , cl_a )
137+
138+ converter .register_structure_hook (Union [cl_a , cl_b ], handler )
139+ unstructured = converter .unstructure (inst )
140+ assert inst == converter .structure (unstructured , C )
114141
115142
116143@pytest .mark .skipif (not is_py310_plus , reason = "3.10+ union syntax" )
117- @settings (suppress_health_check = [HealthCheck .filter_too_much , HealthCheck . too_slow ])
144+ @settings (suppress_health_check = [HealthCheck .too_slow ])
118145@given (
119- simple_typed_classes (defaults = False , newtypes = False , allow_nan = False ),
120- simple_typed_classes (defaults = False , newtypes = False , allow_nan = False ),
146+ simple_typed_classes (
147+ defaults = "never" , newtypes = False , allow_nan = False , min_attrs = 1
148+ ),
149+ simple_typed_classes (
150+ defaults = "never" , newtypes = False , allow_nan = False , min_attrs = 1
151+ ),
121152 unstructure_strats ,
122153)
123154def test_310_union_field_roundtrip (cl_and_vals_a , cl_and_vals_b , strat ):
@@ -126,12 +157,10 @@ def test_310_union_field_roundtrip(cl_and_vals_a, cl_and_vals_b, strat):
126157 """
127158 converter = BaseConverter (unstruct_strat = strat )
128159 cl_a , vals_a , kwargs_a = cl_and_vals_a
129- cl_b , vals_b , _ = cl_and_vals_b
160+ cl_b , _ , _ = cl_and_vals_b
130161 assume (strat is UnstructureStrategy .AS_DICT or not kwargs_a )
131162 a_field_names = {a .name for a in fields (cl_a )}
132163 b_field_names = {a .name for a in fields (cl_b )}
133- assume (a_field_names )
134- assume (b_field_names )
135164
136165 common_names = a_field_names & b_field_names
137166 assume (len (a_field_names ) > len (common_names ))
@@ -146,7 +175,7 @@ class C:
146175 assert inst == converter .structure (converter .unstructure (inst ), C )
147176 else :
148177 # Our disambiguation functions only support dictionaries for now.
149- with pytest .raises (ValueError ):
178+ with pytest .raises (StructureHandlerNotFoundError ):
150179 converter .structure (converter .unstructure (inst ), C )
151180
152181 def handler (obj , _ ):
@@ -156,7 +185,7 @@ def handler(obj, _):
156185 assert inst == converter .structure (converter .unstructure (inst ), C )
157186
158187
159- @given (simple_typed_classes (defaults = False , newtypes = False , allow_nan = False ))
188+ @given (simple_typed_classes (defaults = "never" , newtypes = False , allow_nan = False ))
160189def test_optional_field_roundtrip (cl_and_vals ):
161190 """
162191 Classes with optional fields can be unstructured and structured.
@@ -178,7 +207,7 @@ class C:
178207
179208
180209@pytest .mark .skipif (not is_py310_plus , reason = "3.10+ union syntax" )
181- @given (simple_typed_classes (defaults = False , newtypes = False , allow_nan = False ))
210+ @given (simple_typed_classes (defaults = "never" , newtypes = False , allow_nan = False ))
182211def test_310_optional_field_roundtrip (cl_and_vals ):
183212 """
184213 Classes with optional fields can be unstructured and structured.
0 commit comments