2
2
3
3
import keyword
4
4
import string
5
- from collections import OrderedDict
6
5
from enum import Enum
7
6
from typing import (
8
7
Any ,
23
22
from attr ._make import _CountingAttr
24
23
from attrs import NOTHING , AttrsInstance , Factory , make_class
25
24
from hypothesis import strategies as st
26
- from hypothesis .strategies import SearchStrategy
25
+ from hypothesis .strategies import SearchStrategy , booleans
26
+ from typing_extensions import TypeAlias
27
+
28
+ from . import FeatureFlag
27
29
28
30
PosArg = Any
29
31
PosArgs = tuple [PosArg ]
30
32
KwArgs = dict [str , Any ]
33
+ AttrsAndArgs : TypeAlias = tuple [type [AttrsInstance ], PosArgs , KwArgs ]
31
34
32
35
primitive_strategies = st .sampled_from (
33
36
[
@@ -167,7 +170,7 @@ def gen_attr_names() -> Iterable[str]:
167
170
def _create_hyp_class (
168
171
attrs_and_strategy : list [tuple [_CountingAttr , st .SearchStrategy [PosArgs ]]],
169
172
frozen = None ,
170
- ) -> SearchStrategy [tuple ]:
173
+ ) -> SearchStrategy [AttrsAndArgs ]:
171
174
"""
172
175
A helper function for Hypothesis to generate attrs classes.
173
176
@@ -192,7 +195,7 @@ def key(t):
192
195
return st .tuples (
193
196
st .builds (
194
197
lambda f : make_class (
195
- "HypClass" , OrderedDict (zip (gen_attr_names (), attrs )), frozen = f
198
+ "HypClass" , dict (zip (gen_attr_names (), attrs )), frozen = f
196
199
),
197
200
st .booleans () if frozen is None else st .just (frozen ),
198
201
),
@@ -209,26 +212,28 @@ def just_class(tup):
209
212
return _create_hyp_class (combined_attrs )
210
213
211
214
212
- def just_class_with_type (tup ) :
215
+ def just_class_with_type (tup : tuple ) -> SearchStrategy [ AttrsAndArgs ] :
213
216
nested_cl = tup [1 ][0 ]
214
- default = attr .Factory (nested_cl )
215
- combined_attrs = list (tup [0 ])
216
- combined_attrs .append (
217
- (attr .ib (default = default , type = nested_cl ), st .just (nested_cl ()))
218
- )
219
- return _create_hyp_class (combined_attrs )
220
217
218
+ def make_with_default (takes_self : bool ) -> SearchStrategy [AttrsAndArgs ]:
219
+ combined_attrs = list (tup [0 ])
220
+ combined_attrs .append (
221
+ (
222
+ attr .ib (
223
+ default = (
224
+ Factory (
225
+ nested_cl if not takes_self else lambda _ : nested_cl (),
226
+ takes_self = takes_self ,
227
+ )
228
+ ),
229
+ type = nested_cl ,
230
+ ),
231
+ st .just (nested_cl ()),
232
+ )
233
+ )
234
+ return _create_hyp_class (combined_attrs )
221
235
222
- def just_class_with_type_takes_self (
223
- tup : tuple [list [tuple [_CountingAttr , SearchStrategy ]], tuple [type [AttrsInstance ]]]
224
- ) -> SearchStrategy [tuple [type [AttrsInstance ]]]:
225
- nested_cl = tup [1 ][0 ]
226
- default = Factory (lambda _ : nested_cl (), takes_self = True )
227
- combined_attrs = list (tup [0 ])
228
- combined_attrs .append (
229
- (attr .ib (default = default , type = nested_cl ), st .just (nested_cl ()))
230
- )
231
- return _create_hyp_class (combined_attrs )
236
+ return booleans ().flatmap (make_with_default )
232
237
233
238
234
239
def just_frozen_class_with_type (tup ):
@@ -240,22 +245,45 @@ def just_frozen_class_with_type(tup):
240
245
return _create_hyp_class (combined_attrs )
241
246
242
247
243
- def list_of_class (tup ) :
248
+ def list_of_class (tup : tuple ) -> SearchStrategy [ AttrsAndArgs ] :
244
249
nested_cl = tup [1 ][0 ]
245
- default = attr .Factory (lambda : [nested_cl ()])
246
- combined_attrs = list (tup [0 ])
247
- combined_attrs .append ((attr .ib (default = default ), st .just ([nested_cl ()])))
248
- return _create_hyp_class (combined_attrs )
249
250
251
+ def make_with_default (takes_self : bool ) -> SearchStrategy [AttrsAndArgs ]:
252
+ combined_attrs = list (tup [0 ])
253
+ combined_attrs .append (
254
+ (
255
+ attr .ib (
256
+ default = (
257
+ Factory (lambda : [nested_cl ()])
258
+ if not takes_self
259
+ else Factory (lambda _ : [nested_cl ()], takes_self = True )
260
+ ),
261
+ type = list [nested_cl ],
262
+ ),
263
+ st .just ([nested_cl ()]),
264
+ )
265
+ )
266
+ return _create_hyp_class (combined_attrs )
267
+
268
+ return booleans ().flatmap (make_with_default )
250
269
251
- def list_of_class_with_type (tup ):
270
+
271
+ def list_of_class_with_type (tup : tuple ) -> SearchStrategy [AttrsAndArgs ]:
252
272
nested_cl = tup [1 ][0 ]
253
- default = attr .Factory (lambda : [nested_cl ()])
254
- combined_attrs = list (tup [0 ])
255
- combined_attrs .append (
256
- (attr .ib (default = default , type = List [nested_cl ]), st .just ([nested_cl ()]))
257
- )
258
- return _create_hyp_class (combined_attrs )
273
+
274
+ def make_with_default (takes_self : bool ) -> SearchStrategy [AttrsAndArgs ]:
275
+ default = (
276
+ Factory (lambda : [nested_cl ()])
277
+ if not takes_self
278
+ else Factory (lambda _ : [nested_cl ()], takes_self = True )
279
+ )
280
+ combined_attrs = list (tup [0 ])
281
+ combined_attrs .append (
282
+ (attr .ib (default = default , type = List [nested_cl ]), st .just ([nested_cl ()]))
283
+ )
284
+ return _create_hyp_class (combined_attrs )
285
+
286
+ return booleans ().flatmap (make_with_default )
259
287
260
288
261
289
def dict_of_class (tup ):
@@ -266,7 +294,9 @@ def dict_of_class(tup):
266
294
return _create_hyp_class (combined_attrs )
267
295
268
296
269
- def _create_hyp_nested_strategy (simple_class_strategy ):
297
+ def _create_hyp_nested_strategy (
298
+ simple_class_strategy : SearchStrategy ,
299
+ ) -> SearchStrategy :
270
300
"""
271
301
Create a recursive attrs class.
272
302
Given a strategy for building (simpler) classes, create and return
@@ -275,6 +305,7 @@ def _create_hyp_nested_strategy(simple_class_strategy):
275
305
* a list of simpler classes
276
306
* a dict mapping the string "cls" to a simpler class.
277
307
"""
308
+
278
309
# A strategy producing tuples of the form ([list of attributes], <given
279
310
# class strategy>).
280
311
attrs_and_classes = st .tuples (lists_of_attrs (defaults = True ), simple_class_strategy )
@@ -286,7 +317,6 @@ def _create_hyp_nested_strategy(simple_class_strategy):
286
317
| attrs_and_classes .flatmap (list_of_class_with_type )
287
318
| attrs_and_classes .flatmap (dict_of_class )
288
319
| attrs_and_classes .flatmap (just_frozen_class_with_type )
289
- | attrs_and_classes .flatmap (just_class_with_type_takes_self )
290
320
)
291
321
292
322
@@ -430,9 +460,10 @@ def simple_classes(defaults=None, min_attrs=0, frozen=None, kw_only=None):
430
460
)
431
461
432
462
433
- # Ok, so st.recursive works by taking a base strategy (in this case,
434
- # simple_classes) and a special function. This function receives a strategy,
435
- # and returns another strategy (building on top of the base strategy).
436
- nested_classes = st .recursive (
437
- simple_classes (defaults = True ), _create_hyp_nested_strategy
438
- )
463
+ def nested_classes (
464
+ takes_self : FeatureFlag = "sometimes" ,
465
+ ) -> SearchStrategy [AttrsAndArgs ]:
466
+ # Ok, so st.recursive works by taking a base strategy (in this case,
467
+ # simple_classes) and a special function. This function receives a strategy,
468
+ # and returns another strategy (building on top of the base strategy).
469
+ return st .recursive (simple_classes (defaults = True ), _create_hyp_nested_strategy )
0 commit comments