summaryrefslogtreecommitdiffstats
path: root/contrib/python/hypothesis
diff options
context:
space:
mode:
authorrobot-piglet <[email protected]>2023-12-10 12:55:20 +0300
committerrobot-piglet <[email protected]>2023-12-10 13:10:14 +0300
commitdfbaba029321bce139af71ae1de12bea432dcbaa (patch)
treeb15602f49f63fca95e34e95b46552549e8471114 /contrib/python/hypothesis
parent3dfda0adc5f6695090a1bbd4ee775a8697f830cb (diff)
Intermediate changes
Diffstat (limited to 'contrib/python/hypothesis')
-rw-r--r--contrib/python/hypothesis/py3/.dist-info/METADATA2
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/core.py8
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/extra/_array_helpers.py11
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/extra/array_api.py2
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/extra/lark.py2
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/extra/numpy.py4
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py608
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/internal/conjecture/floats.py23
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py11
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/internal/conjecture/utils.py213
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/internal/floats.py14
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/internal/intervalsets.py26
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/stateful.py18
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/strategies/_internal/collections.py4
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py10
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/strategies/_internal/datetime.py9
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/strategies/_internal/featureflags.py6
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/strategies/_internal/numbers.py177
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/strategies/_internal/random.py19
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/strategies/_internal/regex.py4
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strategies.py12
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strings.py49
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/strategies/_internal/types.py2
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/vendor/tlds-alpha-by-domain.txt4
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/version.py2
-rw-r--r--contrib/python/hypothesis/py3/ya.make2
26 files changed, 750 insertions, 492 deletions
diff --git a/contrib/python/hypothesis/py3/.dist-info/METADATA b/contrib/python/hypothesis/py3/.dist-info/METADATA
index d1b6f76c1d3..b6eca63e8fa 100644
--- a/contrib/python/hypothesis/py3/.dist-info/METADATA
+++ b/contrib/python/hypothesis/py3/.dist-info/METADATA
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: hypothesis
-Version: 6.89.0
+Version: 6.90.0
Summary: A library for property-based testing
Home-page: https://hypothesis.works
Author: David R. MacIver and Zac Hatfield-Dodds
diff --git a/contrib/python/hypothesis/py3/hypothesis/core.py b/contrib/python/hypothesis/py3/hypothesis/core.py
index 74e363447c9..1c4cf9e1441 100644
--- a/contrib/python/hypothesis/py3/hypothesis/core.py
+++ b/contrib/python/hypothesis/py3/hypothesis/core.py
@@ -610,10 +610,10 @@ def get_random_for_wrapped_test(test, wrapped_test):
@attr.s
class Stuff:
- selfy = attr.ib(default=None)
- args = attr.ib(factory=tuple)
- kwargs = attr.ib(factory=dict)
- given_kwargs = attr.ib(factory=dict)
+ selfy: Any = attr.ib(default=None)
+ args: tuple = attr.ib(factory=tuple)
+ kwargs: dict = attr.ib(factory=dict)
+ given_kwargs: dict = attr.ib(factory=dict)
def process_arguments_to_given(wrapped_test, arguments, kwargs, given_kwargs, params):
diff --git a/contrib/python/hypothesis/py3/hypothesis/extra/_array_helpers.py b/contrib/python/hypothesis/py3/hypothesis/extra/_array_helpers.py
index a525d3c473f..c66853fd1c3 100644
--- a/contrib/python/hypothesis/py3/hypothesis/extra/_array_helpers.py
+++ b/contrib/python/hypothesis/py3/hypothesis/extra/_array_helpers.py
@@ -13,7 +13,7 @@ from typing import NamedTuple, Optional, Tuple, Union
from hypothesis import assume, strategies as st
from hypothesis.errors import InvalidArgument
-from hypothesis.internal.conjecture import utils as cu
+from hypothesis.internal.conjecture.utils import _calc_p_continue
from hypothesis.internal.coverage import check_function
from hypothesis.internal.validation import check_type, check_valid_interval
from hypothesis.strategies._internal.utils import defines_strategy
@@ -544,7 +544,7 @@ class MutuallyBroadcastableShapesStrategy(st.SearchStrategy):
if name not in dims:
dim = name.strip("?")
dims[dim] = data.draw(self.side_strat)
- if self.min_dims == 0 and not data.draw_bits(3):
+ if self.min_dims == 0 and not data.draw_boolean(7 / 8):
dims[dim + "?"] = None
else:
dims[dim + "?"] = dims[dim]
@@ -562,6 +562,9 @@ class MutuallyBroadcastableShapesStrategy(st.SearchStrategy):
assert len(use) == self.num_shapes
assert all(isinstance(x, bool) for x in use)
+ _gap = self.max_dims - self.min_dims
+ p_keep_extending_shape = _calc_p_continue(desired_avg=_gap / 2, max_size=_gap)
+
for dim_count in range(1, self.max_dims + 1):
dim = dim_count - 1
@@ -596,9 +599,7 @@ class MutuallyBroadcastableShapesStrategy(st.SearchStrategy):
# shape-tuple even if it is no longer being added to.
# This helps to ensure more stable shrinking behavior.
if self.min_dims < dim_count:
- use[shape_id] &= cu.biased_coin(
- data, 1 - 1 / (1 + self.max_dims - dim)
- )
+ use[shape_id] &= data.draw_boolean(p_keep_extending_shape)
if use[shape_id]:
shape.append(side)
diff --git a/contrib/python/hypothesis/py3/hypothesis/extra/array_api.py b/contrib/python/hypothesis/py3/hypothesis/extra/array_api.py
index cb5a3fee409..d62418f317d 100644
--- a/contrib/python/hypothesis/py3/hypothesis/extra/array_api.py
+++ b/contrib/python/hypothesis/py3/hypothesis/extra/array_api.py
@@ -422,7 +422,7 @@ class ArrayStrategy(st.SearchStrategy):
seen = set()
while elements.more():
- i = cu.integer_range(data, 0, self.array_size - 1)
+ i = data.draw_integer(0, self.array_size - 1)
if i in assigned:
elements.reject()
continue
diff --git a/contrib/python/hypothesis/py3/hypothesis/extra/lark.py b/contrib/python/hypothesis/py3/hypothesis/extra/lark.py
index 9bab2c65cee..57b68ec9f77 100644
--- a/contrib/python/hypothesis/py3/hypothesis/extra/lark.py
+++ b/contrib/python/hypothesis/py3/hypothesis/extra/lark.py
@@ -158,7 +158,7 @@ class LarkStrategy(st.SearchStrategy):
data.stop_example()
def gen_ignore(self, data, draw_state):
- if self.ignored_symbols and data.draw_bits(2) == 3:
+ if self.ignored_symbols and data.draw_boolean(1 / 4):
emit = data.draw(st.sampled_from(self.ignored_symbols))
self.draw_symbol(data, emit, draw_state)
diff --git a/contrib/python/hypothesis/py3/hypothesis/extra/numpy.py b/contrib/python/hypothesis/py3/hypothesis/extra/numpy.py
index f7f27a74138..396b9498fe3 100644
--- a/contrib/python/hypothesis/py3/hypothesis/extra/numpy.py
+++ b/contrib/python/hypothesis/py3/hypothesis/extra/numpy.py
@@ -330,7 +330,7 @@ class ArrayStrategy(st.SearchStrategy):
seen = set()
while elements.more():
- i = cu.integer_range(data, 0, self.array_size - 1)
+ i = data.draw_integer(0, self.array_size - 1)
if not needs_fill[i]:
elements.reject()
continue
@@ -756,7 +756,7 @@ def array_dtypes(
field_names,
st.tuples(field_names, field_names).filter(lambda ns: ns[0] != ns[1]),
)
- elements = st.tuples(name_titles, subtype_strategy)
+ elements: st.SearchStrategy[tuple] = st.tuples(name_titles, subtype_strategy)
if allow_subarrays:
elements |= st.tuples(
name_titles, subtype_strategy, array_shapes(max_dims=2, max_side=2)
diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py
index c1337cde0fa..7eb5fbe0800 100644
--- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py
+++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py
@@ -8,19 +8,23 @@
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
# obtain one at https://mozilla.org/MPL/2.0/.
+import math
import time
from collections import defaultdict
from enum import IntEnum
from random import Random
+from sys import float_info
from typing import (
TYPE_CHECKING,
Any,
+ Callable,
Dict,
FrozenSet,
Hashable,
Iterable,
Iterator,
List,
+ Literal,
NoReturn,
Optional,
Sequence,
@@ -33,9 +37,27 @@ from typing import (
import attr
from hypothesis.errors import Frozen, InvalidArgument, StopTest
-from hypothesis.internal.compat import int_from_bytes, int_to_bytes
+from hypothesis.internal.cache import LRUReusedCache
+from hypothesis.internal.compat import floor, int_from_bytes, int_to_bytes
+from hypothesis.internal.conjecture.floats import float_to_lex, lex_to_float
from hypothesis.internal.conjecture.junkdrawer import IntList, uniform
-from hypothesis.internal.conjecture.utils import calc_label_from_name
+from hypothesis.internal.conjecture.utils import (
+ INT_SIZES,
+ INT_SIZES_SAMPLER,
+ Sampler,
+ calc_label_from_name,
+ many,
+)
+from hypothesis.internal.floats import (
+ SIGNALING_NAN,
+ SMALLEST_SUBNORMAL,
+ float_to_int,
+ make_float_clamper,
+ next_down,
+ next_up,
+ sign_aware_lte,
+)
+from hypothesis.internal.intervalsets import IntervalSet
if TYPE_CHECKING:
from typing_extensions import dataclass_transform
@@ -51,9 +73,17 @@ else:
return wrapper
+ONE_BOUND_INTEGERS_LABEL = calc_label_from_name("trying a one-bound int allowing 0")
+INTEGER_RANGE_DRAW_LABEL = calc_label_from_name("another draw in integer_range()")
+BIASED_COIN_LABEL = calc_label_from_name("biased_coin()")
+BIASED_COIN_INNER_LABEL = calc_label_from_name("inside biased_coin()")
+
TOP_LABEL = calc_label_from_name("top")
DRAW_BYTES_LABEL = calc_label_from_name("draw_bytes() in ConjectureData")
-
+DRAW_FLOAT_LABEL = calc_label_from_name("drawing a float")
+FLOAT_STRATEGY_DO_DRAW_LABEL = calc_label_from_name(
+ "getting another float in FloatStrategy"
+)
InterestingOrigin = Tuple[
Type[BaseException], str, int, Tuple[Any, ...], Tuple[Tuple[Any, ...], ...]
@@ -100,6 +130,39 @@ def structural_coverage(label: int) -> StructuralCoverageTag:
return STRUCTURAL_COVERAGE_CACHE.setdefault(label, StructuralCoverageTag(label))
+NASTY_FLOATS = sorted(
+ [
+ 0.0,
+ 0.5,
+ 1.1,
+ 1.5,
+ 1.9,
+ 1.0 / 3,
+ 10e6,
+ 10e-6,
+ 1.175494351e-38,
+ next_up(0.0),
+ float_info.min,
+ float_info.max,
+ 3.402823466e38,
+ 9007199254740992,
+ 1 - 10e-6,
+ 2 + 10e-6,
+ 1.192092896e-07,
+ 2.2204460492503131e-016,
+ ]
+ + [2.0**-n for n in (24, 14, 149, 126)] # minimum (sub)normals for float16,32
+ + [float_info.min / n for n in (2, 10, 1000, 100_000)] # subnormal in float64
+ + [math.inf, math.nan] * 5
+ + [SIGNALING_NAN],
+ key=float_to_lex,
+)
+NASTY_FLOATS = list(map(float, NASTY_FLOATS))
+NASTY_FLOATS.extend([-x for x in NASTY_FLOATS])
+
+FLOAT_INIT_LOGIC_CACHE = LRUReusedCache(4096)
+
+
class Example:
"""Examples track the hierarchical structure of draws from the byte stream,
within a single test run.
@@ -796,6 +859,475 @@ BYTE_MASKS = [(1 << n) - 1 for n in range(8)]
BYTE_MASKS[0] = 255
+class PrimitiveProvider:
+ # This is the low-level interface which would also be implemented
+ # by e.g. CrossHair, by an Atheris-hypothesis integration, etc.
+ # We'd then build the structured tree handling, database and replay
+ # support, etc. on top of this - so all backends get those for free.
+ #
+ # See https://github.com/HypothesisWorks/hypothesis/issues/3086
+
+ def __init__(self, conjecturedata: "ConjectureData", /) -> None:
+ self._cd = conjecturedata
+
+ def draw_boolean(self, p: float = 0.5, *, forced: Optional[bool] = None) -> bool:
+ """Return True with probability p (assuming a uniform generator),
+ shrinking towards False. If ``forced`` is set to a non-None value, this
+ will always return that value but will write choices appropriate to having
+ drawn that value randomly."""
+ # Note that this could also be implemented in terms of draw_integer().
+
+ # NB this function is vastly more complicated than it may seem reasonable
+ # for it to be. This is because it is used in a lot of places and it's
+ # important for it to shrink well, so it's worth the engineering effort.
+
+ if p <= 0 or p >= 1:
+ bits = 1
+ else:
+ # When there is a meaningful draw, in order to shrink well we will
+ # set things up so that 0 and 1 always correspond to False and True
+ # respectively. This means we want enough bits available that in a
+ # draw we will always have at least one truthy value and one falsey
+ # value.
+ bits = math.ceil(-math.log(min(p, 1 - p), 2))
+ # In order to avoid stupidly large draws where the probability is
+ # effectively zero or one, we treat probabilities of under 2^-64 to be
+ # effectively zero.
+ if bits > 64:
+ # There isn't enough precision near one for this to occur for values
+ # far from 0.
+ p = 0.0
+ bits = 1
+
+ size = 2**bits
+
+ self._cd.start_example(BIASED_COIN_LABEL)
+ while True:
+ # The logic here is a bit complicated and special cased to make it
+ # play better with the shrinker.
+
+ # We imagine partitioning the real interval [0, 1] into 2**n equal parts
+ # and looking at each part and whether its interior is wholly <= p
+ # or wholly >= p. At most one part can be neither.
+
+ # We then pick a random part. If it's wholly on one side or the other
+ # of p then we use that as the answer. If p is contained in the
+ # interval then we start again with a new probability that is given
+ # by the fraction of that interval that was <= our previous p.
+
+ # We then take advantage of the fact that we have control of the
+ # labelling to make this shrink better, using the following tricks:
+
+ # If p is <= 0 or >= 1 the result of this coin is certain. We make sure
+ # to write a byte to the data stream anyway so that these don't cause
+ # difficulties when shrinking.
+ if p <= 0:
+ self._cd.draw_bits(1, forced=0)
+ result = False
+ elif p >= 1:
+ self._cd.draw_bits(1, forced=1)
+ result = True
+ else:
+ falsey = floor(size * (1 - p))
+ truthy = floor(size * p)
+ remainder = size * p - truthy
+
+ if falsey + truthy == size:
+ partial = False
+ else:
+ partial = True
+
+ if forced is None:
+ # We want to get to the point where True is represented by
+ # 1 and False is represented by 0 as quickly as possible, so
+ # we use the remove_discarded machinery in the shrinker to
+ # achieve that by discarding any draws that are > 1 and writing
+ # a suitable draw into the choice sequence at the end of the
+ # loop.
+ self._cd.start_example(BIASED_COIN_INNER_LABEL)
+ i = self._cd.draw_bits(bits)
+ self._cd.stop_example(discard=i > 1)
+ else:
+ i = self._cd.draw_bits(bits, forced=int(forced))
+
+ # We always choose the region that causes us to repeat the loop as
+ # the maximum value, so that shrinking the drawn bits never causes
+ # us to need to draw more self._cd.
+ if partial and i == size - 1:
+ p = remainder
+ continue
+ if falsey == 0:
+ # Every other partition is truthy, so the result is true
+ result = True
+ elif truthy == 0:
+ # Every other partition is falsey, so the result is false
+ result = False
+ elif i <= 1:
+ # We special case so that zero is always false and 1 is always
+ # true which makes shrinking easier because we can always
+ # replace a truthy block with 1. This has the slightly weird
+ # property that shrinking from 2 to 1 can cause the result to
+ # grow, but the shrinker always tries 0 and 1 first anyway, so
+ # this will usually be fine.
+ result = bool(i)
+ else:
+ # Originally everything in the region 0 <= i < falsey was false
+ # and everything above was true. We swapped one truthy element
+ # into this region, so the region becomes 0 <= i <= falsey
+ # except for i = 1. We know i > 1 here, so the test for truth
+ # becomes i > falsey.
+ result = i > falsey
+
+ if i > 1:
+ self._cd.draw_bits(bits, forced=int(result))
+ break
+ self._cd.stop_example()
+ return result
+
+ def draw_integer(
+ self,
+ min_value: Optional[int] = None,
+ max_value: Optional[int] = None,
+ *,
+ # weights are for choosing an element index from a bounded range
+ weights: Optional[Sequence[float]] = None,
+ shrink_towards: int = 0,
+ forced: Optional[int] = None,
+ ) -> int:
+ # This is easy to build on top of our existing conjecture utils,
+ # and it's easy to build sampled_from and weighted_coin on this.
+ if weights is not None:
+ assert min_value is not None
+ assert max_value is not None
+
+ sampler = Sampler(weights)
+ idx = sampler.sample(self._cd)
+
+ if shrink_towards <= min_value:
+ return min_value + idx
+ elif max_value <= shrink_towards:
+ return max_value - idx
+ else:
+ # For range -2..2, interpret idx = 0..4 as [0, 1, 2, -1, -2]
+ if idx <= (gap := max_value - shrink_towards):
+ return shrink_towards + idx
+ else:
+ return shrink_towards - (idx - gap)
+
+ if min_value is None and max_value is None:
+ return self._draw_unbounded_integer()
+
+ if min_value is None:
+ assert max_value is not None # make mypy happy
+ if max_value <= shrink_towards:
+ return max_value - abs(self._draw_unbounded_integer())
+ else:
+ probe = max_value + 1
+ while max_value < probe:
+ self._cd.start_example(ONE_BOUND_INTEGERS_LABEL)
+ probe = self._draw_unbounded_integer() + shrink_towards
+ self._cd.stop_example(discard=max_value < probe)
+ return probe
+
+ if max_value is None:
+ assert min_value is not None
+ if min_value >= shrink_towards:
+ return min_value + abs(self._draw_unbounded_integer())
+ else:
+ probe = min_value - 1
+ while probe < min_value:
+ self._cd.start_example(ONE_BOUND_INTEGERS_LABEL)
+ probe = self._draw_unbounded_integer() + shrink_towards
+ self._cd.stop_example(discard=probe < min_value)
+ return probe
+
+ return self._draw_bounded_integer(
+ min_value,
+ max_value,
+ center=shrink_towards,
+ forced=forced,
+ )
+
+ def draw_float(
+ self,
+ *,
+ min_value: float = -math.inf,
+ max_value: float = math.inf,
+ allow_nan: bool = True,
+ smallest_nonzero_magnitude: float,
+ # TODO: consider supporting these float widths at the IR level in the
+ # future.
+ # width: Literal[16, 32, 64] = 64,
+ # exclude_min and exclude_max handled higher up
+ ) -> float:
+ (
+ sampler,
+ forced_sign_bit,
+ neg_clamper,
+ pos_clamper,
+ nasty_floats,
+ ) = self._draw_float_init_logic(
+ min_value=min_value,
+ max_value=max_value,
+ allow_nan=allow_nan,
+ smallest_nonzero_magnitude=smallest_nonzero_magnitude,
+ )
+
+ while True:
+ self._cd.start_example(FLOAT_STRATEGY_DO_DRAW_LABEL)
+ i = sampler.sample(self._cd) if sampler else 0
+ self._cd.start_example(DRAW_FLOAT_LABEL)
+ if i == 0:
+ result = self._draw_float(forced_sign_bit=forced_sign_bit)
+ if math.copysign(1.0, result) == -1:
+ assert neg_clamper is not None
+ clamped = -neg_clamper(-result)
+ else:
+ assert pos_clamper is not None
+ clamped = pos_clamper(result)
+ if clamped != result:
+ self._cd.stop_example(discard=True)
+ self._cd.start_example(DRAW_FLOAT_LABEL)
+ self._write_float(clamped)
+ result = clamped
+ else:
+ result = nasty_floats[i - 1]
+
+ self._write_float(result)
+
+ self._cd.stop_example() # (DRAW_FLOAT_LABEL)
+ self._cd.stop_example() # (FLOAT_STRATEGY_DO_DRAW_LABEL)
+ return result
+
+ def draw_string(
+ self,
+ intervals: IntervalSet,
+ *,
+ min_size: int = 0,
+ max_size: Optional[int] = None,
+ ) -> str:
+ if max_size is None:
+ max_size = 10**10 # "arbitrarily large"
+
+ average_size = min(
+ max(min_size * 2, min_size + 5),
+ 0.5 * (min_size + max_size),
+ )
+
+ chars = []
+ elements = many(
+ self._cd,
+ min_size=min_size,
+ max_size=max_size,
+ average_size=average_size,
+ )
+ while elements.more():
+ if len(intervals) > 256:
+ if self.draw_boolean(0.2):
+ i = self._draw_bounded_integer(256, len(intervals) - 1)
+ else:
+ i = self._draw_bounded_integer(0, 255)
+ else:
+ i = self._draw_bounded_integer(0, len(intervals) - 1)
+
+ chars.append(intervals.char_in_shrink_order(i))
+
+ return "".join(chars)
+
+ def draw_bytes(self, size: int) -> bytes:
+ return self._cd.draw_bits(8 * size).to_bytes(size, "big")
+
+ def _draw_float(self, forced_sign_bit: Optional[int] = None) -> float:
+ """
+ Helper for draw_float which draws a random 64-bit float.
+ """
+ self._cd.start_example(DRAW_FLOAT_LABEL)
+ try:
+ is_negative = self._cd.draw_bits(1, forced=forced_sign_bit)
+ f = lex_to_float(self._cd.draw_bits(64))
+ return -f if is_negative else f
+ finally:
+ self._cd.stop_example()
+
+ def _write_float(self, f: float) -> None:
+ sign = float_to_int(f) >> 63
+ self._cd.draw_bits(1, forced=sign)
+ self._cd.draw_bits(64, forced=float_to_lex(abs(f)))
+
+ def _draw_unbounded_integer(self) -> int:
+ size = INT_SIZES[INT_SIZES_SAMPLER.sample(self._cd)]
+ r = self._cd.draw_bits(size)
+ sign = r & 1
+ r >>= 1
+ if sign:
+ r = -r
+ return int(r)
+
+ def _draw_bounded_integer(
+ self,
+ lower: int,
+ upper: int,
+ *,
+ center: Optional[int] = None,
+ forced: Optional[int] = None,
+ ) -> int:
+ assert lower <= upper
+ assert forced is None or lower <= forced <= upper
+ if lower == upper:
+ # Write a value even when this is trivial so that when a bound depends
+ # on other values we don't suddenly disappear when the gap shrinks to
+ # zero - if that happens then often the data stream becomes misaligned
+ # and we fail to shrink in cases where we really should be able to.
+ self._cd.draw_bits(1, forced=0)
+ return int(lower)
+
+ if center is None:
+ center = lower
+ center = min(max(center, lower), upper)
+
+ if center == upper:
+ above = False
+ elif center == lower:
+ above = True
+ else:
+ force_above = None if forced is None else forced < center
+ above = not self._cd.draw_bits(1, forced=force_above)
+
+ if above:
+ gap = upper - center
+ else:
+ gap = center - lower
+
+ assert gap > 0
+
+ bits = gap.bit_length()
+ probe = gap + 1
+
+ if bits > 24 and self._cd.draw_bits(3, forced=None if forced is None else 0):
+ # For large ranges, we combine the uniform random distribution from draw_bits
+ # with a weighting scheme with moderate chance. Cutoff at 2 ** 24 so that our
+ # choice of unicode characters is uniform but the 32bit distribution is not.
+ idx = INT_SIZES_SAMPLER.sample(self._cd)
+ bits = min(bits, INT_SIZES[idx])
+
+ while probe > gap:
+ self._cd.start_example(INTEGER_RANGE_DRAW_LABEL)
+ probe = self._cd.draw_bits(
+ bits, forced=None if forced is None else abs(forced - center)
+ )
+ self._cd.stop_example(discard=probe > gap)
+
+ if above:
+ result = center + probe
+ else:
+ result = center - probe
+
+ assert lower <= result <= upper
+ assert forced is None or result == forced, (result, forced, center, above)
+ return result
+
+ @classmethod
+ def _draw_float_init_logic(
+ cls,
+ *,
+ min_value: float,
+ max_value: float,
+ allow_nan: bool,
+ smallest_nonzero_magnitude: float,
+ ) -> Tuple[
+ Optional[Sampler],
+ Optional[Literal[0, 1]],
+ Optional[Callable[[float], float]],
+ Optional[Callable[[float], float]],
+ List[float],
+ ]:
+ """
+ Caches initialization logic for draw_float, as an alternative to
+ computing this for *every* float draw.
+ """
+ # float_to_int allows us to distinguish between e.g. -0.0 and 0.0,
+ # even in light of hash(-0.0) == hash(0.0) and -0.0 == 0.0.
+ key = (
+ float_to_int(min_value),
+ float_to_int(max_value),
+ allow_nan,
+ float_to_int(smallest_nonzero_magnitude),
+ )
+ if key in FLOAT_INIT_LOGIC_CACHE:
+ return FLOAT_INIT_LOGIC_CACHE[key]
+
+ result = cls._compute_draw_float_init_logic(
+ min_value=min_value,
+ max_value=max_value,
+ allow_nan=allow_nan,
+ smallest_nonzero_magnitude=smallest_nonzero_magnitude,
+ )
+ FLOAT_INIT_LOGIC_CACHE[key] = result
+ return result
+
+ @staticmethod
+ def _compute_draw_float_init_logic(
+ *,
+ min_value: float,
+ max_value: float,
+ allow_nan: bool,
+ smallest_nonzero_magnitude: float,
+ ) -> Tuple[
+ Optional[Sampler],
+ Optional[Literal[0, 1]],
+ Optional[Callable[[float], float]],
+ Optional[Callable[[float], float]],
+ List[float],
+ ]:
+ if smallest_nonzero_magnitude == 0.0: # pragma: no cover
+ raise FloatingPointError(
+ "Got allow_subnormal=True, but we can't represent subnormal floats "
+ "right now, in violation of the IEEE-754 floating-point "
+ "specification. This is usually because something was compiled with "
+ "-ffast-math or a similar option, which sets global processor state. "
+ "See https://simonbyrne.github.io/notes/fastmath/ for a more detailed "
+ "writeup - and good luck!"
+ )
+
+ def permitted(f):
+ assert isinstance(f, float)
+ if math.isnan(f):
+ return allow_nan
+ if 0 < abs(f) < smallest_nonzero_magnitude:
+ return False
+ return sign_aware_lte(min_value, f) and sign_aware_lte(f, max_value)
+
+ boundary_values = [
+ min_value,
+ next_up(min_value),
+ min_value + 1,
+ max_value - 1,
+ next_down(max_value),
+ max_value,
+ ]
+ nasty_floats = [f for f in NASTY_FLOATS + boundary_values if permitted(f)]
+ weights = [0.2 * len(nasty_floats)] + [0.8] * len(nasty_floats)
+ sampler = Sampler(weights) if nasty_floats else None
+
+ pos_clamper = neg_clamper = None
+ if sign_aware_lte(0.0, max_value):
+ pos_min = max(min_value, smallest_nonzero_magnitude)
+ allow_zero = sign_aware_lte(min_value, 0.0)
+ pos_clamper = make_float_clamper(pos_min, max_value, allow_zero=allow_zero)
+ if sign_aware_lte(min_value, -0.0):
+ neg_max = min(max_value, -smallest_nonzero_magnitude)
+ allow_zero = sign_aware_lte(-0.0, max_value)
+ neg_clamper = make_float_clamper(
+ -neg_max, -min_value, allow_zero=allow_zero
+ )
+
+ forced_sign_bit: Optional[Literal[0, 1]] = None
+ if (pos_clamper is None) != (neg_clamper is None):
+ forced_sign_bit = 1 if neg_clamper else 0
+
+ return (sampler, forced_sign_bit, neg_clamper, pos_clamper, nasty_floats)
+
+
class ConjectureData:
@classmethod
def for_buffer(
@@ -841,6 +1373,7 @@ class ConjectureData:
self.draw_times: "List[float]" = []
self.max_depth = 0
self.has_discards = False
+ self.provider = PrimitiveProvider(self)
self.__result: "Optional[ConjectureResult]" = None
@@ -879,6 +1412,71 @@ class ConjectureData:
", frozen" if self.frozen else "",
)
+ def draw_integer(
+ self,
+ min_value: Optional[int] = None,
+ max_value: Optional[int] = None,
+ *,
+ # weights are for choosing an element index from a bounded range
+ weights: Optional[Sequence[float]] = None,
+ shrink_towards: int = 0,
+ forced: Optional[int] = None,
+ ) -> int:
+ # Validate arguments
+ if weights is not None:
+ assert min_value is not None
+ assert max_value is not None
+ assert (max_value - min_value) <= 1024 # arbitrary practical limit
+
+ if forced is not None:
+ assert min_value is not None
+ assert max_value is not None
+
+ return self.provider.draw_integer(
+ min_value=min_value,
+ max_value=max_value,
+ weights=weights,
+ shrink_towards=shrink_towards,
+ forced=forced,
+ )
+
+ def draw_float(
+ self,
+ min_value: float = -math.inf,
+ max_value: float = math.inf,
+ *,
+ allow_nan: bool = True,
+ smallest_nonzero_magnitude: float = SMALLEST_SUBNORMAL,
+ # TODO: consider supporting these float widths at the IR level in the
+ # future.
+ # width: Literal[16, 32, 64] = 64,
+ # exclude_min and exclude_max handled higher up
+ ) -> float:
+ assert smallest_nonzero_magnitude > 0
+ return self.provider.draw_float(
+ min_value=min_value,
+ max_value=max_value,
+ allow_nan=allow_nan,
+ smallest_nonzero_magnitude=smallest_nonzero_magnitude,
+ )
+
+ def draw_string(
+ self,
+ intervals: IntervalSet,
+ *,
+ min_size: int = 0,
+ max_size: Optional[int] = None,
+ ) -> str:
+ return self.provider.draw_string(
+ intervals, min_size=min_size, max_size=max_size
+ )
+
+ def draw_bytes(self, size: int) -> bytes:
+ return self.provider.draw_bytes(size)
+
+ def draw_boolean(self, p: float = 0.5, *, forced: Optional[bool] = None) -> bool:
+ return self.provider.draw_boolean(p, forced=forced)
+
def as_result(self) -> Union[ConjectureResult, _Overrun]:
"""Convert the result of running this test into
either an Overrun object or a ConjectureResult."""
@@ -1099,10 +1697,6 @@ class ConjectureData:
assert result.bit_length() <= n
return result
- def draw_bytes(self, n: int) -> bytes:
- """Draw n bytes from the underlying source."""
- return int_to_bytes(self.draw_bits(8 * n), n)
-
def write(self, string: bytes) -> Optional[bytes]:
"""Write ``string`` to the output buffer."""
self.__assert_not_frozen("write")
diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/floats.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/floats.py
index 5608570130b..2b82bea07c0 100644
--- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/floats.py
+++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/floats.py
@@ -9,14 +9,9 @@
# obtain one at https://mozilla.org/MPL/2.0/.
from array import array
-from typing import TYPE_CHECKING, Optional
-from hypothesis.internal.conjecture.utils import calc_label_from_name
from hypothesis.internal.floats import float_to_int, int_to_float
-if TYPE_CHECKING:
- from hypothesis.internal.conjecture.data import ConjectureData
-
"""
This module implements support for arbitrary floating point numbers in
Conjecture. It doesn't make any attempt to get a good distribution, only to
@@ -83,8 +78,6 @@ MAX_EXPONENT = 0x7FF
BIAS = 1023
MAX_POSITIVE_EXPONENT = MAX_EXPONENT - 1 - BIAS
-DRAW_FLOAT_LABEL = calc_label_from_name("drawing a float")
-
def exponent_key(e: int) -> float:
if e == MAX_EXPONENT:
@@ -224,19 +217,3 @@ def is_simple(f: float) -> int:
if i != f:
return False
return i.bit_length() <= 56
-
-
-def draw_float(data: "ConjectureData", forced_sign_bit: Optional[int] = None) -> float:
- try:
- data.start_example(DRAW_FLOAT_LABEL)
- is_negative = data.draw_bits(1, forced=forced_sign_bit)
- f = lex_to_float(data.draw_bits(64))
- return -f if is_negative else f
- finally:
- data.stop_example()
-
-
-def write_float(data: "ConjectureData", f: float) -> None:
- sign = float_to_int(f) >> 63
- data.draw_bits(1, forced=sign)
- data.draw_bits(64, forced=float_to_lex(abs(f)))
diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py
index 69bec609404..f965829759a 100644
--- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py
+++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py
@@ -19,13 +19,14 @@ from hypothesis.internal.conjecture.choicetree import (
prefix_selection_order,
random_selection_order,
)
-from hypothesis.internal.conjecture.data import ConjectureData, ConjectureResult, Status
-from hypothesis.internal.conjecture.dfa import ConcreteDFA
-from hypothesis.internal.conjecture.floats import (
+from hypothesis.internal.conjecture.data import (
DRAW_FLOAT_LABEL,
- float_to_lex,
- lex_to_float,
+ ConjectureData,
+ ConjectureResult,
+ Status,
)
+from hypothesis.internal.conjecture.dfa import ConcreteDFA
+from hypothesis.internal.conjecture.floats import float_to_lex, lex_to_float
from hypothesis.internal.conjecture.junkdrawer import (
binary_search,
find_integer,
diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/utils.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/utils.py
index 7402bb47d38..48a5ec3f27c 100644
--- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/utils.py
+++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/utils.py
@@ -11,15 +11,14 @@
import enum
import hashlib
import heapq
-import math
import sys
from collections import OrderedDict, abc
from functools import lru_cache
from typing import TYPE_CHECKING, List, Optional, Sequence, Tuple, Type, TypeVar, Union
from hypothesis.errors import InvalidArgument
-from hypothesis.internal.compat import floor, int_from_bytes
-from hypothesis.internal.floats import int_to_float, next_up
+from hypothesis.internal.compat import int_from_bytes
+from hypothesis.internal.floats import next_up
if TYPE_CHECKING:
from hypothesis.internal.conjecture.data import ConjectureData
@@ -45,86 +44,10 @@ def combine_labels(*labels: int) -> int:
return label
-INTEGER_RANGE_DRAW_LABEL = calc_label_from_name("another draw in integer_range()")
-BIASED_COIN_LABEL = calc_label_from_name("biased_coin()")
-BIASED_COIN_INNER_LABEL = calc_label_from_name("inside biased_coin()")
SAMPLE_IN_SAMPLER_LABEL = calc_label_from_name("a sample() in Sampler")
ONE_FROM_MANY_LABEL = calc_label_from_name("one more from many()")
-def unbounded_integers(data: "ConjectureData") -> int:
- size = INT_SIZES[INT_SIZES_SAMPLER.sample(data)]
- r = data.draw_bits(size)
- sign = r & 1
- r >>= 1
- if sign:
- r = -r
- return int(r)
-
-
-def integer_range(
- data: "ConjectureData",
- lower: int,
- upper: int,
- center: Optional[int] = None,
- forced: Optional[int] = None,
-) -> int:
- assert lower <= upper
- assert forced is None or lower <= forced <= upper
- if lower == upper:
- # Write a value even when this is trivial so that when a bound depends
- # on other values we don't suddenly disappear when the gap shrinks to
- # zero - if that happens then often the data stream becomes misaligned
- # and we fail to shrink in cases where we really should be able to.
- data.draw_bits(1, forced=0)
- return int(lower)
-
- if center is None:
- center = lower
- center = min(max(center, lower), upper)
-
- if center == upper:
- above = False
- elif center == lower:
- above = True
- else:
- force_above = None if forced is None else forced < center
- above = not data.draw_bits(1, forced=force_above)
-
- if above:
- gap = upper - center
- else:
- gap = center - lower
-
- assert gap > 0
-
- bits = gap.bit_length()
- probe = gap + 1
-
- if bits > 24 and data.draw_bits(3, forced=None if forced is None else 0):
- # For large ranges, we combine the uniform random distribution from draw_bits
- # with a weighting scheme with moderate chance. Cutoff at 2 ** 24 so that our
- # choice of unicode characters is uniform but the 32bit distribution is not.
- idx = INT_SIZES_SAMPLER.sample(data)
- bits = min(bits, INT_SIZES[idx])
-
- while probe > gap:
- data.start_example(INTEGER_RANGE_DRAW_LABEL)
- probe = data.draw_bits(
- bits, forced=None if forced is None else abs(forced - center)
- )
- data.stop_example(discard=probe > gap)
-
- if above:
- result = center + probe
- else:
- result = center - probe
-
- assert lower <= result <= upper
- assert forced is None or result == forced, (result, forced, center, above)
- return result
-
-
T = TypeVar("T")
@@ -159,131 +82,7 @@ def check_sample(
def choice(data: "ConjectureData", values: Sequence[T]) -> T:
- return values[integer_range(data, 0, len(values) - 1)]
-
-
-FLOAT_PREFIX = 0b1111111111 << 52
-FULL_FLOAT = int_to_float(FLOAT_PREFIX | ((2 << 53) - 1)) - 1
-
-
-def fractional_float(data: "ConjectureData") -> float:
- return (int_to_float(FLOAT_PREFIX | data.draw_bits(52)) - 1) / FULL_FLOAT
-
-
-def biased_coin(
- data: "ConjectureData", p: float, *, forced: Optional[bool] = None
-) -> bool:
- """Return True with probability p (assuming a uniform generator),
- shrinking towards False. If ``forced`` is set to a non-None value, this
- will always return that value but will write choices appropriate to having
- drawn that value randomly."""
-
- # NB this function is vastly more complicated than it may seem reasonable
- # for it to be. This is because it is used in a lot of places and it's
- # important for it to shrink well, so it's worth the engineering effort.
-
- if p <= 0 or p >= 1:
- bits = 1
- else:
- # When there is a meaningful draw, in order to shrink well we will
- # set things up so that 0 and 1 always correspond to False and True
- # respectively. This means we want enough bits available that in a
- # draw we will always have at least one truthy value and one falsey
- # value.
- bits = math.ceil(-math.log(min(p, 1 - p), 2))
- # In order to avoid stupidly large draws where the probability is
- # effectively zero or one, we treat probabilities of under 2^-64 to be
- # effectively zero.
- if bits > 64:
- # There isn't enough precision near one for this to occur for values
- # far from 0.
- p = 0.0
- bits = 1
-
- size = 2**bits
-
- data.start_example(BIASED_COIN_LABEL)
- while True:
- # The logic here is a bit complicated and special cased to make it
- # play better with the shrinker.
-
- # We imagine partitioning the real interval [0, 1] into 2**n equal parts
- # and looking at each part and whether its interior is wholly <= p
- # or wholly >= p. At most one part can be neither.
-
- # We then pick a random part. If it's wholly on one side or the other
- # of p then we use that as the answer. If p is contained in the
- # interval then we start again with a new probability that is given
- # by the fraction of that interval that was <= our previous p.
-
- # We then take advantage of the fact that we have control of the
- # labelling to make this shrink better, using the following tricks:
-
- # If p is <= 0 or >= 1 the result of this coin is certain. We make sure
- # to write a byte to the data stream anyway so that these don't cause
- # difficulties when shrinking.
- if p <= 0:
- data.draw_bits(1, forced=0)
- result = False
- elif p >= 1:
- data.draw_bits(1, forced=1)
- result = True
- else:
- falsey = floor(size * (1 - p))
- truthy = floor(size * p)
- remainder = size * p - truthy
-
- if falsey + truthy == size:
- partial = False
- else:
- partial = True
-
- if forced is None:
- # We want to get to the point where True is represented by
- # 1 and False is represented by 0 as quickly as possible, so
- # we use the remove_discarded machinery in the shrinker to
- # achieve that by discarding any draws that are > 1 and writing
- # a suitable draw into the choice sequence at the end of the
- # loop.
- data.start_example(BIASED_COIN_INNER_LABEL)
- i = data.draw_bits(bits)
- data.stop_example(discard=i > 1)
- else:
- i = data.draw_bits(bits, forced=int(forced))
-
- # We always choose the region that causes us to repeat the loop as
- # the maximum value, so that shrinking the drawn bits never causes
- # us to need to draw more data.
- if partial and i == size - 1:
- p = remainder
- continue
- if falsey == 0:
- # Every other partition is truthy, so the result is true
- result = True
- elif truthy == 0:
- # Every other partition is falsey, so the result is false
- result = False
- elif i <= 1:
- # We special case so that zero is always false and 1 is always
- # true which makes shrinking easier because we can always
- # replace a truthy block with 1. This has the slightly weird
- # property that shrinking from 2 to 1 can cause the result to
- # grow, but the shrinker always tries 0 and 1 first anyway, so
- # this will usually be fine.
- result = bool(i)
- else:
- # Originally everything in the region 0 <= i < falsey was false
- # and everything above was true. We swapped one truthy element
- # into this region, so the region becomes 0 <= i <= falsey
- # except for i = 1. We know i > 1 here, so the test for truth
- # becomes i > falsey.
- result = i > falsey
-
- if i > 1:
- data.draw_bits(bits, forced=int(result))
- break
- data.stop_example()
- return result
+ return values[data.draw_integer(0, len(values) - 1)]
class Sampler:
@@ -375,7 +174,7 @@ class Sampler:
def sample(self, data: "ConjectureData") -> int:
data.start_example(SAMPLE_IN_SAMPLER_LABEL)
base, alternate, alternate_chance = choice(data, self.table)
- use_alternate = biased_coin(data, alternate_chance)
+ use_alternate = data.draw_boolean(alternate_chance)
data.stop_example()
if use_alternate:
return alternate
@@ -437,8 +236,8 @@ class many:
forced_result = True
elif self.count >= self.max_size:
forced_result = False
- should_continue = biased_coin(
- self.data, self.p_continue, forced=forced_result
+ should_continue = self.data.draw_boolean(
+ self.p_continue, forced=forced_result
)
if should_continue:
diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/floats.py b/contrib/python/hypothesis/py3/hypothesis/internal/floats.py
index e0cd95d3741..6c4210e997b 100644
--- a/contrib/python/hypothesis/py3/hypothesis/internal/floats.py
+++ b/contrib/python/hypothesis/py3/hypothesis/internal/floats.py
@@ -149,3 +149,17 @@ def make_float_clamper(
return max(min_float, min(max_float, float_val))
return float_clamper
+
+
+def sign_aware_lte(x: float, y: float) -> bool:
+ """Less-than-or-equals, but strictly orders -0.0 and 0.0"""
+ if x == 0.0 == y:
+ return math.copysign(1.0, x) <= math.copysign(1.0, y)
+ else:
+ return x <= y
+
+
+SMALLEST_SUBNORMAL = next_up(0.0)
+SIGNALING_NAN = int_to_float(0x7FF8_0000_0000_0001) # nonzero mantissa
+assert math.isnan(SIGNALING_NAN)
+assert math.copysign(1, SIGNALING_NAN) == 1
diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/intervalsets.py b/contrib/python/hypothesis/py3/hypothesis/internal/intervalsets.py
index a4d88515819..c5e82f6b22c 100644
--- a/contrib/python/hypothesis/py3/hypothesis/internal/intervalsets.py
+++ b/contrib/python/hypothesis/py3/hypothesis/internal/intervalsets.py
@@ -26,6 +26,8 @@ class IntervalSet:
for u, v in self.intervals:
self.offsets.append(self.offsets[-1] + v - u + 1)
self.size = self.offsets.pop()
+ self._idx_of_zero = self.index_above(ord("0"))
+ self._idx_of_Z = min(self.index_above(ord("Z")), len(self) - 1)
def __len__(self):
return self.size
@@ -228,3 +230,27 @@ class IntervalSet:
else:
j += 1
return IntervalSet(intervals)
+
+ def char_in_shrink_order(self, i: int) -> str:
+ # We would like it so that, where possible, shrinking replaces
+ # characters with simple ascii characters, so we rejig this
+ # bit so that the smallest values are 0, 1, 2, ..., Z.
+ #
+ # Imagine that numbers are laid out as abc0yyyZ...
+ # this rearranges them so that they are laid out as
+ # 0yyyZcba..., which gives a better shrinking order.
+ if i <= self._idx_of_Z:
+ # We want to rewrite the integers [0, n] inclusive
+ # to [zero_point, Z_point].
+ n = self._idx_of_Z - self._idx_of_zero
+ if i <= n:
+ i += self._idx_of_zero
+ else:
+ # We want to rewrite the integers [n + 1, Z_point] to
+ # [zero_point, 0] (reversing the order so that codepoints below
+ # zero_point shrink upwards).
+ i = self._idx_of_zero - (i - n)
+ assert i < self._idx_of_zero
+ assert 0 <= i <= self._idx_of_Z
+
+ return chr(self[i])
diff --git a/contrib/python/hypothesis/py3/hypothesis/stateful.py b/contrib/python/hypothesis/py3/hypothesis/stateful.py
index b827d50d2db..e70c805815a 100644
--- a/contrib/python/hypothesis/py3/hypothesis/stateful.py
+++ b/contrib/python/hypothesis/py3/hypothesis/stateful.py
@@ -140,7 +140,7 @@ def run_state_machine_as_test(state_machine_factory, *, settings=None, _min_step
must_stop = True
elif steps_run <= _min_steps:
must_stop = False
- if cu.biased_coin(cd, 2**-16, forced=must_stop):
+ if cd.draw_boolean(p=2**-16, forced=must_stop):
break
steps_run += 1
@@ -220,9 +220,10 @@ def run_state_machine_as_test(state_machine_factory, *, settings=None, _min_step
class StateMachineMeta(type):
def __setattr__(cls, name, value):
if name == "settings" and isinstance(value, Settings):
+ descr = f"settings({value.show_changed()})"
raise AttributeError(
- f"Assigning {cls.__name__}.settings = {value} does nothing. Assign "
- f"to {cls.__name__}.TestCase.settings, or use @{value} as a decorator "
+ f"Assigning {cls.__name__}.settings = {descr} does nothing. Assign "
+ f"to {cls.__name__}.TestCase.settings, or use @{descr} as a decorator "
f"on the {cls.__name__} class."
)
return super().__setattr__(name, value)
@@ -255,6 +256,15 @@ class RuleBasedStateMachine(metaclass=StateMachineMeta):
self._initialize_rules_to_run = copy(self.initialize_rules())
self._rules_strategy = RuleStrategy(self)
+ if isinstance(s := vars(type(self)).get("settings"), Settings):
+ tname = type(self).__name__
+ descr = f"settings({s.show_changed()})"
+ raise InvalidDefinition(
+ f"Assigning settings = {descr} as a class attribute does nothing. "
+ f"Assign to {tname}.TestCase.settings, or use @{descr} as a decorator "
+ f"on the {tname} class."
+ )
+
def _pretty_print(self, value):
if isinstance(value, VarReference):
return value.name
@@ -440,7 +450,7 @@ class BundleReferenceStrategy(SearchStrategy):
# Shrink towards the right rather than the left. This makes it easier
# to delete data generated earlier, as when the error is towards the
# end there can be a lot of hard to remove padding.
- position = cu.integer_range(data, 0, len(bundle) - 1, center=len(bundle))
+ position = data.draw_integer(0, len(bundle) - 1, shrink_towards=len(bundle))
if self.consume:
return bundle.pop(position) # pragma: no cover # coverage is flaky here
else:
diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/collections.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/collections.py
index 5d9c9617aa3..2bee499ac2e 100644
--- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/collections.py
+++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/collections.py
@@ -260,7 +260,7 @@ class UniqueSampledListStrategy(UniqueListStrategy):
while remaining and should_draw.more():
i = len(remaining) - 1
- j = cu.integer_range(data, 0, i)
+ j = data.draw_integer(0, i)
if j != i:
remaining[i], remaining[j] = remaining[j], remaining[i]
value = self.element_strategy._transform(remaining.pop())
@@ -330,7 +330,7 @@ class FixedAndOptionalKeysDictStrategy(SearchStrategy):
data, min_size=0, max_size=len(remaining), average_size=len(remaining) / 2
)
while should_draw.more():
- j = cu.integer_range(data, 0, len(remaining) - 1)
+ j = data.draw_integer(0, len(remaining) - 1)
remaining[-1], remaining[j] = remaining[j], remaining[-1]
key = remaining.pop()
result[key] = data.draw(self.optional[key])
diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py
index 3624e7e239a..b8c56015877 100644
--- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py
+++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py
@@ -74,11 +74,7 @@ from hypothesis.internal.compat import (
get_type_hints,
is_typed_named_tuple,
)
-from hypothesis.internal.conjecture.utils import (
- calc_label_from_cls,
- check_sample,
- integer_range,
-)
+from hypothesis.internal.conjecture.utils import calc_label_from_cls, check_sample
from hypothesis.internal.entropy import get_seeder_and_restorer
from hypothesis.internal.floats import float_of
from hypothesis.internal.reflection import (
@@ -1707,7 +1703,7 @@ class PermutationStrategy(SearchStrategy):
# change. We don't consider the last element as it's always a no-op.
result = list(self.values)
for i in range(len(result) - 1):
- j = integer_range(data, i, len(result) - 1)
+ j = data.draw_integer(i, len(result) - 1)
result[i], result[j] = result[j], result[i]
return result
@@ -2011,7 +2007,7 @@ def shared(
@composite
def _maybe_nil_uuids(draw, uuid):
# Equivalent to `random_uuids | just(...)`, with a stronger bias to the former.
- if draw(data()).conjecture_data.draw_bits(6) == 63:
+ if draw(data()).conjecture_data.draw_boolean(1 / 64):
return UUID("00000000-0000-0000-0000-000000000000")
return uuid
diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/datetime.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/datetime.py
index a23779711f7..abf77c5026f 100644
--- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/datetime.py
+++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/datetime.py
@@ -16,7 +16,6 @@ from pathlib import Path
from typing import Optional
from hypothesis.errors import InvalidArgument
-from hypothesis.internal.conjecture import utils
from hypothesis.internal.validation import check_type, check_valid_interval
from hypothesis.strategies._internal.core import sampled_from
from hypothesis.strategies._internal.misc import just, none
@@ -113,9 +112,9 @@ def draw_capped_multipart(
if name == "day" and not cap_high:
_, high = monthrange(**result)
if name == "year":
- val = utils.integer_range(data, low, high, 2000)
+ val = data.draw_integer(low, high, shrink_towards=2000)
else:
- val = utils.integer_range(data, low, high)
+ val = data.draw_integer(low, high)
result[name] = val
cap_low = cap_low and val == low
cap_high = cap_high and val == high
@@ -125,7 +124,7 @@ def draw_capped_multipart(
# the logic above, and be very sensitive to the specific timezone
# (at the cost of efficient shrinking and mutation), so at least for
# now we stick with the status quo and generate it independently.
- result["fold"] = utils.integer_range(data, 0, 1)
+ result["fold"] = data.draw_integer(0, 1)
return result
@@ -312,7 +311,7 @@ class TimedeltaStrategy(SearchStrategy):
for name in ("days", "seconds", "microseconds"):
low = getattr(self.min_value if low_bound else dt.timedelta.min, name)
high = getattr(self.max_value if high_bound else dt.timedelta.max, name)
- val = utils.integer_range(data, low, high, 0)
+ val = data.draw_integer(low, high)
result[name] = val
low_bound = low_bound and val == low
high_bound = high_bound and val == high
diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/featureflags.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/featureflags.py
index 66d648cce19..cf72b5c10bf 100644
--- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/featureflags.py
+++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/featureflags.py
@@ -52,7 +52,7 @@ class FeatureFlags:
# features will be enabled. This is so that we shrink in the direction
# of more features being enabled.
if self.__data is not None:
- self.__p_disabled = data.draw_bits(8) / 255.0
+ self.__p_disabled = data.draw_integer(0, 255) / 255.0
else:
# If data is None we're in example mode so all that matters is the
# enabled/disabled lists above. We set this up so that everything
@@ -81,8 +81,8 @@ class FeatureFlags:
# of the test case where we originally decided, the next point at
# which we make this decision just makes the decision it previously
# made.
- is_disabled = cu.biased_coin(
- self.__data, self.__p_disabled, forced=self.__is_disabled.get(name)
+ is_disabled = self.__data.draw_boolean(
+ self.__p_disabled, forced=self.__is_disabled.get(name)
)
self.__is_disabled[name] = is_disabled
data.stop_example()
diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/numbers.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/numbers.py
index ae66a2c1b23..302f6f92b55 100644
--- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/numbers.py
+++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/numbers.py
@@ -11,23 +11,20 @@
import math
from decimal import Decimal
from fractions import Fraction
-from sys import float_info
from typing import Literal, Optional, Union
from hypothesis.control import reject
from hypothesis.errors import InvalidArgument
-from hypothesis.internal.conjecture import floats as flt, utils as d
-from hypothesis.internal.conjecture.utils import calc_label_from_name
from hypothesis.internal.filtering import (
get_float_predicate_bounds,
get_integer_predicate_bounds,
)
from hypothesis.internal.floats import (
+ SMALLEST_SUBNORMAL,
float_of,
float_to_int,
int_to_float,
is_negative,
- make_float_clamper,
next_down,
next_down_normal,
next_up,
@@ -48,7 +45,6 @@ from hypothesis.strategies._internal.utils import cacheable, defines_strategy
# See https://github.com/python/mypy/issues/3186 - numbers.Real is wrong!
Real = Union[int, float, Fraction, Decimal]
-ONE_BOUND_INTEGERS_LABEL = d.calc_label_from_name("trying a one-bound int allowing 0")
class IntegersStrategy(SearchStrategy):
@@ -69,34 +65,14 @@ class IntegersStrategy(SearchStrategy):
return f"integers({self.start}, {self.end})"
def do_draw(self, data):
- if self.start is None and self.end is None:
- return d.unbounded_integers(data)
-
- if self.start is None:
- if self.end <= 0:
- return self.end - abs(d.unbounded_integers(data))
- else:
- probe = self.end + 1
- while self.end < probe:
- data.start_example(ONE_BOUND_INTEGERS_LABEL)
- probe = d.unbounded_integers(data)
- data.stop_example(discard=self.end < probe)
- return probe
-
- if self.end is None:
- if self.start >= 0:
- return self.start + abs(d.unbounded_integers(data))
- else:
- probe = self.start - 1
- while probe < self.start:
- data.start_example(ONE_BOUND_INTEGERS_LABEL)
- probe = d.unbounded_integers(data)
- data.stop_example(discard=probe < self.start)
- return probe
-
# For bounded integers, make the bounds and near-bounds more likely.
forced = None
- if self.end - self.start > 127:
+ if (
+ self.end is not None
+ and self.start is not None
+ and self.end - self.start > 127
+ ):
+ bits = data.draw_integer(0, 127)
forced = {
122: self.start,
123: self.start,
@@ -104,9 +80,11 @@ class IntegersStrategy(SearchStrategy):
125: self.end,
126: self.start + 1,
127: self.end - 1,
- }.get(data.draw_bits(7))
+ }.get(bits)
- return d.integer_range(data, self.start, self.end, center=0, forced=forced)
+ return data.draw_integer(
+ min_value=self.start, max_value=self.end, forced=forced
+ )
def filter(self, condition):
if condition is math.isfinite:
@@ -166,54 +144,6 @@ def integers(
return IntegersStrategy(min_value, max_value)
-SMALLEST_SUBNORMAL = next_up(0.0)
-SIGNALING_NAN = int_to_float(0x7FF8_0000_0000_0001) # nonzero mantissa
-assert math.isnan(SIGNALING_NAN)
-assert math.copysign(1, SIGNALING_NAN) == 1
-
-NASTY_FLOATS = sorted(
- [
- 0.0,
- 0.5,
- 1.1,
- 1.5,
- 1.9,
- 1.0 / 3,
- 10e6,
- 10e-6,
- 1.175494351e-38,
- next_up(0.0),
- float_info.min,
- float_info.max,
- 3.402823466e38,
- 9007199254740992,
- 1 - 10e-6,
- 2 + 10e-6,
- 1.192092896e-07,
- 2.2204460492503131e-016,
- ]
- + [2.0**-n for n in (24, 14, 149, 126)] # minimum (sub)normals for float16,32
- + [float_info.min / n for n in (2, 10, 1000, 100_000)] # subnormal in float64
- + [math.inf, math.nan] * 5
- + [SIGNALING_NAN],
- key=flt.float_to_lex,
-)
-NASTY_FLOATS = list(map(float, NASTY_FLOATS))
-NASTY_FLOATS.extend([-x for x in NASTY_FLOATS])
-
-FLOAT_STRATEGY_DO_DRAW_LABEL = calc_label_from_name(
- "getting another float in FloatStrategy"
-)
-
-
-def _sign_aware_lte(x: float, y: float) -> bool:
- """Less-than-or-equals, but strictly orders -0.0 and 0.0"""
- if x == 0.0 == y:
- return math.copysign(1.0, x) <= math.copysign(1.0, y)
- else:
- return x <= y
-
-
class FloatStrategy(SearchStrategy):
"""A strategy for floating point numbers."""
@@ -246,38 +176,6 @@ class FloatStrategy(SearchStrategy):
self.allow_nan = allow_nan
self.smallest_nonzero_magnitude = smallest_nonzero_magnitude
- boundary_values = [
- min_value,
- next_up(min_value),
- min_value + 1,
- max_value - 1,
- next_down(max_value),
- max_value,
- ]
- self.nasty_floats = [
- f for f in NASTY_FLOATS + boundary_values if self.permitted(f)
- ]
- weights = [0.2 * len(self.nasty_floats)] + [0.8] * len(self.nasty_floats)
- self.sampler = d.Sampler(weights) if self.nasty_floats else None
-
- self.pos_clamper = self.neg_clamper = None
- if _sign_aware_lte(0.0, max_value):
- pos_min = max(min_value, smallest_nonzero_magnitude)
- allow_zero = _sign_aware_lte(min_value, 0.0)
- self.pos_clamper = make_float_clamper(
- pos_min, max_value, allow_zero=allow_zero
- )
- if _sign_aware_lte(min_value, -0.0):
- neg_max = min(max_value, -smallest_nonzero_magnitude)
- allow_zero = _sign_aware_lte(-0.0, max_value)
- self.neg_clamper = make_float_clamper(
- -neg_max, -min_value, allow_zero=allow_zero
- )
-
- self.forced_sign_bit: Optional[int] = None
- if (self.pos_clamper is None) != (self.neg_clamper is None):
- self.forced_sign_bit = 1 if self.neg_clamper else 0
-
def __repr__(self):
return "{}(min_value={}, max_value={}, allow_nan={}, smallest_nonzero_magnitude={})".format(
self.__class__.__name__,
@@ -287,39 +185,13 @@ class FloatStrategy(SearchStrategy):
self.smallest_nonzero_magnitude,
)
- def permitted(self, f):
- assert isinstance(f, float)
- if math.isnan(f):
- return self.allow_nan
- if 0 < abs(f) < self.smallest_nonzero_magnitude:
- return False
- return _sign_aware_lte(self.min_value, f) and _sign_aware_lte(f, self.max_value)
-
def do_draw(self, data):
- while True:
- data.start_example(FLOAT_STRATEGY_DO_DRAW_LABEL)
- i = self.sampler.sample(data) if self.sampler else 0
- data.start_example(flt.DRAW_FLOAT_LABEL)
- if i == 0:
- result = flt.draw_float(data, forced_sign_bit=self.forced_sign_bit)
- is_negative = flt.float_to_int(result) >> 63
- if is_negative:
- clamped = -self.neg_clamper(-result)
- else:
- clamped = self.pos_clamper(result)
- if clamped != result:
- data.stop_example(discard=True)
- data.start_example(flt.DRAW_FLOAT_LABEL)
- flt.write_float(data, clamped)
- result = clamped
- else:
- result = self.nasty_floats[i - 1]
-
- flt.write_float(data, result)
-
- data.stop_example() # (DRAW_FLOAT_LABEL)
- data.stop_example() # (FLOAT_STRATEGY_DO_DRAW_LABEL)
- return result
+ return data.draw_float(
+ min_value=self.min_value,
+ max_value=self.max_value,
+ allow_nan=self.allow_nan,
+ smallest_nonzero_magnitude=self.smallest_nonzero_magnitude,
+ )
def filter(self, condition):
# Handle a few specific weird cases.
@@ -331,10 +203,13 @@ class FloatStrategy(SearchStrategy):
smallest_nonzero_magnitude=self.smallest_nonzero_magnitude,
)
if condition is math.isinf:
- permitted_infs = [x for x in (-math.inf, math.inf) if self.permitted(x)]
- if not permitted_infs:
- return nothing()
- return SampledFromStrategy(permitted_infs)
+ if permitted_infs := [
+ x
+ for x in (-math.inf, math.inf)
+ if self.min_value <= x <= self.max_value
+ ]:
+ return SampledFromStrategy(permitted_infs)
+ return nothing()
if condition is math.isnan:
if not self.allow_nan:
return nothing()
@@ -639,7 +514,7 @@ class NanStrategy(SearchStrategy):
def do_draw(self, data):
# Nans must have all exponent bits and the first mantissa bit set, so
# we generate by taking 64 random bits and setting the required ones.
- sign_bit = data.draw_bits(1) << 63
+ sign_bit = int(data.draw_boolean()) << 63
nan_bits = float_to_int(math.nan)
- mantissa_bits = data.draw_bits(52)
+ mantissa_bits = data.draw_integer(0, 2**52 - 1)
return int_to_float(sign_bit | nan_bits | mantissa_bits)
diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/random.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/random.py
index f7afd4d9578..41c9feb5234 100644
--- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/random.py
+++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/random.py
@@ -16,7 +16,6 @@ from typing import Any, Dict
import attr
from hypothesis.control import should_note
-from hypothesis.internal.conjecture import utils as cu
from hypothesis.internal.reflection import define_function_signature
from hypothesis.reporting import report
from hypothesis.strategies._internal.core import (
@@ -233,7 +232,7 @@ class ArtificialRandom(HypothesisRandom):
return self.__convert_result(method, kwargs, result)
if method == "_randbelow":
- result = cu.integer_range(self.__data, 0, kwargs["n"] - 1)
+ result = self.__data.draw_integer(0, kwargs["n"] - 1)
elif method in ("betavariate", "random"):
result = self.__data.draw(UNIFORM)
elif method == "uniform":
@@ -266,18 +265,18 @@ class ArtificialRandom(HypothesisRandom):
if (start - stop) % step == 0:
endpoint -= 1
- i = cu.integer_range(self.__data, 0, endpoint)
+ i = self.__data.draw_integer(0, endpoint)
result = start + i * step
else:
- result = cu.integer_range(self.__data, start, stop - 1)
+ result = self.__data.draw_integer(start, stop - 1)
elif method == "randint":
- result = cu.integer_range(self.__data, kwargs["a"], kwargs["b"])
+ result = self.__data.draw_integer(kwargs["a"], kwargs["b"])
# New in Python 3.12, so not taken by our coverage job
elif method == "binomialvariate": # pragma: no cover
- result = cu.integer_range(self.__data, 0, kwargs["n"])
+ result = self.__data.draw_integer(0, kwargs["n"])
elif method == "choice":
seq = kwargs["seq"]
- result = cu.integer_range(self.__data, 0, len(seq) - 1)
+ result = self.__data.draw_integer(0, len(seq) - 1)
elif method == "choices":
k = kwargs["k"]
result = self.__data.draw(
@@ -309,14 +308,14 @@ class ArtificialRandom(HypothesisRandom):
)
elif method == "getrandbits":
- result = self.__data.draw_bits(kwargs["n"])
+ result = self.__data.draw_integer(0, 2 ** kwargs["n"] - 1)
elif method == "triangular":
low = normalize_zero(kwargs["low"])
high = normalize_zero(kwargs["high"])
mode = normalize_zero(kwargs["mode"])
if mode is None:
result = self.__data.draw(floats(low, high))
- elif self.__data.draw_bits(1):
+ elif self.__data.draw_boolean(0.5):
result = self.__data.draw(floats(mode, high))
else:
result = self.__data.draw(floats(low, mode))
@@ -436,7 +435,7 @@ class RandomStrategy(SearchStrategy):
def do_draw(self, data):
if self.__use_true_random:
- seed = data.draw_bits(64)
+ seed = data.draw_integer(0, 2**64 - 1)
return TrueRandom(seed=seed, note_method_calls=self.__note_method_calls)
else:
return ArtificialRandom(
diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/regex.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/regex.py
index a7f99c4ae56..23c3aedc82a 100644
--- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/regex.py
+++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/regex.py
@@ -58,7 +58,9 @@ BYTES_LOOKUP = {
}
-GROUP_CACHE_STRATEGY = st.shared(st.builds(dict), key="hypothesis.regex.group_cache")
+GROUP_CACHE_STRATEGY: st.SearchStrategy[dict] = st.shared(
+ st.builds(dict), key="hypothesis.regex.group_cache"
+)
@st.composite
diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strategies.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strategies.py
index 415c9092ee6..6b68a1d353d 100644
--- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strategies.py
+++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strategies.py
@@ -544,7 +544,7 @@ class SampledFromStrategy(SearchStrategy):
# Start with ordinary rejection sampling. It's fast if it works, and
# if it doesn't work then it was only a small amount of overhead.
for _ in range(3):
- i = cu.integer_range(data, 0, len(self.elements) - 1)
+ i = data.draw_integer(0, len(self.elements) - 1)
if i not in known_bad_indices:
element = self.get_element(i)
if element is not filter_not_satisfied:
@@ -558,10 +558,6 @@ class SampledFromStrategy(SearchStrategy):
if not max_good_indices:
return filter_not_satisfied
- # Figure out the bit-length of the index that we will write back after
- # choosing an allowed element.
- write_length = len(self.elements).bit_length()
-
# Impose an arbitrary cutoff to prevent us from wasting too much time
# on very large element lists.
cutoff = 10000
@@ -570,7 +566,7 @@ class SampledFromStrategy(SearchStrategy):
# Before building the list of allowed indices, speculatively choose
# one of them. We don't yet know how many allowed indices there will be,
# so this choice might be out-of-bounds, but that's OK.
- speculative_index = cu.integer_range(data, 0, max_good_indices - 1)
+ speculative_index = data.draw_integer(0, max_good_indices - 1)
# Calculate the indices of allowed values, so that we can choose one
# of them at random. But if we encounter the speculatively-chosen one,
@@ -585,14 +581,14 @@ class SampledFromStrategy(SearchStrategy):
if len(allowed) > speculative_index:
# Early-exit case: We reached the speculative index, so
# we just return the corresponding element.
- data.draw_bits(write_length, forced=i)
+ data.draw_integer(0, len(self.elements) - 1, forced=i)
return element
# The speculative index didn't work out, but at this point we've built
# and can choose from the complete list of allowed indices and elements.
if allowed:
i, element = cu.choice(data, allowed)
- data.draw_bits(write_length, forced=i)
+ data.draw_integer(0, len(self.elements) - 1, forced=i)
return element
# If there are no allowed indices, the filter couldn't be satisfied.
return filter_not_satisfied
diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strings.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strings.py
index 22952b6fe32..021ce3c6e6c 100644
--- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strings.py
+++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strings.py
@@ -15,7 +15,6 @@ from functools import lru_cache
from hypothesis.errors import HypothesisWarning, InvalidArgument
from hypothesis.internal import charmap
-from hypothesis.internal.conjecture.utils import biased_coin, integer_range
from hypothesis.internal.intervalsets import IntervalSet
from hypothesis.strategies._internal.collections import ListStrategy
from hypothesis.strategies._internal.lazy import unwrap_strategies
@@ -29,10 +28,6 @@ class OneCharStringStrategy(SearchStrategy):
assert isinstance(intervals, IntervalSet)
self.intervals = intervals
self._force_repr = force_repr
- self.zero_point = self.intervals.index_above(ord("0"))
- self.Z_point = min(
- self.intervals.index_above(ord("Z")), len(self.intervals) - 1
- )
@classmethod
def from_characters_args(
@@ -78,44 +73,20 @@ class OneCharStringStrategy(SearchStrategy):
return self._force_repr or f"OneCharStringStrategy({self.intervals!r})"
def do_draw(self, data):
- if len(self.intervals) > 256:
- if biased_coin(data, 0.2):
- i = integer_range(data, 256, len(self.intervals) - 1)
- else:
- i = integer_range(data, 0, 255)
- else:
- i = integer_range(data, 0, len(self.intervals) - 1)
-
- i = self.rewrite_integer(i)
-
- return chr(self.intervals[i])
-
- def rewrite_integer(self, i):
- # We would like it so that, where possible, shrinking replaces
- # characters with simple ascii characters, so we rejig this
- # bit so that the smallest values are 0, 1, 2, ..., Z.
- #
- # Imagine that numbers are laid out as abc0yyyZ...
- # this rearranges them so that they are laid out as
- # 0yyyZcba..., which gives a better shrinking order.
- if i <= self.Z_point:
- # We want to rewrite the integers [0, n] inclusive
- # to [zero_point, Z_point].
- n = self.Z_point - self.zero_point
- if i <= n:
- i += self.zero_point
- else:
- # We want to rewrite the integers [n + 1, Z_point] to
- # [zero_point, 0] (reversing the order so that codepoints below
- # zero_point shrink upwards).
- i = self.zero_point - (i - n)
- assert i < self.zero_point
- assert 0 <= i <= self.Z_point
- return i
+ return data.draw_string(self.intervals, min_size=1, max_size=1)
class TextStrategy(ListStrategy):
def do_draw(self, data):
+ # if our element strategy is OneCharStringStrategy, we can skip the
+ # ListStrategy draw and jump right to our nice IR string draw.
+ # Doing so for user-provided element strategies is not correct in
+ # general, as they may define a different distribution than our IR.
+ elems = unwrap_strategies(self.element_strategy)
+ if isinstance(elems, OneCharStringStrategy):
+ return data.draw_string(
+ elems.intervals, min_size=self.min_size, max_size=self.max_size
+ )
return "".join(super().do_draw(data))
def __repr__(self):
diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/types.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/types.py
index 0a5fae4d9b4..d9790814150 100644
--- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/types.py
+++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/types.py
@@ -713,7 +713,7 @@ _global_type_lookup.update(
st.binary(),
st.integers(0, 255),
# As with Reversible, we tuplize this for compatibility with Hashable.
- st.lists(st.integers(0, 255)).map(tuple), # type: ignore
+ st.lists(st.integers(0, 255)).map(tuple),
),
typing.BinaryIO: st.builds(io.BytesIO, st.binary()),
typing.TextIO: st.builds(io.StringIO, st.text()),
diff --git a/contrib/python/hypothesis/py3/hypothesis/vendor/tlds-alpha-by-domain.txt b/contrib/python/hypothesis/py3/hypothesis/vendor/tlds-alpha-by-domain.txt
index 931aeb432bd..5402c8d9cb1 100644
--- a/contrib/python/hypothesis/py3/hypothesis/vendor/tlds-alpha-by-domain.txt
+++ b/contrib/python/hypothesis/py3/hypothesis/vendor/tlds-alpha-by-domain.txt
@@ -1,4 +1,4 @@
-# Version 2023111100, Last Updated Sat Nov 11 07:07:01 2023 UTC
+# Version 2023111800, Last Updated Sat Nov 18 07:07:01 2023 UTC
AAA
AARP
ABB
@@ -379,7 +379,6 @@ ES
ESQ
ESTATE
ET
-ETISALAT
EU
EUROVISION
EUS
@@ -1370,7 +1369,6 @@ XN--MGB9AWBF
XN--MGBA3A3EJT
XN--MGBA3A4F16A
XN--MGBA7C0BBN0A
-XN--MGBAAKC7DVF
XN--MGBAAM7A8H
XN--MGBAB2BD
XN--MGBAH1A3HJKRD
diff --git a/contrib/python/hypothesis/py3/hypothesis/version.py b/contrib/python/hypothesis/py3/hypothesis/version.py
index 24995e5689f..158c609209c 100644
--- a/contrib/python/hypothesis/py3/hypothesis/version.py
+++ b/contrib/python/hypothesis/py3/hypothesis/version.py
@@ -8,5 +8,5 @@
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
# obtain one at https://mozilla.org/MPL/2.0/.
-__version_info__ = (6, 89, 0)
+__version_info__ = (6, 90, 0)
__version__ = ".".join(map(str, __version_info__))
diff --git a/contrib/python/hypothesis/py3/ya.make b/contrib/python/hypothesis/py3/ya.make
index 2ff1945862d..b9abaef8bd6 100644
--- a/contrib/python/hypothesis/py3/ya.make
+++ b/contrib/python/hypothesis/py3/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(6.89.0)
+VERSION(6.90.0)
LICENSE(MPL-2.0)