10
10
11
11
import copy
12
12
from collections .abc import Iterable
13
- from typing import Any , overload
13
+ from typing import Any , Optional , overload
14
14
15
+ from hypothesis import strategies as st
15
16
from hypothesis .errors import InvalidArgument
16
17
from hypothesis .internal .conjecture import utils as cu
17
18
from hypothesis .internal .conjecture .engine import BUFFER_SIZE
24
25
T4 ,
25
26
T5 ,
26
27
Ex ,
27
- MappedStrategy ,
28
28
SearchStrategy ,
29
29
T ,
30
30
check_strategy ,
@@ -306,50 +306,32 @@ def do_draw(self, data):
306
306
return result
307
307
308
308
309
- class FixedKeysDictStrategy ( MappedStrategy ):
309
+ class FixedDictStrategy ( SearchStrategy ):
310
310
"""A strategy which produces dicts with a fixed set of keys, given a
311
311
strategy for each of their equivalent values.
312
312
313
313
e.g. {'foo' : some_int_strategy} would generate dicts with the single
314
314
key 'foo' mapping to some integer.
315
315
"""
316
316
317
- def __init__ (self , strategy_dict ):
318
- dict_type = type (strategy_dict )
319
- self .keys = tuple (strategy_dict .keys ())
320
- super ().__init__ (
321
- strategy = TupleStrategy (strategy_dict [k ] for k in self .keys ),
322
- pack = lambda value : dict_type (zip (self .keys , value )),
317
+ def __init__ (
318
+ self ,
319
+ mapping : dict [T , SearchStrategy [Ex ]],
320
+ * ,
321
+ optional : Optional [dict [T , SearchStrategy [Ex ]]],
322
+ ):
323
+ dict_type = type (mapping )
324
+ keys = tuple (mapping .keys ())
325
+ self .fixed = st .tuples (* [mapping [k ] for k in keys ]).map (
326
+ lambda value : dict_type (zip (keys , value ))
323
327
)
324
-
325
- def calc_is_empty (self , recur ):
326
- return recur (self .mapped_strategy )
327
-
328
- def __repr__ (self ):
329
- return f"FixedKeysDictStrategy({ self .keys !r} , { self .mapped_strategy !r} )"
330
-
331
-
332
- class FixedAndOptionalKeysDictStrategy (SearchStrategy ):
333
- """A strategy which produces dicts with a fixed set of keys, given a
334
- strategy for each of their equivalent values.
335
-
336
- e.g. {'foo' : some_int_strategy} would generate dicts with the single
337
- key 'foo' mapping to some integer.
338
- """
339
-
340
- def __init__ (self , strategy_dict , optional ):
341
- self .required = strategy_dict
342
- self .fixed = FixedKeysDictStrategy (strategy_dict )
343
328
self .optional = optional
344
329
345
- def calc_is_empty (self , recur ):
346
- return recur (self .fixed )
347
-
348
- def __repr__ (self ):
349
- return f"FixedAndOptionalKeysDictStrategy({ self .required !r} , { self .optional !r} )"
350
-
351
330
def do_draw (self , data ):
352
- result = data .draw (self .fixed )
331
+ value = data .draw (self .fixed )
332
+ if self .optional is None :
333
+ return value
334
+
353
335
remaining = [k for k , v in self .optional .items () if not v .is_empty ]
354
336
should_draw = cu .many (
355
337
data , min_size = 0 , max_size = len (remaining ), average_size = len (remaining ) / 2
@@ -358,5 +340,13 @@ def do_draw(self, data):
358
340
j = data .draw_integer (0 , len (remaining ) - 1 )
359
341
remaining [- 1 ], remaining [j ] = remaining [j ], remaining [- 1 ]
360
342
key = remaining .pop ()
361
- result [key ] = data .draw (self .optional [key ])
362
- return result
343
+ value [key ] = data .draw (self .optional [key ])
344
+ return value
345
+
346
+ def calc_is_empty (self , recur ):
347
+ return recur (self .fixed )
348
+
349
+ def __repr__ (self ):
350
+ if self .optional is not None :
351
+ return f"fixed_dictionaries({ self .keys !r} , optional={ self .optional !r} )"
352
+ return f"fixed_dictionaries({ self .keys !r} )"
0 commit comments