@@ -455,8 +455,11 @@ def __attrs_post_init__(self):
455
455
self .arguments_strategies = {}
456
456
bundles = []
457
457
for k , v in sorted (self .arguments .items ()):
458
+ assert not isinstance (v , BundleReferenceStrategy )
458
459
if isinstance (v , Bundle ):
459
460
bundles .append (v )
461
+ consume = isinstance (v , BundleConsumer )
462
+ v = BundleReferenceStrategy (v .name , consume = consume )
460
463
self .arguments_strategies [k ] = v
461
464
self .bundles = tuple (bundles )
462
465
@@ -469,6 +472,26 @@ def __repr__(self) -> str:
469
472
self_strategy = st .runner ()
470
473
471
474
475
+ class BundleReferenceStrategy (SearchStrategy ):
476
+ def __init__ (self , name : str , * , consume : bool = False ):
477
+ self .name = name
478
+ self .consume = consume
479
+
480
+ def do_draw (self , data ):
481
+ machine = data .draw (self_strategy )
482
+ bundle = machine .bundle (self .name )
483
+ if not bundle :
484
+ data .mark_invalid (f"Cannot draw from empty bundle { self .name !r} " )
485
+ # Shrink towards the right rather than the left. This makes it easier
486
+ # to delete data generated earlier, as when the error is towards the
487
+ # end there can be a lot of hard to remove padding.
488
+ position = data .draw_integer (0 , len (bundle ) - 1 , shrink_towards = len (bundle ))
489
+ if self .consume :
490
+ return bundle .pop (position ) # pragma: no cover # coverage is flaky here
491
+ else :
492
+ return bundle [position ]
493
+
494
+
472
495
class Bundle (SearchStrategy [Ex ]):
473
496
"""A collection of values for use in stateful testing.
474
497
@@ -495,32 +518,16 @@ def __init__(
495
518
self , name : str , * , consume : bool = False , draw_references : bool = True
496
519
) -> None :
497
520
self .name = name
498
- self .consume = consume
521
+ self .__reference_strategy = BundleReferenceStrategy ( name , consume = consume )
499
522
self .draw_references = draw_references
500
523
501
524
def do_draw (self , data ):
502
525
machine = data .draw (self_strategy )
503
-
504
- bundle = machine .bundle (self .name )
505
- if not bundle :
506
- data .mark_invalid (f"Cannot draw from empty bundle { self .name !r} " )
507
- # Shrink towards the right rather than the left. This makes it easier
508
- # to delete data generated earlier, as when the error is towards the
509
- # end there can be a lot of hard to remove padding.
510
- position = data .draw_integer (0 , len (bundle ) - 1 , shrink_towards = len (bundle ))
511
- if self .consume :
512
- reference = bundle .pop (
513
- position
514
- ) # pragma: no cover # coverage is flaky here
515
- else :
516
- reference = bundle [position ]
517
-
518
- if self .draw_references :
519
- return reference
526
+ reference = data .draw (self .__reference_strategy )
520
527
return machine .names_to_values [reference .name ]
521
528
522
529
def __repr__ (self ):
523
- consume = self .consume
530
+ consume = self .__reference_strategy . consume
524
531
if consume is False :
525
532
return f"Bundle(name={ self .name !r} )"
526
533
return f"Bundle(name={ self .name !r} , { consume = } )"
@@ -539,11 +546,18 @@ def available(self, data):
539
546
def flatmap (self , expand ):
540
547
if self .draw_references :
541
548
return type (self )(
542
- self .name , consume = self .consume , draw_references = False
549
+ self .name ,
550
+ consume = self .__reference_strategy .consume ,
551
+ draw_references = False ,
543
552
).flatmap (expand )
544
553
return super ().flatmap (expand )
545
554
546
555
556
+ class BundleConsumer (Bundle [Ex ]):
557
+ def __init__ (self , bundle : Bundle [Ex ]) -> None :
558
+ super ().__init__ (bundle .name , consume = True )
559
+
560
+
547
561
def consumes (bundle : Bundle [Ex ]) -> SearchStrategy [Ex ]:
548
562
"""When introducing a rule in a RuleBasedStateMachine, this function can
549
563
be used to mark bundles from which each value used in a step with the
@@ -559,10 +573,7 @@ def consumes(bundle: Bundle[Ex]) -> SearchStrategy[Ex]:
559
573
"""
560
574
if not isinstance (bundle , Bundle ):
561
575
raise TypeError ("Argument to be consumed must be a bundle." )
562
- return type (bundle )(
563
- name = bundle .name ,
564
- consume = True ,
565
- )
576
+ return BundleConsumer (bundle )
566
577
567
578
568
579
@attr .s ()
@@ -609,7 +620,7 @@ def _convert_targets(targets, target):
609
620
)
610
621
raise InvalidArgument (msg % (t , type (t )))
611
622
while isinstance (t , Bundle ):
612
- if t . consume :
623
+ if isinstance ( t , BundleConsumer ) :
613
624
note_deprecation (
614
625
f"Using consumes({ t .name } ) doesn't makes sense in this context. "
615
626
"This will be an error in a future version of Hypothesis." ,
0 commit comments