diff options
| author | robot-piglet <[email protected]> | 2025-11-17 15:36:50 +0300 |
|---|---|---|
| committer | robot-piglet <[email protected]> | 2025-11-17 16:16:43 +0300 |
| commit | 587164cd7213f9ff3d1feaad777b7804dc6c342f (patch) | |
| tree | 500e13a84fba80a8cc47767045ffd71d89ee8c63 /contrib/python/hypothesis | |
| parent | be8dd0630b1dee6c76c7c7566cf6903eac0cbb9b (diff) | |
Intermediate changes
commit_hash:31e1caa5b7ddaf9cddf874715cad6425dc610f22
Diffstat (limited to 'contrib/python/hypothesis')
6 files changed, 68 insertions, 18 deletions
diff --git a/contrib/python/hypothesis/py3/.dist-info/METADATA b/contrib/python/hypothesis/py3/.dist-info/METADATA index dd1a0d3f396..fa535de452c 100644 --- a/contrib/python/hypothesis/py3/.dist-info/METADATA +++ b/contrib/python/hypothesis/py3/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: hypothesis -Version: 6.143.0 +Version: 6.144.0 Summary: A library for property-based testing Author-email: "David R. MacIver and Zac Hatfield-Dodds" <[email protected]> License-Expression: MPL-2.0 diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/engine.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/engine.py index 1a20c61220f..a363cdd047a 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/engine.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/engine.py @@ -317,6 +317,7 @@ class ConjectureRunner: # We use call_count because there may be few possible valid_examples. self.first_bug_found_at: int | None = None self.last_bug_found_at: int | None = None + self.first_bug_found_time: float = math.inf self.shrunk_examples: set[InterestingOrigin] = set() self.health_check_state: HealthCheckState | None = None @@ -660,6 +661,7 @@ class ConjectureRunner: self.last_bug_found_at = self.call_count if self.first_bug_found_at is None: self.first_bug_found_at = self.call_count + self.first_bug_found_time = time.monotonic() else: if sort_key(data.nodes) < sort_key(existing.nodes): self.shrinks += 1 @@ -1073,9 +1075,11 @@ class ConjectureRunner: return True # Users who disable shrinking probably want to exit as fast as possible. # If we've found a bug and won't report more than one, stop looking. + # If we first saw a bug more than 10 seconds ago, stop looking. elif ( Phase.shrink not in self.settings.phases or not self.settings.report_multiple_bugs + or time.monotonic() - self.first_bug_found_time > 10 ): return False assert isinstance(self.first_bug_found_at, int) diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/filtering.py b/contrib/python/hypothesis/py3/hypothesis/internal/filtering.py index c92d7be83a6..155e67e74a9 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/filtering.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/filtering.py @@ -37,6 +37,12 @@ from hypothesis.internal.floats import next_down, next_up from hypothesis.internal.lambda_sources import lambda_description from hypothesis.internal.reflection import get_pretty_function_description +try: + # new in 3.14 + from functools import Placeholder # type: ignore +except ImportError: + Placeholder = object() + Ex = TypeVar("Ex") Predicate = Callable[[Ex], bool] @@ -221,10 +227,26 @@ def get_numeric_predicate_bounds(predicate: Predicate) -> ConstructivePredicate: unchanged = ConstructivePredicate.unchanged(predicate) if ( isinstance(predicate, partial) - and len(predicate.args) == 1 and not predicate.keywords + and ( + len(predicate.args) == 1 + or (predicate.args[0] is Placeholder and len(predicate.args) == 2) + ) ): - arg = predicate.args[0] + if len(predicate.args) == 1: + arg = predicate.args[0] + func = predicate.func + else: # pragma: no cover # Python 3.14+ only + assert predicate.args[0] is Placeholder + arg = predicate.args[1] + func = { # reverses the table below; eq is unchanged + operator.lt: operator.gt, + operator.le: operator.ge, + operator.ge: operator.le, + operator.gt: operator.lt, + }.get(predicate.func, predicate.func) + assert func not in (min_len, max_len) # sanity-check; these are private + if ( (isinstance(arg, Decimal) and Decimal.is_snan(arg)) or not isinstance(arg, (int, float, Fraction, Decimal)) @@ -242,8 +264,8 @@ def get_numeric_predicate_bounds(predicate: Predicate) -> ConstructivePredicate: min_len: {"min_value": arg, "len": True}, max_len: {"max_value": arg, "len": True}, } - if predicate.func in options: - return ConstructivePredicate(options[predicate.func], None) + if func in options: + return ConstructivePredicate(options[func], None) # This section is a little complicated, but stepping through with comments should # help to clarify it. We start by finding the source code for our predicate and diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py index 62bd40e13a4..187be5c78f1 100644 --- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py +++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py @@ -1424,11 +1424,13 @@ def _from_type(thing: type[Ex]) -> SearchStrategy[Ex]: # Taken from `Lib/typing.py` and modified: def _get_typeddict_qualifiers(key, annotation_type): qualifiers = [] + annotations = [] while True: annotation_origin = types.extended_get_origin(annotation_type) if annotation_origin is Annotated: if annotation_args := get_args(annotation_type): annotation_type = annotation_args[0] + annotations.extend(annotation_args[1:]) else: break elif annotation_origin in types.RequiredTypes: @@ -1442,6 +1444,8 @@ def _from_type(thing: type[Ex]) -> SearchStrategy[Ex]: annotation_type = _get_annotation_arg(key, annotation_type) else: break + if annotations: + annotation_type = Annotated[(annotation_type, *annotations)] return set(qualifiers), annotation_type # The __optional_keys__ attribute may or may not be present, but if there's no @@ -1693,24 +1697,44 @@ def fractions( def _as_finite_decimal( - value: Real | str | None, name: str, allow_infinity: bool | None + value: Real | str | None, name: str, allow_infinity: bool | None, places: int | None ) -> Decimal | None: """Convert decimal bounds to decimals, carefully.""" assert name in ("min_value", "max_value") if value is None: return None + old = value + if isinstance(value, Fraction): + value = Context(prec=places).divide(value.numerator, value.denominator) + if old != value: + raise InvalidArgument( + f"{old!r} cannot be exactly represented as a decimal with {places=}" + ) if not isinstance(value, Decimal): with localcontext(Context()): # ensure that default traps are enabled value = try_convert(Decimal, value, name) assert isinstance(value, Decimal) + if value.is_nan(): + raise InvalidArgument(f"Invalid {name}={value!r}") + + # If you are reading this conditional, I am so sorry. I did my best. + finitude_old = value if isinstance(old, str) else old + if math.isfinite(finitude_old) != math.isfinite(value) or ( + value.is_finite() and Fraction(str(old)) != Fraction(str(value)) + ): + note_deprecation( + f"{old!r} cannot be exactly represented as a decimal with {places=}", + since="2025-11-02", + has_codemod=False, + stacklevel=1, + ) + if value.is_finite(): return value - if value.is_infinite() and (value < 0 if "min" in name else value > 0): - if allow_infinity or allow_infinity is None: - return None - raise InvalidArgument(f"{allow_infinity=}, but {name}={value!r}") - # This could be infinity, quiet NaN, or signalling NaN - raise InvalidArgument(f"Invalid {name}={value!r}") + assert value.is_infinite() + if (value < 0 if "min" in name else value > 0) and allow_infinity is not False: + return None + raise InvalidArgument(f"{allow_infinity=}, but {name}={value!r}") @cacheable @@ -1747,8 +1771,8 @@ def decimals( check_valid_integer(places, "places") if places is not None and places < 0: raise InvalidArgument(f"{places=} may not be negative") - min_value = _as_finite_decimal(min_value, "min_value", allow_infinity) - max_value = _as_finite_decimal(max_value, "max_value", allow_infinity) + min_value = _as_finite_decimal(min_value, "min_value", allow_infinity, places) + max_value = _as_finite_decimal(max_value, "max_value", allow_infinity, places) check_valid_interval(min_value, max_value, "min_value", "max_value") if allow_infinity and (None not in (min_value, max_value)): raise InvalidArgument("Cannot allow infinity between finite bounds") @@ -1793,12 +1817,12 @@ def decimals( strat = fractions(min_value, max_value).map(fraction_to_decimal) # Compose with sampled_from for infinities and NaNs as appropriate special: list[Decimal] = [] - if allow_nan or (allow_nan is None and (None in (min_value, max_value))): - special.extend(map(Decimal, ("NaN", "-NaN", "sNaN", "-sNaN"))) if allow_infinity or (allow_infinity is None and max_value is None): special.append(Decimal("Infinity")) if allow_infinity or (allow_infinity is None and min_value is None): special.append(Decimal("-Infinity")) + if allow_nan or (allow_nan is None and (None in (min_value, max_value))): + special.extend(map(Decimal, ("NaN", "-NaN", "sNaN", "-sNaN"))) return strat | (sampled_from(special) if special else nothing()) diff --git a/contrib/python/hypothesis/py3/hypothesis/version.py b/contrib/python/hypothesis/py3/hypothesis/version.py index c18a9cfb689..f9daf2ead6b 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, 143, 0) +__version_info__ = (6, 144, 0) __version__ = ".".join(map(str, __version_info__)) diff --git a/contrib/python/hypothesis/py3/ya.make b/contrib/python/hypothesis/py3/ya.make index ed19ada6367..c392b732384 100644 --- a/contrib/python/hypothesis/py3/ya.make +++ b/contrib/python/hypothesis/py3/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(6.143.0) +VERSION(6.144.0) LICENSE(MPL-2.0) |
