forked from HypothesisWorks/hypothesis
/
strategies.py
2181 lines (1835 loc) · 83.1 KB
/
strategies.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# coding=utf-8
#
# This file is part of Hypothesis, which may be found at
# https://github.com/HypothesisWorks/hypothesis-python
#
# Most of this work is copyright (C) 2013-2018 David R. MacIver
# (david@drmaciver.com), but it contains contributions by others. See
# CONTRIBUTING.rst for a full list of people who may hold copyright, and
# consult the git log if you need to determine who owns an individual
# contribution.
#
# This Source Code Form is subject to the terms of the Mozilla Public License,
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
# obtain one at http://mozilla.org/MPL/2.0/.
#
# END HEADER
from __future__ import division, print_function, absolute_import
import sys
import enum
import math
import random
import string
import datetime as dt
import operator
from uuid import UUID
from decimal import Context, Decimal, localcontext
from inspect import isclass, isfunction
from fractions import Fraction
from functools import reduce
import attr
from hypothesis.errors import InvalidArgument, ResolutionFailed
from hypothesis.control import note, assume, reject, cleanup, \
current_build_context
from hypothesis._settings import note_deprecation
from hypothesis.internal.cache import LRUReusedCache
from hypothesis.searchstrategy import SearchStrategy, check_strategy
from hypothesis.internal.compat import abc, gcd, ceil, floor, hrange, \
string_types, get_type_hints, getfullargspec, typing_root_type, \
implements_iterator
from hypothesis.internal.floats import next_up, float_of, next_down, \
is_negative, float_to_int, int_to_float, count_between_floats
from hypothesis.internal.charmap import as_general_categories
from hypothesis.internal.cathetus import cathetus
from hypothesis.internal.renaming import renamed_arguments
from hypothesis.utils.conventions import infer, not_set
from hypothesis.internal.reflection import proxies, required_args, \
is_typed_named_tuple, define_function_signature
from hypothesis.internal.validation import check_type, try_convert, \
check_valid_size, check_valid_bound, check_valid_sizes, \
check_valid_integer, check_valid_interval, check_valid_magnitude
from hypothesis.searchstrategy.lazy import LazyStrategy
from hypothesis.searchstrategy.misc import BoolStrategy, JustStrategy, \
RandomStrategy, SampledFromStrategy
from hypothesis.searchstrategy.shared import SharedStrategy
from hypothesis.searchstrategy.numbers import FloatStrategy, \
BoundedIntStrategy, IntegersFromStrategy, WideRangeIntStrategy, \
FixedBoundedFloatStrategy
from hypothesis.searchstrategy.streams import StreamStrategy
from hypothesis.searchstrategy.strings import FixedSizeBytes, \
StringStrategy, BinaryStringStrategy, OneCharStringStrategy
from hypothesis.searchstrategy.datetime import DateStrategy, \
DatetimeStrategy, TimedeltaStrategy
from hypothesis.searchstrategy.deferred import DeferredStrategy
from hypothesis.searchstrategy.recursive import RecursiveStrategy
from hypothesis.internal.conjecture.utils import choice, check_sample, \
integer_range, calc_label_from_cls
from hypothesis.searchstrategy.strategies import OneOfStrategy
from hypothesis.searchstrategy.collections import ListStrategy, \
TupleStrategy, UniqueListStrategy, FixedKeysDictStrategy
typing = None # type: Union[None, ModuleType]
try:
import typing as typing_module
typing = typing_module
except ImportError:
pass
try:
import numpy
except ImportError:
numpy = None
if False:
from types import ModuleType # noqa
from random import Random # noqa
from typing import Any, Dict, Union, Sequence, Callable, Pattern # noqa
from typing import TypeVar, Tuple, List, Set, FrozenSet, overload # noqa
from typing import Type, Text, AnyStr, Optional # noqa
from hypothesis.utils.conventions import InferType # noqa
from hypothesis.searchstrategy.strategies import T, Ex # noqa
K, V = TypeVar['K'], TypeVar['V']
# See https://github.com/python/mypy/issues/3186 - numbers.Real is wrong!
Real = Union[int, float, Fraction, Decimal]
else:
def overload(f):
return f
__all__ = [
'nothing',
'just', 'one_of',
'none',
'choices', 'streaming',
'booleans', 'integers', 'floats', 'complex_numbers', 'fractions',
'decimals',
'characters', 'text', 'from_regex', 'binary', 'uuids',
'tuples', 'lists', 'sets', 'frozensets', 'iterables',
'dictionaries', 'fixed_dictionaries',
'sampled_from', 'permutations',
'datetimes', 'dates', 'times', 'timedeltas',
'builds',
'randoms', 'random_module',
'recursive', 'composite',
'shared', 'runner', 'data',
'deferred',
'from_type', 'register_type_strategy', 'emails',
]
_strategies = set()
class FloatKey(object):
def __init__(self, f):
self.value = float_to_int(f)
def __eq__(self, other):
return isinstance(other, FloatKey) and (
other.value == self.value
)
def __ne__(self, other):
return not self.__eq__(other)
def __hash__(self):
return hash(self.value)
def convert_value(v):
if isinstance(v, float):
return FloatKey(v)
return (type(v), v)
STRATEGY_CACHE = LRUReusedCache(1024)
def cacheable(fn):
# type: (T) -> T
@proxies(fn)
def cached_strategy(*args, **kwargs):
kwargs_cache_key = set()
try:
for k, v in kwargs.items():
kwargs_cache_key.add((k, convert_value(v)))
except TypeError:
return fn(*args, **kwargs)
cache_key = (
fn,
tuple(map(convert_value, args)), frozenset(kwargs_cache_key))
try:
return STRATEGY_CACHE[cache_key]
except TypeError:
return fn(*args, **kwargs)
except KeyError:
result = fn(*args, **kwargs)
if not isinstance(result, SearchStrategy) or result.is_cacheable:
STRATEGY_CACHE[cache_key] = result
return result
cached_strategy.__clear_cache = STRATEGY_CACHE.clear
return cached_strategy
def base_defines_strategy(force_reusable):
# type: (bool) -> Callable[[T], T]
"""Returns a decorator for strategy functions.
If force_reusable is True, the generated values are assumed to be
reusable, i.e. immutable and safe to cache, across multiple test
invocations.
"""
def decorator(strategy_definition):
"""A decorator that registers the function as a strategy and makes it
lazily evaluated."""
_strategies.add(strategy_definition.__name__)
@proxies(strategy_definition)
def accept(*args, **kwargs):
result = LazyStrategy(strategy_definition, args, kwargs)
if force_reusable:
result.force_has_reusable_values = True
assert result.has_reusable_values
return result
return accept
return decorator
defines_strategy = base_defines_strategy(False)
defines_strategy_with_reusable_values = base_defines_strategy(True)
class Nothing(SearchStrategy):
def calc_is_empty(self, recur):
return True
def do_draw(self, data):
# This method should never be called because draw() will mark the
# data as invalid immediately because is_empty is True.
assert False # pragma: no cover
def calc_has_reusable_values(self, recur):
return True
def __repr__(self):
return 'nothing()'
def map(self, f):
return self
def filter(self, f):
return self
def flatmap(self, f):
return self
NOTHING = Nothing()
@cacheable
def nothing():
# type: () -> SearchStrategy
"""This strategy never successfully draws a value and will always reject on
an attempt to draw.
Examples from this strategy do not shrink (because there are none).
"""
return NOTHING
def just(value):
# type: (T) -> SearchStrategy[T]
"""Return a strategy which only generates ``value``.
Note: ``value`` is not copied. Be wary of using mutable values.
If ``value`` is the result of a callable, you can use
:func:`builds(callable) <hypothesis.strategies.builds>` instead
of ``just(callable())`` to get a fresh value each time.
Examples from this strategy do not shrink (because there is only one).
"""
return JustStrategy(value)
@defines_strategy_with_reusable_values
def none():
# type: () -> SearchStrategy[None]
"""Return a strategy which only generates None.
Examples from this strategy do not shrink (because there is only
one).
"""
return just(None)
@overload
def one_of(args):
# type: (Sequence[SearchStrategy[Any]]) -> SearchStrategy[Any]
pass # pragma: no cover
@overload
def one_of(*args):
# type: (SearchStrategy[Any]) -> SearchStrategy[Any]
pass # pragma: no cover
def one_of(*args):
# Mypy workaround alert: Any is too loose above; the return paramater
# should be the union of the input parameters. Unfortunately, Mypy <=0.600
# raises errors due to incompatible inputs instead. See #1270 for links.
# v0.610 doesn't error; it gets inference wrong for 2+ arguments instead.
"""Return a strategy which generates values from any of the argument
strategies.
This may be called with one iterable argument instead of multiple
strategy arguments. In which case ``one_of(x)`` and ``one_of(*x)`` are
equivalent.
Examples from this strategy will generally shrink to ones that come from
strategies earlier in the list, then shrink according to behaviour of the
strategy that produced them. In order to get good shrinking behaviour,
try to put simpler strategies first. e.g. ``one_of(none(), text())`` is
better than ``one_of(text(), none())``.
This is especially important when using recursive strategies. e.g.
``x = st.deferred(lambda: st.none() | st.tuples(x, x))`` will shrink well,
but ``x = st.deferred(lambda: st.tuples(x, x) | st.none())`` will shrink
very badly indeed.
"""
if len(args) == 1 and not isinstance(args[0], SearchStrategy):
try:
args = tuple(args[0])
except TypeError:
pass
return OneOfStrategy(args)
@cacheable
@defines_strategy_with_reusable_values
def integers(min_value=None, max_value=None):
# type: (Real, Real) -> SearchStrategy[int]
"""Returns a strategy which generates integers (in Python 2 these may be
ints or longs).
If min_value is not None then all values will be >= min_value. If
max_value is not None then all values will be <= max_value
Examples from this strategy will shrink towards zero, and negative values
will also shrink towards positive (i.e. -n may be replaced by +n).
"""
check_valid_bound(min_value, 'min_value')
check_valid_bound(max_value, 'max_value')
check_valid_interval(min_value, max_value, 'min_value', 'max_value')
min_int_value = None if min_value is None else ceil(min_value)
max_int_value = None if max_value is None else floor(max_value)
if min_int_value is not None and max_int_value is not None and \
min_int_value > max_int_value:
raise InvalidArgument('No integers between min_value=%r and '
'max_value=%r' % (min_value, max_value))
if min_int_value is None:
if max_int_value is None:
return (
WideRangeIntStrategy()
)
else:
return IntegersFromStrategy(0).map(lambda x: max_int_value - x)
else:
if max_int_value is None:
return IntegersFromStrategy(min_int_value)
else:
assert min_int_value <= max_int_value
if min_int_value == max_int_value:
return just(min_int_value)
elif min_int_value >= 0:
return BoundedIntStrategy(min_int_value, max_int_value)
elif max_int_value <= 0:
return BoundedIntStrategy(
-max_int_value, -min_int_value
).map(lambda t: -t)
else:
return integers(min_value=0, max_value=max_int_value) | \
integers(min_value=min_int_value, max_value=0)
@cacheable
@defines_strategy
def booleans():
# type: () -> SearchStrategy[bool]
"""Returns a strategy which generates instances of bool.
Examples from this strategy will shrink towards False (i.e.
shrinking will try to replace True with False where possible).
"""
return BoolStrategy()
@cacheable
@defines_strategy_with_reusable_values
def floats(
min_value=None, # type: Real
max_value=None, # type: Real
allow_nan=None, # type: bool
allow_infinity=None, # type: bool
width=64, # type: int
):
# type: (...) -> SearchStrategy[float]
"""Returns a strategy which generates floats.
- If min_value is not None, all values will be >= min_value.
- If max_value is not None, all values will be <= max_value.
- If min_value or max_value is not None, it is an error to enable
allow_nan.
- If both min_value and max_value are not None, it is an error to enable
allow_infinity.
Where not explicitly ruled out by the bounds, all of infinity, -infinity
and NaN are possible values generated by this strategy.
The width argument specifies the maximum number of bits of precision
required to represent the generated float. Valid values are 16, 32, or 64.
Passing ``width=32`` will still use the builtin 64-bit ``float`` class,
but always for values which can be exactly represented as a 32-bit float.
Half-precision floats (``width=16``) are only supported on Python 3.6, or
if Numpy is installed.
Examples from this strategy have a complicated and hard to explain
shrinking behaviour, but it tries to improve "human readability". Finite
numbers will be preferred to infinity and infinity will be preferred to
NaN.
"""
if allow_nan is None:
allow_nan = bool(min_value is None and max_value is None)
elif allow_nan:
if min_value is not None or max_value is not None:
raise InvalidArgument(
'Cannot have allow_nan=%r, with min_value or max_value' % (
allow_nan
))
if width not in (16, 32, 64):
raise InvalidArgument(
'Got width=%r, but the only valid values are the integers 16, '
'32, and 64.' % (width,)
)
if width == 16 and sys.version_info[:2] < (3, 6) and numpy is None:
raise InvalidArgument( # pragma: no cover
'width=16 requires either Numpy, or Python >= 3.6'
)
check_valid_bound(min_value, 'min_value')
check_valid_bound(max_value, 'max_value')
min_arg, max_arg = min_value, max_value
if min_value is not None:
min_value = float_of(min_value, width)
assert isinstance(min_value, float)
if max_value is not None:
max_value = float_of(max_value, width)
assert isinstance(max_value, float)
check_valid_interval(min_value, max_value, 'min_value', 'max_value')
if min_value == float(u'-inf'):
min_value = None
if max_value == float(u'inf'):
max_value = None
if min_value is not None and min_arg is not None and min_value < min_arg:
min_value = next_up(min_value, width)
assert min_value > min_arg # type: ignore
if max_value is not None and max_arg is not None and max_value > max_arg:
max_value = next_down(max_value, width)
assert max_value < max_arg # type: ignore
if min_value is not None and max_value is not None and \
min_value > max_value:
raise InvalidArgument(
'There are no %s-bit floating-point values between min_value=%r '
'and max_value=%r' % (width, min_arg, max_arg))
if allow_infinity is None:
allow_infinity = bool(min_value is None or max_value is None)
elif allow_infinity:
if min_value is not None and max_value is not None:
raise InvalidArgument(
'Cannot have allow_infinity=%r, with both min_value and '
'max_value' % (
allow_infinity
))
if min_value is None and max_value is None:
result = FloatStrategy(
allow_infinity=allow_infinity, allow_nan=allow_nan,
) # type: SearchStrategy[float]
elif min_value is not None and max_value is not None:
if min_value == max_value:
assert isinstance(min_value, float)
result = just(min_value)
elif is_negative(min_value):
if is_negative(max_value):
result = floats(min_value=-max_value, max_value=-min_value)\
.map(operator.neg)
else:
result = floats(min_value=0.0, max_value=max_value) | floats(
min_value=0.0, max_value=-min_value).map(operator.neg)
elif count_between_floats(min_value, max_value) > 1000:
result = FixedBoundedFloatStrategy(
lower_bound=min_value, upper_bound=max_value
)
else:
ub_int = float_to_int(max_value, width)
lb_int = float_to_int(min_value, width)
assert lb_int <= ub_int
result = integers(min_value=lb_int, max_value=ub_int).map(
lambda x: int_to_float(x, width)
)
elif min_value is not None:
assert isinstance(min_value, float)
if min_value < 0:
result = floats(
min_value=0.0, allow_infinity=allow_infinity
) | floats(min_value=min_value, max_value=-0.0)
else:
result = (
floats(allow_infinity=allow_infinity, allow_nan=False).map(
lambda x: assume(not math.isnan(x)) and
min_value + abs(x) # type: ignore
)
)
if min_value == 0 and not is_negative(min_value):
result = result.filter(lambda x: math.copysign(1.0, x) == 1)
else:
assert isinstance(max_value, float)
if max_value > 0:
result = floats(
min_value=0.0,
max_value=max_value,
) | floats(max_value=-0.0, allow_infinity=allow_infinity)
else:
result = (
floats(allow_infinity=allow_infinity, allow_nan=False).map(
lambda x: assume(not math.isnan(x)) and
max_value - abs(x) # type: ignore
)
)
if max_value == 0 and is_negative(max_value):
result = result.filter(is_negative)
if width < 64:
def downcast(x):
try:
return float_of(x, width)
except OverflowError:
reject()
return result.map(downcast)
return result
@cacheable
@defines_strategy
def tuples(*args):
# type: (*SearchStrategy) -> SearchStrategy[tuple]
"""Return a strategy which generates a tuple of the same length as args by
generating the value at index i from args[i].
e.g. tuples(integers(), integers()) would generate a tuple of length
two with both values an integer.
Examples from this strategy shrink by shrinking their component parts.
"""
for arg in args:
check_strategy(arg)
return TupleStrategy(args)
@overload
def sampled_from(elements):
# type: (Sequence[T]) -> SearchStrategy[T]
pass # pragma: no cover
@overload
def sampled_from(elements):
# type: (Type[enum.Enum]) -> SearchStrategy[Any]
# `SearchStrategy[Enum]` is unreliable due to mataclass issues.
pass # pragma: no cover
@defines_strategy
def sampled_from(elements):
"""Returns a strategy which generates any value present in ``elements``.
Note that as with :func:`~hypothesis.strategies.just`, values will not be
copied and thus you should be careful of using mutable data.
``sampled_from`` supports ordered collections, as well as
:class:`~python:enum.Enum` objects. :class:`~python:enum.Flag` objects
may also generate any combination of their members.
Examples from this strategy shrink by replacing them with values earlier in
the list. So e.g. sampled_from((10, 1)) will shrink by trying to replace
1 values with 10, and sampled_from((1, 10)) will shrink by trying to
replace 10 values with 1.
"""
values = check_sample(elements, 'sampled_from')
if not values:
return nothing()
if len(values) == 1:
return just(values[0])
if hasattr(enum, 'Flag') and isclass(elements) and \
issubclass(elements, enum.Flag):
# Combinations of enum.Flag members are also members. We generate
# these dynamically, because static allocation takes O(2^n) memory.
return sets(sampled_from(values), min_size=1).map(
lambda s: reduce(operator.or_, s))
return SampledFromStrategy(values)
@cacheable
@defines_strategy
def lists(
elements=None, # type: SearchStrategy[Ex]
min_size=0, # type: int
average_size=None, # type: None
max_size=None, # type: int
unique_by=None, # type: Callable[..., Any]
unique=False, # type: bool
):
# type: (...) -> SearchStrategy[List[Ex]]
"""Returns a list containing values drawn from elements with length in the
interval [min_size, max_size] (no bounds in that direction if these are
None). If max_size is 0 then elements may be None and only the empty list
will be drawn.
The average_size argument is deprecated. Internal upgrades since
Hypothesis 1.x mean we no longer needed this hint to generate useful data.
If unique is True (or something that evaluates to True), we compare direct
object equality, as if unique_by was ``lambda x: x``. This comparison only
works for hashable types.
if unique_by is not None it must be a function returning a hashable type
when given a value drawn from elements. The resulting list will satisfy the
condition that for ``i`` != ``j``, ``unique_by(result[i])`` !=
``unique_by(result[j])``.
Examples from this strategy shrink by trying to remove elements from the
list, and by shrinking each individual element of the list.
"""
check_valid_sizes(min_size, average_size, max_size)
if elements is None:
note_deprecation(
'Passing a strategy for `elements` of the list will be required '
'in a future version of Hypothesis. To create lists that are '
'always empty, use `builds(list)` or `lists(nothing())`.'
)
if min_size or average_size or max_size:
# Checked internally for lists with an elements strategy, but
# we're about to skip that and return builds(list) instead...
raise InvalidArgument(
'Cannot create a non-empty collection (min_size=%r, '
'average_size=%r, max_size=%r) without elements.'
% (min_size, average_size, max_size)
)
return builds(list)
if max_size == 0:
return builds(list)
check_strategy(elements, 'elements')
if unique:
if unique_by is not None:
raise InvalidArgument((
'cannot specify both unique and unique_by (you probably only '
'want to set unique_by)'
))
else:
def unique_by(x):
return x
if unique_by is not None:
return UniqueListStrategy(
elements=elements,
max_size=max_size,
min_size=min_size,
key=unique_by
)
return ListStrategy(elements, min_size=min_size, max_size=max_size)
@cacheable
@defines_strategy
def sets(
elements=None, # type: SearchStrategy[Ex]
min_size=0, # type: int
average_size=None, # type: None
max_size=None, # type: int
):
# type: (...) -> SearchStrategy[Set[Ex]]
"""This has the same behaviour as lists, but returns sets instead.
Note that Hypothesis cannot tell if values are drawn from elements
are hashable until running the test, so you can define a strategy
for sets of an unhashable type but it will fail at test time.
Examples from this strategy shrink by trying to remove elements from the
set, and by shrinking each individual element of the set.
"""
if elements is None:
note_deprecation(
'Passing a strategy for `elements` of the set will be required '
'in a future version of Hypothesis. To create sets that are '
'always empty, use `builds(set)` or `sets(nothing())`.'
)
return lists(
elements=elements, min_size=min_size, average_size=average_size,
max_size=max_size, unique=True
).map(set)
@cacheable
@defines_strategy
def frozensets(
elements=None, # type: SearchStrategy[Ex]
min_size=0, # type: int
average_size=None, # type: None
max_size=None, # type: int
):
# type: (...) -> SearchStrategy[FrozenSet[Ex]]
"""This is identical to the sets function but instead returns
frozensets."""
if elements is None:
note_deprecation(
'Passing a strategy for `elements` of the frozenset will be '
'required in a future version of Hypothesis. To create '
'frozensets that are always empty, use `builds(frozenset)` '
'or `frozensets(nothing())`.'
)
return lists(
elements=elements, min_size=min_size, average_size=average_size,
max_size=max_size, unique=True
).map(frozenset)
@implements_iterator
class PrettyIter(object):
def __init__(self, values):
self._values = values
self._iter = iter(self._values)
def __iter__(self):
return self._iter
def __next__(self):
return next(self._iter)
def __repr__(self):
return 'iter({!r})'.format(self._values)
@defines_strategy
def iterables(elements=None, min_size=0, average_size=None, max_size=None,
unique_by=None, unique=False):
"""This has the same behaviour as lists, but returns iterables instead.
Some iterables cannot be indexed (e.g. sets) and some do not have a
fixed length (e.g. generators). This strategy produces iterators,
which cannot be indexed and do not have a fixed length. This ensures
that you do not accidentally depend on sequence behaviour.
"""
if elements is None:
note_deprecation(
'Passing a strategy for `elements` of the iterable will be '
'required in a future version of Hypothesis. To create '
'iterables that are always empty, use `iterables(nothing())`.'
)
return lists(
elements=elements, min_size=min_size, average_size=average_size,
max_size=max_size, unique_by=unique_by, unique=unique
).map(PrettyIter)
@defines_strategy
def fixed_dictionaries(
mapping # type: Dict[T, SearchStrategy[Ex]]
):
# type: (...) -> SearchStrategy[Dict[T, Ex]]
"""Generates a dictionary of the same type as mapping with a fixed set of
keys mapping to strategies. mapping must be a dict subclass.
Generated values have all keys present in mapping, with the
corresponding values drawn from mapping[key]. If mapping is an
instance of OrderedDict the keys will also be in the same order,
otherwise the order is arbitrary.
Examples from this strategy shrink by shrinking each individual value in
the generated dictionary.
"""
check_type(dict, mapping, 'mapping')
for v in mapping.values():
check_strategy(v)
return FixedKeysDictStrategy(mapping)
@cacheable
@defines_strategy
def dictionaries(
keys, # type: SearchStrategy[Ex]
values, # type: SearchStrategy[T]
dict_class=dict, # type: type
min_size=0, # type: int
average_size=None, # type: None
max_size=None, # type: int
):
# type: (...) -> SearchStrategy[Dict[Ex, T]]
# Describing the exact dict_class to Mypy drops the key and value types,
# so we report Dict[K, V] instead of Mapping[Any, Any] for now. Sorry!
"""Generates dictionaries of type dict_class with keys drawn from the keys
argument and values drawn from the values argument.
The size parameters have the same interpretation as for lists.
Examples from this strategy shrink by trying to remove keys from the
generated dictionary, and by shrinking each generated key and value.
"""
check_valid_sizes(min_size, average_size, max_size)
if max_size == 0:
return fixed_dictionaries(dict_class())
check_strategy(keys)
check_strategy(values)
return lists(
tuples(keys, values),
min_size=min_size, max_size=max_size,
unique_by=lambda x: x[0]
).map(dict_class)
@defines_strategy
def streaming(elements):
"""Generates an infinite stream of values where each value is drawn from
elements.
The result is iterable (the iterator will never terminate) and
indexable.
Examples from this strategy shrink by trying to shrink each value drawn.
.. deprecated:: 3.15.0
Use :func:`data() <hypothesis.strategies.data>` instead.
"""
note_deprecation(
'streaming() has been deprecated. Use the data() strategy instead and '
'replace stream iteration with data.draw() calls.'
)
check_strategy(elements)
return StreamStrategy(elements)
@cacheable
@defines_strategy_with_reusable_values
def characters(
whitelist_categories=None, # type: Sequence[Text]
blacklist_categories=None, # type: Sequence[Text]
blacklist_characters=None, # type: Sequence[Text]
min_codepoint=None, # type: int
max_codepoint=None, # type: int
whitelist_characters=None, # type: Sequence[Text]
):
# type: (...) -> SearchStrategy[Text]
"""Generates unicode text type (unicode on python 2, str on python 3)
characters following specified filtering rules.
- When no filtering rules are specifed, any character can be produced.
- If ``min_codepoint`` or ``max_codepoint`` is specifed, then only
characters having a codepoint in that range will be produced.
- If ``whitelist_categories`` is specified, then only characters from those
Unicode categories will be produced. This is a further restriction,
characters must also satisfy ``min_codepoint`` and ``max_codepoint``.
- If ``blacklist_categories`` is specified, then any character from those
categories will not be produced. Any overlap between
``whitelist_categories`` and ``blacklist_categories`` will raise an
exception, as each character can only belong to a single class.
- If ``whitelist_characters`` is specified, then any additional characters
in that list will also be produced.
- If ``blacklist_characters`` is specified, then any characters in
that list will be not be produced. Any overlap between \
``whitelist_characters`` and ``blacklist_characters`` will raise an
exception.
The ``_codepoint`` arguments must be integers between zero and
:obj:`python:sys.maxunicode`. The ``_characters`` arguments must be
collections of length-one unicode strings, such as a unicode string.
The ``_categories`` arguments must be used to specify either the
one-letter Unicode major category or the two-letter Unicode
`general category`_. For example, ``('Nd', 'Lu')`` signifies "Number,
decimal digit" and "Letter, uppercase". A single letter ('major category')
can be given to match all corresponding categories, for example ``'P'``
for characters in any punctuation category.
.. _general category: https://wikipedia.org/wiki/Unicode_character_property
Examples from this strategy shrink towards the codepoint for ``'0'``,
or the first allowable codepoint after it if ``'0'`` is excluded.
"""
check_valid_size(min_codepoint, 'min_codepoint')
check_valid_size(max_codepoint, 'max_codepoint')
check_valid_interval(min_codepoint, max_codepoint,
'min_codepoint', 'max_codepoint')
if all((whitelist_characters is not None,
min_codepoint is None,
max_codepoint is None,
whitelist_categories is None,
blacklist_categories is None,
)):
raise InvalidArgument(
'Passing only whitelist_characters=%r would have no effect. '
'Perhaps you want sampled_from() ?' % (whitelist_characters,))
blacklist_characters = blacklist_characters or ''
whitelist_characters = whitelist_characters or ''
overlap = set(blacklist_characters).intersection(whitelist_characters)
if overlap:
raise InvalidArgument(
'Characters %r are present in both whitelist_characters=%r, and '
'blacklist_characters=%r' % (
sorted(overlap), whitelist_characters, blacklist_characters))
blacklist_categories = as_general_categories(
blacklist_categories, 'blacklist_categories')
if whitelist_categories is not None and not whitelist_categories and \
not whitelist_characters:
raise InvalidArgument(
'When whitelist_categories is an empty collection and there are '
'no characters specified in whitelist_characters, nothing can '
'be generated by the characters() strategy.')
whitelist_categories = as_general_categories(
whitelist_categories, 'whitelist_categories')
both_cats = set(
blacklist_categories or ()).intersection(whitelist_categories or ())
if both_cats:
raise InvalidArgument(
'Categories %r are present in both whitelist_categories=%r, and '
'blacklist_categories=%r' % (
sorted(both_cats), whitelist_categories, blacklist_categories))
return OneCharStringStrategy(whitelist_categories=whitelist_categories,
blacklist_categories=blacklist_categories,
blacklist_characters=blacklist_characters,
min_codepoint=min_codepoint,
max_codepoint=max_codepoint,
whitelist_characters=whitelist_characters)
@cacheable
@defines_strategy_with_reusable_values
def text(
alphabet=None, # type: Union[Sequence[Text], SearchStrategy[Text]]
min_size=0, # type: int
average_size=None, # type: None
max_size=None # type: int
):
# type: (...) -> SearchStrategy[Text]
"""Generates values of a unicode text type (unicode on python 2, str on
python 3) with values drawn from alphabet, which should be an iterable of
length one strings or a strategy generating such. If it is None it will
default to generating the full unicode range (excluding surrogate
characters). If it is an empty collection this will only generate empty
strings.
min_size and max_size have the usual interpretations.
The average_size argument is deprecated. Internal upgrades since
Hypothesis 1.x mean we no longer needed this hint to generate useful data.
Examples from this strategy shrink towards shorter strings, and with the
characters in the text shrinking as per the alphabet strategy.
"""
check_valid_sizes(min_size, average_size, max_size)
if alphabet is None:
char_strategy = characters(blacklist_categories=('Cs',))
elif not alphabet:
if (min_size or 0) > 0:
raise InvalidArgument(
'Invalid min_size %r > 0 for empty alphabet' % (
min_size,
)
)
return just(u'')
elif isinstance(alphabet, SearchStrategy):
char_strategy = alphabet
else:
if not isinstance(alphabet, abc.Sequence):
note_deprecation(
'alphabet must be an ordered sequence, or tests may be '
'flaky and shrinking weaker, but a %r is not a type of '
'sequence. This will be an error in future.'
% (type(alphabet),)
)
alphabet = list(alphabet)
non_string = [c for c in alphabet if not isinstance(c, string_types)]
if non_string:
note_deprecation(
'The following elements in alphabet are not unicode '
'strings, which will be an error in future: %r'
% (non_string,)
)
alphabet = [str(c) for c in alphabet]
not_one_char = [c for c in alphabet
if isinstance(c, string_types) and len(c) != 1]
if not_one_char:
note_deprecation(
'The following elements in alphabet are not of length '
'one, which leads to violation of size constraints and '
'will be an error in future: %r' % (not_one_char,)
)
char_strategy = sampled_from(alphabet)
return StringStrategy(lists(
char_strategy, min_size=min_size, max_size=max_size
))