summaryrefslogtreecommitdiffstats
path: root/contrib/python/hypothesis
diff options
context:
space:
mode:
authorrobot-piglet <[email protected]>2025-11-19 10:16:42 +0300
committerrobot-piglet <[email protected]>2025-11-19 10:29:21 +0300
commitccd5394bd239df01603ee6fa4adb04ea8389cf85 (patch)
tree1fb10dba55fbd344904aaf26bd22abcbba1d6faa /contrib/python/hypothesis
parent8c1573d7b800bff882ae5a8bb66e8c85dabc1777 (diff)
Intermediate changes
commit_hash:a0c75506f0d1cf5a6805d32237dc5b93c517de28
Diffstat (limited to 'contrib/python/hypothesis')
-rw-r--r--contrib/python/hypothesis/py3/.dist-info/METADATA3
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/core.py6
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/extra/pandas/impl.py15
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/internal/cache.py13
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/internal/conjecture/choice.py23
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py39
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/internal/conjecture/datatree.py35
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/internal/conjecture/dfa/lstar.py15
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/internal/conjecture/engine.py2
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py2
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/internal/escalation.py6
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/internal/observability.py10
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/stateful.py12
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/strategies/_internal/attrs.py3
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py22
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/strategies/_internal/misc.py4
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/strategies/_internal/random.py9
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strategies.py7
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/strategies/_internal/utils.py114
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/vendor/pretty.py2
-rw-r--r--contrib/python/hypothesis/py3/hypothesis/version.py2
-rw-r--r--contrib/python/hypothesis/py3/ya.make3
22 files changed, 181 insertions, 166 deletions
diff --git a/contrib/python/hypothesis/py3/.dist-info/METADATA b/contrib/python/hypothesis/py3/.dist-info/METADATA
index fa535de452c..be7f9497fd0 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.144.0
+Version: 6.145.1
Summary: A library for property-based testing
Author-email: "David R. MacIver and Zac Hatfield-Dodds" <[email protected]>
License-Expression: MPL-2.0
@@ -33,7 +33,6 @@ Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE.txt
-Requires-Dist: attrs>=22.2.0
Requires-Dist: exceptiongroup>=1.0.0; python_version < "3.11"
Requires-Dist: sortedcontainers<3.0.0,>=2.1.0
Provides-Extra: cli
diff --git a/contrib/python/hypothesis/py3/hypothesis/core.py b/contrib/python/hypothesis/py3/hypothesis/core.py
index 9315b89a8a4..3cc6a4e85ed 100644
--- a/contrib/python/hypothesis/py3/hypothesis/core.py
+++ b/contrib/python/hypothesis/py3/hypothesis/core.py
@@ -157,7 +157,7 @@ global_force_seed = None
threadlocal = ThreadLocal(_hypothesis_global_random=lambda: None)
-@dataclass
+@dataclass(slots=True, frozen=False)
class Example:
args: Any
kwargs: Any
@@ -718,7 +718,7 @@ def get_random_for_wrapped_test(test, wrapped_test):
return Random(seed)
-@dataclass
+@dataclass(slots=True, frozen=False)
class Stuff:
selfy: Any
args: tuple
@@ -1622,7 +1622,7 @@ def fake_subTest(self, msg=None, **__):
yield
-@dataclass
+@dataclass(slots=False, frozen=False)
class HypothesisHandle:
"""This object is provided as the .hypothesis attribute on @given tests.
diff --git a/contrib/python/hypothesis/py3/hypothesis/extra/pandas/impl.py b/contrib/python/hypothesis/py3/hypothesis/extra/pandas/impl.py
index 7160566f62e..4d4c00f1277 100644
--- a/contrib/python/hypothesis/py3/hypothesis/extra/pandas/impl.py
+++ b/contrib/python/hypothesis/py3/hypothesis/extra/pandas/impl.py
@@ -11,10 +11,10 @@
from collections import OrderedDict, abc
from collections.abc import Sequence
from copy import copy
+from dataclasses import dataclass
from datetime import datetime, timedelta
from typing import Any, Generic, Union
-import attr
import numpy as np
import pandas
@@ -25,7 +25,6 @@ from hypothesis.errors import InvalidArgument
from hypothesis.extra import numpy as npst
from hypothesis.internal.conjecture import utils as cu
from hypothesis.internal.coverage import check, check_function
-from hypothesis.internal.reflection import get_pretty_function_description
from hypothesis.internal.validation import (
check_type,
check_valid_interval,
@@ -364,7 +363,7 @@ def series(
return result()
[email protected](slots=True)
+@dataclass(slots=True, frozen=False)
class column(Generic[Ex]):
"""Data object for describing a column in a DataFrame.
@@ -382,11 +381,11 @@ class column(Generic[Ex]):
* unique: If all values in this column should be distinct.
"""
- name: str | int | None = attr.ib(default=None)
- elements: st.SearchStrategy[Ex] | None = attr.ib(default=None)
- dtype: Any = attr.ib(default=None, repr=get_pretty_function_description)
- fill: st.SearchStrategy[Ex] | None = attr.ib(default=None)
- unique: bool = attr.ib(default=False)
+ name: str | int | None = None
+ elements: st.SearchStrategy[Ex] | None = None
+ dtype: Any = None
+ fill: st.SearchStrategy[Ex] | None = None
+ unique: bool = False
def columns(
diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/cache.py b/contrib/python/hypothesis/py3/hypothesis/internal/cache.py
index 73b9ed76d2c..899576f228d 100644
--- a/contrib/python/hypothesis/py3/hypothesis/internal/cache.py
+++ b/contrib/python/hypothesis/py3/hypothesis/internal/cache.py
@@ -10,22 +10,21 @@
import threading
from collections import OrderedDict
+from dataclasses import dataclass
from typing import Any, Generic, TypeVar
-import attr
-
from hypothesis.errors import InvalidArgument
K = TypeVar("K")
V = TypeVar("V")
[email protected](slots=True)
+@dataclass(slots=True, frozen=False)
class Entry(Generic[K, V]):
- key: K = attr.ib()
- value: V = attr.ib()
- score: int = attr.ib()
- pins: int = attr.ib(default=0)
+ key: K
+ value: V
+ score: int
+ pins: int = 0
@property
def sort_key(self) -> tuple[int, ...]:
diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/choice.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/choice.py
index ffc08246fa6..b66f7e27537 100644
--- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/choice.py
+++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/choice.py
@@ -10,6 +10,7 @@
import math
from collections.abc import Callable, Hashable, Iterable, Sequence
+from dataclasses import dataclass
from typing import (
Literal,
TypeAlias,
@@ -18,8 +19,6 @@ from typing import (
cast,
)
-import attr
-
from hypothesis.errors import ChoiceTooLarge
from hypothesis.internal.conjecture.floats import float_to_lex, lex_to_float
from hypothesis.internal.conjecture.utils import identity
@@ -72,23 +71,23 @@ ChoiceKeyT: TypeAlias = (
)
[email protected](slots=True)
+@dataclass(slots=True, frozen=False)
class ChoiceTemplate:
- type: Literal["simplest"] = attr.ib()
- count: int | None = attr.ib()
+ type: Literal["simplest"]
+ count: int | None
- def __attrs_post_init__(self) -> None:
+ def __post_init__(self) -> None:
if self.count is not None:
assert self.count > 0
[email protected](slots=True, repr=False, eq=False)
+@dataclass(slots=True, frozen=False)
class ChoiceNode:
- type: ChoiceTypeT = attr.ib()
- value: ChoiceT = attr.ib()
- constraints: ChoiceConstraintsT = attr.ib()
- was_forced: bool = attr.ib()
- index: int | None = attr.ib(default=None)
+ type: ChoiceTypeT
+ value: ChoiceT
+ constraints: ChoiceConstraintsT
+ was_forced: bool
+ index: int | None = None
def copy(
self,
diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py
index 6f9d07591bb..4ac53e9dd92 100644
--- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py
+++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py
@@ -12,6 +12,7 @@ import math
import time
from collections import defaultdict
from collections.abc import Hashable, Iterable, Iterator, Sequence
+from dataclasses import dataclass, field
from enum import IntEnum
from functools import cached_property
from random import Random
@@ -26,8 +27,6 @@ from typing import (
overload,
)
-import attr
-
from hypothesis.errors import (
CannotProceedScopeT,
ChoiceTooLarge,
@@ -120,9 +119,9 @@ class Status(IntEnum):
return f"Status.{self.name}"
[email protected](slots=True, frozen=True)
+@dataclass(slots=True, frozen=True)
class StructuralCoverageTag:
- label: int = attr.ib()
+ label: int
STRUCTURAL_COVERAGE_CACHE: dict[int, StructuralCoverageTag] = {}
@@ -568,27 +567,27 @@ class DataObserver:
pass
[email protected](slots=True)
+@dataclass(slots=True, frozen=True)
class ConjectureResult:
"""Result class storing the parts of ConjectureData that we
will care about after the original ConjectureData has outlived its
usefulness."""
- status: Status = attr.ib()
- interesting_origin: InterestingOrigin | None = attr.ib()
- nodes: tuple[ChoiceNode, ...] = attr.ib(eq=False, repr=False)
- length: int = attr.ib()
- output: str = attr.ib()
- expected_exception: BaseException | None = attr.ib()
- expected_traceback: str | None = attr.ib()
- has_discards: bool = attr.ib()
- target_observations: TargetObservations = attr.ib()
- tags: frozenset[StructuralCoverageTag] = attr.ib()
- spans: Spans = attr.ib(repr=False, eq=False)
- arg_slices: set[tuple[int, int]] = attr.ib(repr=False)
- slice_comments: dict[tuple[int, int], str] = attr.ib(repr=False)
- misaligned_at: MisalignedAt | None = attr.ib(repr=False)
- cannot_proceed_scope: CannotProceedScopeT | None = attr.ib(repr=False)
+ status: Status
+ interesting_origin: InterestingOrigin | None
+ nodes: tuple[ChoiceNode, ...] = field(repr=False, compare=False)
+ length: int
+ output: str
+ expected_exception: BaseException | None
+ expected_traceback: str | None
+ has_discards: bool
+ target_observations: TargetObservations
+ tags: frozenset[StructuralCoverageTag]
+ spans: Spans = field(repr=False, compare=False)
+ arg_slices: set[tuple[int, int]] = field(repr=False)
+ slice_comments: dict[tuple[int, int], str] = field(repr=False)
+ misaligned_at: MisalignedAt | None = field(repr=False)
+ cannot_proceed_scope: CannotProceedScopeT | None = field(repr=False)
def as_result(self) -> "ConjectureResult":
return self
diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/datatree.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/datatree.py
index 54e9b2664db..df02449a917 100644
--- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/datatree.py
+++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/datatree.py
@@ -10,11 +10,10 @@
import math
from collections.abc import Generator, Set
+from dataclasses import dataclass, field
from random import Random
from typing import TYPE_CHECKING, Final, TypeAlias, cast
-import attr
-
from hypothesis.errors import (
FlakyReplay,
FlakyStrategyDefinition,
@@ -64,14 +63,14 @@ _FLAKY_STRAT_MSG = (
EMPTY: frozenset[int] = frozenset()
[email protected](slots=True)
+@dataclass(slots=True, frozen=True)
class Killed:
"""Represents a transition to part of the tree which has been marked as
"killed", meaning we want to treat it as not worth exploring, so it will
be treated as if it were completely explored for the purposes of
exhaustion."""
- next_node: "TreeNode" = attr.ib()
+ next_node: "TreeNode"
def _repr_pretty_(self, p: "RepresentationPrinter", cycle: bool) -> None:
assert cycle is False
@@ -89,14 +88,14 @@ def _node_pretty(
return f"{choice_type} {value!r}{forced_marker} {constraints}"
[email protected](slots=True)
+@dataclass(slots=True, frozen=False)
class Branch:
"""Represents a transition where multiple choices can be made as to what
to drawn."""
- constraints: ChoiceConstraintsT = attr.ib()
- choice_type: ChoiceTypeT = attr.ib()
- children: dict[ChoiceT, "TreeNode"] = attr.ib(repr=False)
+ constraints: ChoiceConstraintsT
+ choice_type: ChoiceTypeT
+ children: dict[ChoiceT, "TreeNode"] = field(repr=False)
@property
def max_children(self) -> int:
@@ -117,12 +116,12 @@ class Branch:
p.pretty(child)
[email protected](slots=True, frozen=True)
+@dataclass(slots=True, frozen=True)
class Conclusion:
"""Represents a transition to a finished state."""
- status: Status = attr.ib()
- interesting_origin: InterestingOrigin | None = attr.ib()
+ status: Status
+ interesting_origin: InterestingOrigin | None
def _repr_pretty_(self, p: "RepresentationPrinter", cycle: bool) -> None:
assert cycle is False
@@ -334,7 +333,7 @@ def all_children(
yield from _floats_between(min_point, max_value)
[email protected](slots=True)
+@dataclass(slots=True, frozen=False)
class TreeNode:
"""
A node, or collection of directly descended nodes, in a DataTree.
@@ -400,15 +399,15 @@ class TreeNode:
# The constraints, value, and choice_types of the nodes stored here. These always
# have the same length. The values at index i belong to node i.
- constraints: list[ChoiceConstraintsT] = attr.ib(factory=list)
- values: list[ChoiceT] = attr.ib(factory=list)
- choice_types: list[ChoiceTypeT] = attr.ib(factory=list)
+ constraints: list[ChoiceConstraintsT] = field(default_factory=list)
+ values: list[ChoiceT] = field(default_factory=list)
+ choice_types: list[ChoiceTypeT] = field(default_factory=list)
# The indices of nodes which had forced values.
#
# Stored as None if no indices have been forced, purely for space saving
# reasons (we force quite rarely).
- __forced: set[int] | None = attr.ib(default=None, init=False)
+ __forced: set[int] | None = field(default=None, init=False)
# What happens next after drawing these nodes. (conceptually, "what is the
# child/children of the last node stored here").
@@ -419,14 +418,14 @@ class TreeNode:
# - Conclusion (ConjectureData.conclude_test was called here)
# - Killed (this branch is valid and may even have children, but should not
# be explored when generating novel prefixes)
- transition: None | Branch | Conclusion | Killed = attr.ib(default=None)
+ transition: None | Branch | Conclusion | Killed = None
# A tree node is exhausted if every possible sequence of draws below it has
# been explored. We only update this when performing operations that could
# change the answer.
#
# See also TreeNode.check_exhausted.
- is_exhausted: bool = attr.ib(default=False, init=False)
+ is_exhausted: bool = field(default=False, init=False)
@property
def forced(self) -> Set[int]:
diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/dfa/lstar.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/dfa/lstar.py
index 061cb038253..25e638637b2 100644
--- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/dfa/lstar.py
+++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/dfa/lstar.py
@@ -10,8 +10,7 @@
from bisect import bisect_right, insort
from collections import Counter
-
-import attr
+from dataclasses import dataclass, field
from hypothesis.errors import InvalidState
from hypothesis.internal.conjecture.dfa import DFA, cached
@@ -81,31 +80,31 @@ learning languages offline that we can record for later use.
"""
[email protected](slots=True)
+@dataclass(slots=True, frozen=False)
class DistinguishedState:
"""Relevant information for a state that we have witnessed as definitely
distinct from ones we have previously seen so far."""
# Index of this state in the learner's list of states
- index: int = attr.ib()
+ index: int
# A string that witnesses this state (i.e. when starting from the origin
# and following this string you will end up in this state).
- label: str = attr.ib()
+ label: str
# A boolean as to whether this is an accepting state.
- accepting: bool = attr.ib()
+ accepting: bool
# A list of experiments that it is necessary to run to determine whether
# a string is in this state. This is stored as a dict mapping experiments
# to their expected result. A string is only considered to lead to this
# state if ``all(learner.member(s + experiment) == result for experiment,
# result in self.experiments.items())``.
- experiments: dict = attr.ib()
+ experiments: dict
# A cache of transitions out of this state, mapping bytes to the states
# that they lead to.
- transitions: dict = attr.ib(factory=dict)
+ transitions: dict = field(default_factory=dict)
class LStar:
diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/engine.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/engine.py
index a363cdd047a..7af61ac6045 100644
--- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/engine.py
+++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/engine.py
@@ -113,7 +113,7 @@ def shortlex(s):
return (len(s), s)
-@dataclass
+@dataclass(slots=True, frozen=False)
class HealthCheckState:
valid_examples: int = field(default=0)
invalid_examples: int = field(default=0)
diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py
index 9aad89c6e44..204b794345c 100644
--- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py
+++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py
@@ -89,7 +89,7 @@ def sort_key(nodes: Sequence[ChoiceNode]) -> tuple[int, tuple[int, ...]]:
)
-@dataclass
+@dataclass(slots=True, frozen=False)
class ShrinkPass:
function: Any
name: str | None = None
diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/escalation.py b/contrib/python/hypothesis/py3/hypothesis/internal/escalation.py
index c29c846b770..031a99004c0 100644
--- a/contrib/python/hypothesis/py3/hypothesis/internal/escalation.py
+++ b/contrib/python/hypothesis/py3/hypothesis/internal/escalation.py
@@ -16,7 +16,7 @@ import traceback
from collections.abc import Callable
from dataclasses import dataclass
from functools import partial
-from inspect import getframeinfo
+from inspect import getfile, getsourcefile
from pathlib import Path
from types import ModuleType, TracebackType
@@ -82,7 +82,7 @@ def get_trimmed_traceback(
return tb
while tb.tb_next is not None and (
# If the frame is from one of our files, it's been added by Hypothesis.
- is_hypothesis_file(getframeinfo(tb.tb_frame).filename)
+ is_hypothesis_file(getsourcefile(tb.tb_frame) or getfile(tb.tb_frame))
# But our `@proxies` decorator overrides the source location,
# so we check for an attribute it injects into the frame too.
or tb.tb_frame.f_globals.get("__hypothesistracebackhide__") is True
@@ -91,7 +91,7 @@ def get_trimmed_traceback(
return tb
-@dataclass(frozen=True)
+@dataclass(slots=True, frozen=True)
class InterestingOrigin:
# The `interesting_origin` is how Hypothesis distinguishes between multiple
# failures, for reporting and also to replay from the example database (even
diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/observability.py b/contrib/python/hypothesis/py3/hypothesis/internal/observability.py
index 0310967132a..84d5b51bf49 100644
--- a/contrib/python/hypothesis/py3/hypothesis/internal/observability.py
+++ b/contrib/python/hypothesis/py3/hypothesis/internal/observability.py
@@ -68,7 +68,7 @@ _callbacks: dict[int | None, list[CallbackThreadT]] = {}
_callbacks_all_threads: list[CallbackAllThreadsT] = []
-@dataclass
+@dataclass(slots=True, frozen=False)
class PredicateCounts:
satisfied: int = 0
unsatisfied: int = 0
@@ -167,7 +167,7 @@ def nodes_to_json(nodes: tuple[ChoiceNode, ...]) -> list[dict[str, Any]]:
]
-@dataclass
+@dataclass(slots=True, frozen=True)
class ObservationMetadata:
traceback: str | None
reproduction_decorator: str | None
@@ -221,7 +221,7 @@ class ObservationMetadata:
return data
-@dataclass
+@dataclass(slots=True, frozen=True)
class BaseObservation:
type: Literal["test_case", "info", "alert", "error"]
property: str
@@ -232,14 +232,14 @@ InfoObservationType = Literal["info", "alert", "error"]
TestCaseStatus = Literal["gave_up", "passed", "failed"]
-@dataclass
+@dataclass(slots=True, frozen=True)
class InfoObservation(BaseObservation):
type: InfoObservationType
title: str
content: str | dict
-@dataclass
+@dataclass(slots=True, frozen=True)
class TestCaseObservation(BaseObservation):
__test__ = False # no! bad pytest!
diff --git a/contrib/python/hypothesis/py3/hypothesis/stateful.py b/contrib/python/hypothesis/py3/hypothesis/stateful.py
index 7c2ef1ccb40..22596158397 100644
--- a/contrib/python/hypothesis/py3/hypothesis/stateful.py
+++ b/contrib/python/hypothesis/py3/hypothesis/stateful.py
@@ -269,7 +269,7 @@ class StateMachineMeta(type):
return super().__setattr__(name, value)
-@dataclass
+@dataclass(slots=True, frozen=True)
class _SetupState:
rules: list["Rule"]
invariants: list["Invariant"]
@@ -492,7 +492,7 @@ class RuleBasedStateMachine(metaclass=StateMachineMeta):
return StateMachineTestCase
-@dataclass
+@dataclass(slots=True, frozen=False)
class Rule:
targets: Any
function: Any
@@ -501,9 +501,9 @@ class Rule:
bundles: tuple["Bundle", ...] = field(init=False)
_cached_hash: int | None = field(init=False, default=None)
_cached_repr: str | None = field(init=False, default=None)
+ arguments_strategies: dict[Any, Any] = field(init=False, default_factory=dict)
def __post_init__(self):
- self.arguments_strategies = {}
bundles = []
for k, v in sorted(self.arguments.items()):
assert not isinstance(v, BundleReferenceStrategy)
@@ -658,7 +658,7 @@ def consumes(bundle: Bundle[Ex]) -> SearchStrategy[Ex]:
return BundleConsumer(bundle)
-@dataclass
+@dataclass(slots=True, frozen=True)
class MultipleResults(Iterable[Ex]):
values: tuple[Ex, ...]
@@ -941,7 +941,7 @@ def initialize(
return accept
-@dataclass
+@dataclass(slots=True, frozen=True)
class VarReference:
name: str
@@ -1010,7 +1010,7 @@ def precondition(precond: Callable[[Any], bool]) -> Callable[[TestFunc], TestFun
return decorator
-@dataclass
+@dataclass(slots=True, frozen=True)
class Invariant:
function: Any
preconditions: Any
diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/attrs.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/attrs.py
index 555b7ca09b6..4e057df545c 100644
--- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/attrs.py
+++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/attrs.py
@@ -8,6 +8,9 @@
# 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/.
+# Since Hypothesis doesn't have a hard dependency on attrs, be careful to only import
+# this file when attrs is in sys.modules.
+
from collections.abc import Collection, Generator, Iterable, Sequence
from functools import reduce
from itertools import chain
diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py
index 187be5c78f1..bd3be431a29 100644
--- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py
+++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py
@@ -45,8 +45,6 @@ from typing import (
)
from uuid import UUID
-import attr
-
from hypothesis._settings import note_deprecation
from hypothesis.control import (
cleanup,
@@ -172,7 +170,7 @@ def sampled_from(
...
-@defines_strategy(try_non_lazy=True)
+@defines_strategy(eager="try")
def sampled_from(
elements: type[enum.Enum] | Sequence[Any],
) -> SearchStrategy[Any]:
@@ -1154,7 +1152,11 @@ def builds(
required = required_args(target, args, kwargs)
to_infer = {k for k, v in kwargs.items() if v is ...}
if required or to_infer:
- if isinstance(target, type) and attr.has(target):
+ if (
+ isinstance(target, type)
+ and (attr := sys.modules.get("attr")) is not None
+ and attr.has(target)
+ ): # pragma: no cover # covered by our attrs tests in check-niche
# Use our custom introspection for attrs classes
from hypothesis.strategies._internal.attrs import from_attrs
@@ -1188,7 +1190,7 @@ def builds(
@cacheable
-@defines_strategy(never_lazy=True)
+@defines_strategy(eager=True)
def from_type(thing: type[T]) -> SearchStrategy[T]:
"""Looks up the appropriate search strategy for the given type.
@@ -1536,7 +1538,7 @@ def _from_type(thing: type[Ex]) -> SearchStrategy[Ex]:
required = required_args(thing)
if required and not (
required.issubset(get_type_hints(thing))
- or attr.has(thing)
+ or ((attr := sys.modules.get("attr")) is not None and attr.has(thing))
or is_typed_named_tuple(thing) # weird enough that we have a specific check
):
raise ResolutionFailed(
@@ -1826,7 +1828,7 @@ def decimals(
return strat | (sampled_from(special) if special else nothing())
-@defines_strategy(never_lazy=True)
+@defines_strategy(eager=True)
def recursive(
base: SearchStrategy[Ex],
extend: Callable[[SearchStrategy[Any]], SearchStrategy[T]],
@@ -2171,7 +2173,7 @@ def complex_numbers(
return constrained_complex()
-@defines_strategy(never_lazy=True)
+@defines_strategy(eager=True)
def shared(
base: SearchStrategy[Ex],
*,
@@ -2338,7 +2340,7 @@ class DataStrategy(SearchStrategy):
@cacheable
-@defines_strategy(never_lazy=True)
+@defines_strategy(eager=True)
def data() -> SearchStrategy[DataObject]:
"""
Provides an object ``data`` with a ``data.draw`` function which acts like
@@ -2486,7 +2488,7 @@ def register_type_strategy(
@cacheable
-@defines_strategy(never_lazy=True)
+@defines_strategy(eager=True)
def deferred(definition: Callable[[], SearchStrategy[Ex]]) -> SearchStrategy[Ex]:
"""A deferred strategy allows you to write a strategy that references other
strategies that have not yet been defined. This allows for the easy
diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/misc.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/misc.py
index 6e453040e42..cbcfa324455 100644
--- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/misc.py
+++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/misc.py
@@ -64,7 +64,7 @@ class JustStrategy(SampledFromStrategy[Ex]):
return self._transform(self.value)
-@defines_strategy(never_lazy=True)
+@defines_strategy(eager=True)
def just(value: T) -> SearchStrategy[T]:
"""Return a strategy which only generates ``value``.
@@ -120,7 +120,7 @@ NOTHING = Nothing()
@cacheable
-@defines_strategy(never_lazy=True)
+@defines_strategy(eager=True)
def nothing() -> SearchStrategy["Never"]:
"""This strategy never successfully draws a value and will always reject on
an attempt to draw.
diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/random.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/random.py
index 8ff1d077877..523991c6e72 100644
--- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/random.py
+++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/random.py
@@ -11,11 +11,10 @@
import abc
import inspect
import math
+from dataclasses import dataclass, field
from random import Random
from typing import Any
-import attr
-
from hypothesis.control import should_note
from hypothesis.internal.conjecture.data import ConjectureData
from hypothesis.internal.reflection import define_function_signature
@@ -147,10 +146,10 @@ for r in RANDOM_METHODS:
define_copy_method(r)
[email protected](slots=True)
+@dataclass(slots=True, frozen=False)
class RandomState:
- next_states: dict = attr.ib(factory=dict)
- state_id: Any = attr.ib(default=None)
+ next_states: dict = field(default_factory=dict)
+ state_id: Any = None
def state_for_seed(data, seed):
diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strategies.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strategies.py
index e5e03145d61..0fd21ea108a 100644
--- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strategies.py
+++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strategies.py
@@ -271,9 +271,12 @@ class SearchStrategy(Generic[Ex]):
def has_reusable_values(self) -> Any:
return recursive_property(self, "has_reusable_values", True)
- # Whether this strategy is suitable for holding onto in a cache.
@property
def is_cacheable(self) -> Any:
+ """
+ Whether it is safe to hold on to instances of this strategy in a cache.
+ See _STRATEGY_CACHE.
+ """
return recursive_property(self, "is_cacheable", True)
def calc_is_cacheable(self, recur: RecurT) -> bool:
@@ -918,7 +921,7 @@ def one_of(*args: SearchStrategy[Any]) -> SearchStrategy[Any]: # pragma: no cov
...
-@defines_strategy(never_lazy=True)
+@defines_strategy(eager=True)
def one_of(
*args: Sequence[SearchStrategy[Any]] | SearchStrategy[Any],
) -> SearchStrategy[Any]:
diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/utils.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/utils.py
index 6c5ec635c6a..c9656436c58 100644
--- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/utils.py
+++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/utils.py
@@ -12,11 +12,10 @@ import dataclasses
import sys
from collections.abc import Callable
from functools import partial
-from typing import TypeAlias, TypeVar
+from typing import Literal, TypeAlias, TypeVar
from weakref import WeakValueDictionary
-import attr
-
+from hypothesis.errors import InvalidArgument
from hypothesis.internal.cache import LRUReusedCache
from hypothesis.internal.floats import clamp, float_to_int
from hypothesis.internal.reflection import proxies
@@ -34,13 +33,13 @@ _all_strategies: WeakValueDictionary[str, Callable] = WeakValueDictionary()
_STRATEGY_CACHE = LRUReusedCache[StrategyCacheKey, object](1024)
-def convert_value(v: object) -> ValueKey:
- if isinstance(v, float):
- return (float, float_to_int(v))
- return (type(v), v)
+def _value_key(value: object) -> ValueKey:
+ if isinstance(value, float):
+ return (float, float_to_int(value))
+ return (type(value), value)
-def clear_cache() -> None:
+def clear_strategy_cache() -> None:
_STRATEGY_CACHE.clear()
@@ -55,64 +54,85 @@ def cacheable(fn: T) -> T:
return fn(*args, **kwargs)
try:
- kwargs_cache_key = {(k, convert_value(v)) for k, v in kwargs.items()}
+ kwargs_cache_key = {(k, _value_key(v)) for k, v in kwargs.items()}
except TypeError:
return fn(*args, **kwargs)
- cache_key = (fn, tuple(map(convert_value, args)), frozenset(kwargs_cache_key))
+
+ cache_key = (
+ fn,
+ tuple(_value_key(v) for v in args),
+ frozenset(kwargs_cache_key),
+ )
try:
- if cache_key in _STRATEGY_CACHE:
- return _STRATEGY_CACHE[cache_key]
+ return _STRATEGY_CACHE[cache_key]
+ except KeyError:
+ pass
except TypeError:
return fn(*args, **kwargs)
- else:
- result = fn(*args, **kwargs)
- if not isinstance(result, SearchStrategy) or result.is_cacheable:
- _STRATEGY_CACHE[cache_key] = result
- return result
- cached_strategy.__clear_cache = clear_cache # type: ignore
+ result = fn(*args, **kwargs)
+ if not isinstance(result, SearchStrategy) or result.is_cacheable:
+ _STRATEGY_CACHE[cache_key] = result
+ return result
+
+ # note that calling this clears the full _STRATEGY_CACHE for all strategies,
+ # not just the cache for this strategy.
+ cached_strategy.__clear_cache = clear_strategy_cache # type: ignore
return cached_strategy
def defines_strategy(
*,
force_reusable_values: bool = False,
- try_non_lazy: bool = False,
- never_lazy: bool = False,
+ eager: bool | Literal["try"] = False,
) -> Callable[[T], T]:
- """Returns a decorator for strategy functions.
+ """
+ Each standard strategy function provided to users by Hypothesis should be
+ decorated with @defines_strategy. This registers the strategy with _all_strategies,
+ which is used in our own test suite to check that e.g. we document all strategies
+ in sphinx.
- If ``force_reusable_values`` is True, the returned strategy will be marked
- with ``.has_reusable_values == True`` even if it uses maps/filters or
- non-reusable strategies internally. This tells our numpy/pandas strategies
- that they can implicitly use such strategies as background values.
+ If you're reading this and are the author of a third-party strategy library:
+ don't worry, third-party strategies don't need to be decorated with
+ @defines_strategy. This function is internal to Hypothesis and not intended
+ for outside use.
- If ``try_non_lazy`` is True, attempt to execute the strategy definition
- function immediately, so that a LazyStrategy is only returned if this
- raises an exception.
+ Parameters
+ ----------
+ force_reusable_values : bool
+ If ``True``, strategies returned from the strategy function will have
+ ``.has_reusable_values == True`` set, even if it uses maps/filters or
+ non-reusable strategies internally. This tells our numpy/pandas strategies
+ that they can implicitly use such strategies as background values.
+ eager : bool | "try"
+ If ``True``, strategies returned by the strategy function are returned
+ as-is, and not wrapped in LazyStrategy.
- If ``never_lazy`` is True, the decorator performs no lazy-wrapping at all,
- and instead returns the original function.
+ If "try", we first attempt to call the strategy function and return the
+ resulting strategy. If this throws an exception, we treat it the same as
+ ``eager = False``, by returning the strategy function wrapped in a
+ LazyStrategy.
"""
+ if eager is not False and force_reusable_values: # pragma: no cover
+ # We could support eager + force_reusable_values with a suitable wrapper,
+ # but there are currently no callers that request this combination.
+ raise InvalidArgument(
+ f"Passing both eager={eager} and force_reusable_values=True is "
+ "currently not supported"
+ )
+
def decorator(strategy_definition):
- """A decorator that registers the function as a strategy and makes it
- lazily evaluated."""
_all_strategies[strategy_definition.__name__] = strategy_definition
- if never_lazy:
- assert not try_non_lazy
- # We could potentially support never_lazy + force_reusable_values
- # with a suitable wrapper, but currently there are no callers that
- # request this combination.
- assert not force_reusable_values
+ if eager is True:
return strategy_definition
- from hypothesis.strategies._internal.lazy import LazyStrategy
-
@proxies(strategy_definition)
def accept(*args, **kwargs):
- if try_non_lazy:
+ from hypothesis.strategies._internal.lazy import LazyStrategy
+
+ if eager == "try":
# Why not try this unconditionally? Because we'd end up with very
# deep nesting of recursive strategies - better to be lazy unless we
# *know* that eager evaluation is the right choice.
@@ -178,19 +198,15 @@ def _to_jsonable(obj: object, *, avoid_realization: bool, seen: set[int]) -> obj
pass
# Special handling for dataclasses, attrs, and pydantic classes
- if (
- (dcs := sys.modules.get("dataclasses"))
- and dcs.is_dataclass(obj)
- and not isinstance(obj, type)
- ):
+ if dataclasses.is_dataclass(obj) and not isinstance(obj, type):
# Avoid dataclasses.asdict here to ensure that inner to_json overrides
# can get called as well
return {
field.name: recur(getattr(obj, field.name))
- for field in dataclasses.fields(obj) # type: ignore
+ for field in dataclasses.fields(obj)
}
- if attr.has(type(obj)):
- return recur(attr.asdict(obj, recurse=False)) # type: ignore
+ if (attr := sys.modules.get("attr")) is not None and attr.has(type(obj)):
+ return recur(attr.asdict(obj, recurse=False))
if (pyd := sys.modules.get("pydantic")) and isinstance(obj, pyd.BaseModel):
return recur(obj.model_dump())
diff --git a/contrib/python/hypothesis/py3/hypothesis/vendor/pretty.py b/contrib/python/hypothesis/py3/hypothesis/vendor/pretty.py
index 83fab2406d7..af790bbab2c 100644
--- a/contrib/python/hypothesis/py3/hypothesis/vendor/pretty.py
+++ b/contrib/python/hypothesis/py3/hypothesis/vendor/pretty.py
@@ -232,7 +232,7 @@ class RepresentationPrinter:
self.type_pprinters[cls] = printer
return printer(obj, self, cycle)
else:
- if hasattr(cls, "__attrs_attrs__"):
+ if hasattr(cls, "__attrs_attrs__"): # pragma: no cover
return pprint_fields(
obj,
self,
diff --git a/contrib/python/hypothesis/py3/hypothesis/version.py b/contrib/python/hypothesis/py3/hypothesis/version.py
index f9daf2ead6b..cee78be632c 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, 144, 0)
+__version_info__ = (6, 145, 1)
__version__ = ".".join(map(str, __version_info__))
diff --git a/contrib/python/hypothesis/py3/ya.make b/contrib/python/hypothesis/py3/ya.make
index c392b732384..e0998729d12 100644
--- a/contrib/python/hypothesis/py3/ya.make
+++ b/contrib/python/hypothesis/py3/ya.make
@@ -2,12 +2,11 @@
PY3_LIBRARY()
-VERSION(6.144.0)
+VERSION(6.145.1)
LICENSE(MPL-2.0)
PEERDIR(
- contrib/python/attrs
contrib/python/sortedcontainers
)