diff options
| author | robot-piglet <[email protected]> | 2025-08-05 16:28:24 +0300 |
|---|---|---|
| committer | robot-piglet <[email protected]> | 2025-08-05 16:37:35 +0300 |
| commit | cd649dea60f5d85081c37347c3369f689a93f07e (patch) | |
| tree | ac74b7ff57cdc4e88d84ccf8551282f2e88e4ae4 /contrib/python/hypothesis | |
| parent | f4087baec8b81e624de9234395f50762eff62663 (diff) | |
Intermediate changes
commit_hash:0b06a7e4b4ea41e9b9e62d6109a06e5aef2a19cb
Diffstat (limited to 'contrib/python/hypothesis')
49 files changed, 543 insertions, 625 deletions
diff --git a/contrib/python/hypothesis/py3/.dist-info/METADATA b/contrib/python/hypothesis/py3/.dist-info/METADATA index 7916731906c..f4d09309a39 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.112.5 +Version: 6.115.1 Summary: A library for property-based testing Home-page: https://hypothesis.works Author: David R. MacIver and Zac Hatfield-Dodds @@ -22,7 +22,6 @@ Classifier: Operating System :: Microsoft :: Windows Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 @@ -32,7 +31,7 @@ Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Education :: Testing Classifier: Topic :: Software Development :: Testing Classifier: Typing :: Typed -Requires-Python: >=3.8 +Requires-Python: >=3.9 Description-Content-Type: text/x-rst License-File: LICENSE.txt Requires-Dist: attrs>=22.2.0 @@ -41,20 +40,19 @@ Requires-Dist: exceptiongroup>=1.0.0; python_version < "3.11" Provides-Extra: all Requires-Dist: black>=19.10b0; extra == "all" Requires-Dist: click>=7.0; extra == "all" -Requires-Dist: crosshair-tool>=0.0.73; extra == "all" -Requires-Dist: django>=3.2; extra == "all" +Requires-Dist: crosshair-tool>=0.0.74; extra == "all" +Requires-Dist: django>=4.2; extra == "all" Requires-Dist: dpcontracts>=0.4; extra == "all" -Requires-Dist: hypothesis-crosshair>=0.0.14; extra == "all" +Requires-Dist: hypothesis-crosshair>=0.0.16; extra == "all" Requires-Dist: lark>=0.10.1; extra == "all" Requires-Dist: libcst>=0.3.16; extra == "all" -Requires-Dist: numpy>=1.17.3; extra == "all" +Requires-Dist: numpy>=1.19.3; extra == "all" Requires-Dist: pandas>=1.1; extra == "all" Requires-Dist: pytest>=4.6; extra == "all" Requires-Dist: python-dateutil>=1.4; extra == "all" Requires-Dist: pytz>=2014.1; extra == "all" Requires-Dist: redis>=3.0.0; extra == "all" Requires-Dist: rich>=9.0.0; extra == "all" -Requires-Dist: backports.zoneinfo>=0.2.1; python_version < "3.9" and extra == "all" Requires-Dist: tzdata>=2024.2; (sys_platform == "win32" or sys_platform == "emscripten") and extra == "all" Provides-Extra: cli Requires-Dist: click>=7.0; extra == "cli" @@ -63,12 +61,12 @@ Requires-Dist: rich>=9.0.0; extra == "cli" Provides-Extra: codemods Requires-Dist: libcst>=0.3.16; extra == "codemods" Provides-Extra: crosshair -Requires-Dist: hypothesis-crosshair>=0.0.14; extra == "crosshair" -Requires-Dist: crosshair-tool>=0.0.73; extra == "crosshair" +Requires-Dist: hypothesis-crosshair>=0.0.16; extra == "crosshair" +Requires-Dist: crosshair-tool>=0.0.74; extra == "crosshair" Provides-Extra: dateutil Requires-Dist: python-dateutil>=1.4; extra == "dateutil" Provides-Extra: django -Requires-Dist: django>=3.2; extra == "django" +Requires-Dist: django>=4.2; extra == "django" Provides-Extra: dpcontracts Requires-Dist: dpcontracts>=0.4; extra == "dpcontracts" Provides-Extra: ghostwriter @@ -76,7 +74,7 @@ Requires-Dist: black>=19.10b0; extra == "ghostwriter" Provides-Extra: lark Requires-Dist: lark>=0.10.1; extra == "lark" Provides-Extra: numpy -Requires-Dist: numpy>=1.17.3; extra == "numpy" +Requires-Dist: numpy>=1.19.3; extra == "numpy" Provides-Extra: pandas Requires-Dist: pandas>=1.1; extra == "pandas" Provides-Extra: pytest @@ -86,7 +84,6 @@ Requires-Dist: pytz>=2014.1; extra == "pytz" Provides-Extra: redis Requires-Dist: redis>=3.0.0; extra == "redis" Provides-Extra: zoneinfo -Requires-Dist: backports.zoneinfo>=0.2.1; python_version < "3.9" and extra == "zoneinfo" Requires-Dist: tzdata>=2024.2; (sys_platform == "win32" or sys_platform == "emscripten") and extra == "zoneinfo" ========== diff --git a/contrib/python/hypothesis/py3/_hypothesis_ftz_detector.py b/contrib/python/hypothesis/py3/_hypothesis_ftz_detector.py index 2c73530c9ab..2ee203429b1 100644 --- a/contrib/python/hypothesis/py3/_hypothesis_ftz_detector.py +++ b/contrib/python/hypothesis/py3/_hypothesis_ftz_detector.py @@ -18,13 +18,13 @@ import of Hypothesis itself from each subprocess which must import the worker fu import importlib import sys -from typing import TYPE_CHECKING, Callable, Optional, Set, Tuple +from typing import TYPE_CHECKING, Callable, Optional if TYPE_CHECKING: from multiprocessing import Queue from typing import TypeAlias -FTZCulprits: "TypeAlias" = Tuple[Optional[bool], Set[str]] +FTZCulprits: "TypeAlias" = tuple[Optional[bool], set[str]] KNOWN_EVER_CULPRITS = ( @@ -104,7 +104,7 @@ def identify_ftz_culprits() -> str: # that importing them in a new process sets the FTZ state. As a heuristic, we'll # start with packages known to have ever enabled FTZ, then top-level packages as # a way to eliminate large fractions of the search space relatively quickly. - def key(name: str) -> Tuple[bool, int, str]: + def key(name: str) -> tuple[bool, int, str]: """Prefer known-FTZ modules, then top-level packages, then alphabetical.""" return (name not in KNOWN_EVER_CULPRITS, name.count("."), name) diff --git a/contrib/python/hypothesis/py3/hypothesis/_settings.py b/contrib/python/hypothesis/py3/hypothesis/_settings.py index d337be4f579..2f18ff920fe 100644 --- a/contrib/python/hypothesis/py3/hypothesis/_settings.py +++ b/contrib/python/hypothesis/py3/hypothesis/_settings.py @@ -19,18 +19,9 @@ import datetime import inspect import os import warnings +from collections.abc import Collection from enum import Enum, EnumMeta, IntEnum, unique -from typing import ( - TYPE_CHECKING, - Any, - ClassVar, - Collection, - Dict, - List, - Optional, - TypeVar, - Union, -) +from typing import TYPE_CHECKING, Any, ClassVar, Optional, TypeVar, Union import attr @@ -49,7 +40,7 @@ if TYPE_CHECKING: __all__ = ["settings"] -all_settings: Dict[str, "Setting"] = {} +all_settings: dict[str, "Setting"] = {} T = TypeVar("T") @@ -138,7 +129,7 @@ class settings(metaclass=settingsMeta): """ __definitions_are_locked = False - _profiles: ClassVar[Dict[str, "settings"]] = {} + _profiles: ClassVar[dict[str, "settings"]] = {} __module__ = "hypothesis" def __getattr__(self, name): @@ -479,7 +470,7 @@ class HealthCheck(Enum, metaclass=HealthCheckMeta): return f"{self.__class__.__name__}.{self.name}" @classmethod - def all(cls) -> List["HealthCheck"]: + def all(cls) -> list["HealthCheck"]: # Skipping of deprecated attributes is handled in HealthCheckMeta.__iter__ note_deprecation( "`HealthCheck.all()` is deprecated; use `list(HealthCheck)` instead.", diff --git a/contrib/python/hypothesis/py3/hypothesis/core.py b/contrib/python/hypothesis/py3/hypothesis/core.py index 304ba614d08..9a1a19c1e29 100644 --- a/contrib/python/hypothesis/py3/hypothesis/core.py +++ b/contrib/python/hypothesis/py3/hypothesis/core.py @@ -24,6 +24,7 @@ import unittest import warnings import zlib from collections import defaultdict +from collections.abc import Coroutine, Hashable from functools import partial from random import Random from typing import ( @@ -31,12 +32,7 @@ from typing import ( Any, BinaryIO, Callable, - Coroutine, - Hashable, - List, Optional, - Tuple, - Type, TypeVar, Union, overload, @@ -55,6 +51,7 @@ from hypothesis._settings import ( ) from hypothesis.control import BuildContext from hypothesis.errors import ( + BackendCannotProceed, DeadlineExceeded, DidNotReproduce, FailedHealthCheck, @@ -178,7 +175,7 @@ class example: if not (args or kwargs): raise InvalidArgument("An example must provide at least one argument") - self.hypothesis_explicit_examples: List[Example] = [] + self.hypothesis_explicit_examples: list[Example] = [] self._this_example = Example(tuple(args), kwargs) def __call__(self, test: TestFunc) -> TestFunc: @@ -193,7 +190,7 @@ class example: *, reason: str = "", raises: Union[ - Type[BaseException], Tuple[Type[BaseException], ...] + type[BaseException], tuple[type[BaseException], ...] ] = BaseException, ) -> "example": """Mark this example as an expected failure, similarly to @@ -208,7 +205,7 @@ class example: @example(...).xfail() @example(...).xfail(reason="Prices must be non-negative") @example(...).xfail(raises=(KeyError, ValueError)) - @example(...).xfail(sys.version_info[:2] >= (3, 9), reason="needs py39+") + @example(...).xfail(sys.version_info[:2] >= (3, 12), reason="needs py 3.12") @example(...).xfail(condition=sys.platform != "linux", raises=OSError) def test(x): pass @@ -228,21 +225,6 @@ class example: # strategy. If we happen to generate y=0, the test will fail # because only the explicit example is treated as xfailing. x / y - - Note that this "method chaining" syntax requires Python 3.9 or later, for - :pep:`614` relaxing grammar restrictions on decorators. If you need to - support older versions of Python, you can use an identity function: - - .. code-block:: python - - def identity(x): - return x - - - @identity(example(...).xfail()) - def test(x): - pass - """ check_type(bool, condition, "condition") check_type(str, reason, "reason") @@ -283,21 +265,6 @@ class example: @example(...).via("hy-target-$label") def test(x): pass - - Note that this "method chaining" syntax requires Python 3.9 or later, for - :pep:`614` relaxing grammar restrictions on decorators. If you need to - support older versions of Python, you can use an identity function: - - .. code-block:: python - - def identity(x): - return x - - - @identity(example(...).via("label")) - def test(x): - pass - """ if not isinstance(whence, str): raise InvalidArgument(".via() must be passed a string") @@ -1063,7 +1030,7 @@ class StateForActualGivenExecution: # This was unexpected, meaning that the assume was flaky. # Report it as such. raise self._flaky_replay_to_failure(err, e) from None - except StopTest: + except (StopTest, BackendCannotProceed): # The engine knows how to handle this control exception, so it's # OK to re-raise it. raise @@ -1122,10 +1089,15 @@ class StateForActualGivenExecution: self.settings.backend != "hypothesis" and not getattr(runner, "_switch_to_hypothesis_provider", False) ) - data._observability_args = data.provider.realize( - data._observability_args - ) - self._string_repr = data.provider.realize(self._string_repr) + try: + data._observability_args = data.provider.realize( + data._observability_args + ) + self._string_repr = data.provider.realize(self._string_repr) + except BackendCannotProceed: + data._observability_args = {} + self._string_repr = "<backend failed to realize symbolic arguments>" + tc = make_testcase( start_timestamp=self._start_timestamp, test_name_or_nodeid=self.test_identifier, @@ -1335,10 +1307,17 @@ class StateForActualGivenExecution: # finished and they can't draw more data from it. ran_example.freeze() # pragma: no branch # No branch is possible here because we never have an active exception. - _raise_to_user(errors_to_report, self.settings, report_lines) + _raise_to_user( + errors_to_report, + self.settings, + report_lines, + verified_by=runner._verified_by, + ) -def _raise_to_user(errors_to_report, settings, target_lines, trailer=""): +def _raise_to_user( + errors_to_report, settings, target_lines, trailer="", verified_by=None +): """Helper function for attaching notes and grouping multiple errors.""" failing_prefix = "Falsifying example: " ls = [] @@ -1346,7 +1325,7 @@ def _raise_to_user(errors_to_report, settings, target_lines, trailer=""): for note in fragments: add_note(err, note) if note.startswith(failing_prefix): - ls.append(note[len(failing_prefix) :]) + ls.append(note.removeprefix(failing_prefix)) if current_pytest_item.value: current_pytest_item.value._hypothesis_failing_examples = ls @@ -1362,6 +1341,11 @@ def _raise_to_user(errors_to_report, settings, target_lines, trailer=""): if settings.verbosity >= Verbosity.normal: for line in target_lines: add_note(the_error_hypothesis_found, line) + + if verified_by: + msg = f"backend={verified_by!r} claimed to verify this test passes - please send them a bug report!" + add_note(err, msg) + raise the_error_hypothesis_found @@ -1837,7 +1821,7 @@ def find( ) specifier.validate() - last: List[Ex] = [] + last: list[Ex] = [] @settings @given(specifier) diff --git a/contrib/python/hypothesis/py3/hypothesis/database.py b/contrib/python/hypothesis/py3/hypothesis/database.py index 72b015ce9e8..72b6d8251df 100644 --- a/contrib/python/hypothesis/py3/hypothesis/database.py +++ b/contrib/python/hypothesis/py3/hypothesis/database.py @@ -14,12 +14,13 @@ import json import os import sys import warnings +from collections.abc import Iterable from datetime import datetime, timedelta, timezone from functools import lru_cache from hashlib import sha384 from os import getenv from pathlib import Path, PurePath -from typing import Dict, Iterable, Optional, Set +from typing import Optional from urllib.error import HTTPError, URLError from urllib.request import Request, urlopen from zipfile import BadZipFile, ZipFile @@ -195,7 +196,7 @@ class DirectoryBasedExampleDatabase(ExampleDatabase): def __init__(self, path: os.PathLike) -> None: self.path = Path(path) - self.keypaths: Dict[bytes, Path] = {} + self.keypaths: dict[bytes, Path] = {} def __repr__(self) -> str: return f"DirectoryBasedExampleDatabase({self.path!r})" @@ -444,7 +445,7 @@ class GitHubArtifactDatabase(ExampleDatabase): # .hypothesis/github-artifacts/<artifact-name>/<modified_isoformat>.zip self._artifact: Optional[Path] = None # This caches the artifact structure - self._access_cache: Optional[Dict[PurePath, Set[PurePath]]] = None + self._access_cache: Optional[dict[PurePath, set[PurePath]]] = None # Message to display if user doesn't wrap around ReadOnlyDatabase self._read_only_message = ( diff --git a/contrib/python/hypothesis/py3/hypothesis/errors.py b/contrib/python/hypothesis/py3/hypothesis/errors.py index 7da59ebd904..3adae78b6be 100644 --- a/contrib/python/hypothesis/py3/hypothesis/errors.py +++ b/contrib/python/hypothesis/py3/hypothesis/errors.py @@ -8,6 +8,8 @@ # 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/. +from typing import Literal + from hypothesis.internal.compat import ExceptionGroup @@ -237,3 +239,36 @@ class RewindRecursive(Exception): class SmallSearchSpaceWarning(HypothesisWarning): """Indicates that an inferred strategy does not span the search space in a meaningful way, for example by only creating default instances.""" + + +class BackendCannotProceed(HypothesisException): + """UNSTABLE API + + Raised by alternative backends when the PrimitiveProvider cannot proceed. + This is expected to occur inside one of the `.draw_*()` methods, or for + symbolic execution perhaps in `.realize(...)`. + + The optional `scope` argument can enable smarter integration: + + verified: + Do not request further test cases from this backend. We _may_ + generate more test cases with other backends; if one fails then + Hypothesis will report unsound verification in the backend too. + + exhausted: + Do not request further test cases from this backend; finish testing + with test cases generated with the default backend. Common if e.g. + native code blocks symbolic reasoning very early. + + discard_test_case: + This particular test case could not be converted to concrete values; + skip any further processing and continue with another test case from + this backend. + """ + + def __init__( + self, + scope: Literal["verified", "exhausted", "discard_test_case", "other"] = "other", + /, + ) -> None: + self.scope = scope diff --git a/contrib/python/hypothesis/py3/hypothesis/extra/_array_helpers.py b/contrib/python/hypothesis/py3/hypothesis/extra/_array_helpers.py index c66853fd1c3..681d55e8f22 100644 --- a/contrib/python/hypothesis/py3/hypothesis/extra/_array_helpers.py +++ b/contrib/python/hypothesis/py3/hypothesis/extra/_array_helpers.py @@ -9,7 +9,7 @@ # obtain one at https://mozilla.org/MPL/2.0/. import re -from typing import NamedTuple, Optional, Tuple, Union +from typing import NamedTuple, Optional, Union from hypothesis import assume, strategies as st from hypothesis.errors import InvalidArgument @@ -36,13 +36,13 @@ __all__ = [ ] -Shape = Tuple[int, ...] +Shape = tuple[int, ...] # We silence flake8 here because it disagrees with mypy about `ellipsis` (`type(...)`) -BasicIndex = Tuple[Union[int, slice, None, "ellipsis"], ...] # noqa: F821 +BasicIndex = tuple[Union[int, slice, None, "ellipsis"], ...] # noqa: F821 class BroadcastableShapes(NamedTuple): - input_shapes: Tuple[Shape, ...] + input_shapes: tuple[Shape, ...] result_shape: Shape @@ -121,7 +121,7 @@ def valid_tuple_axes( *, min_size: int = 0, max_size: Optional[int] = None, -) -> st.SearchStrategy[Tuple[int, ...]]: +) -> st.SearchStrategy[tuple[int, ...]]: """All tuples will have a length >= ``min_size`` and <= ``max_size``. The default value for ``max_size`` is ``ndim``. @@ -282,7 +282,7 @@ _SIGNATURE_MULTIPLE_OUTPUT = rf"^{_ARGUMENT_LIST}->{_ARGUMENT_LIST}$" class _GUfuncSig(NamedTuple): - input_shapes: Tuple[Shape, ...] + input_shapes: tuple[Shape, ...] result_shape: Shape diff --git a/contrib/python/hypothesis/py3/hypothesis/extra/_patching.py b/contrib/python/hypothesis/py3/hypothesis/extra/_patching.py index b508f199d6c..0532a354a9b 100644 --- a/contrib/python/hypothesis/py3/hypothesis/extra/_patching.py +++ b/contrib/python/hypothesis/py3/hypothesis/extra/_patching.py @@ -112,8 +112,6 @@ class AddExamplesCodemod(VisitorBasedCodemodCommand): else node.args ), ) - # Note: calling a method on a decorator requires PEP-614, i.e. Python 3.9+, - # but plumbing two cases through doesn't seem worth the trouble :-/ via = cst.Call( func=cst.Attribute(node, cst.Name("via")), args=[cst.Arg(cst.SimpleString(repr(via)))], diff --git a/contrib/python/hypothesis/py3/hypothesis/extra/array_api.py b/contrib/python/hypothesis/py3/hypothesis/extra/array_api.py index 85e5f3f8f99..f3a7ecc1557 100644 --- a/contrib/python/hypothesis/py3/hypothesis/extra/array_api.py +++ b/contrib/python/hypothesis/py3/hypothesis/extra/array_api.py @@ -10,21 +10,15 @@ import math import sys +from collections.abc import Iterable, Iterator, Mapping, Sequence from numbers import Real from types import SimpleNamespace from typing import ( TYPE_CHECKING, Any, - Iterable, - Iterator, - List, Literal, - Mapping, NamedTuple, Optional, - Sequence, - Tuple, - Type, TypeVar, Union, get_args, @@ -89,7 +83,7 @@ DataType = TypeVar("DataType") @check_function -def check_xp_attributes(xp: Any, attributes: List[str]) -> None: +def check_xp_attributes(xp: Any, attributes: list[str]) -> None: missing_attrs = [attr for attr in attributes if not hasattr(xp, attr)] if len(missing_attrs) > 0: f_attrs = ", ".join(missing_attrs) @@ -100,7 +94,7 @@ def check_xp_attributes(xp: Any, attributes: List[str]) -> None: def partition_attributes_and_stubs( xp: Any, attributes: Iterable[str] -) -> Tuple[List[Any], List[str]]: +) -> tuple[list[Any], list[str]]: non_stubs = [] stubs = [] for attr in attributes: @@ -112,7 +106,7 @@ def partition_attributes_and_stubs( return non_stubs, stubs -def warn_on_missing_dtypes(xp: Any, stubs: List[str]) -> None: +def warn_on_missing_dtypes(xp: Any, stubs: list[str]) -> None: f_stubs = ", ".join(stubs) warn( f"Array module {xp.__name__} does not have the following " @@ -124,7 +118,7 @@ def warn_on_missing_dtypes(xp: Any, stubs: List[str]) -> None: def find_castable_builtin_for_dtype( xp: Any, api_version: NominalVersion, dtype: DataType -) -> Type[Union[bool, int, float, complex]]: +) -> type[Union[bool, int, float, complex]]: """Returns builtin type which can have values that are castable to the given dtype, according to :xp-ref:`type promotion rules <type_promotion.html>`. @@ -591,7 +585,7 @@ def _arrays( @check_function -def check_dtypes(xp: Any, dtypes: List[DataType], stubs: List[str]) -> None: +def check_dtypes(xp: Any, dtypes: list[DataType], stubs: list[str]) -> None: if len(dtypes) == 0: assert len(stubs) > 0, "No dtypes passed but stubs is empty" f_stubs = ", ".join(stubs) diff --git a/contrib/python/hypothesis/py3/hypothesis/extra/codemods.py b/contrib/python/hypothesis/py3/hypothesis/extra/codemods.py index 85d1d44fd66..00e6ad30b1d 100644 --- a/contrib/python/hypothesis/py3/hypothesis/extra/codemods.py +++ b/contrib/python/hypothesis/py3/hypothesis/extra/codemods.py @@ -48,7 +48,7 @@ at the cost of additional configuration (adding ``'hypothesis.extra'`` to the import functools import importlib from inspect import Parameter, signature -from typing import ClassVar, List +from typing import ClassVar import libcst as cst import libcst.matchers as m @@ -65,7 +65,7 @@ def refactor(code: str) -> str: """ context = cst.codemod.CodemodContext() mod = cst.parse_module(code) - transforms: List[VisitorBasedCodemodCommand] = [ + transforms: list[VisitorBasedCodemodCommand] = [ HypothesisFixPositionalKeywonlyArgs(context), HypothesisFixComplexMinMagnitude(context), HypothesisFixHealthCheckAll(context), diff --git a/contrib/python/hypothesis/py3/hypothesis/extra/django/_fields.py b/contrib/python/hypothesis/py3/hypothesis/extra/django/_fields.py index d27eafd3d62..36b43dffe61 100644 --- a/contrib/python/hypothesis/py3/hypothesis/extra/django/_fields.py +++ b/contrib/python/hypothesis/py3/hypothesis/extra/django/_fields.py @@ -13,7 +13,7 @@ import string from datetime import datetime, timedelta from decimal import Decimal from functools import lru_cache -from typing import Any, Callable, Dict, Type, TypeVar, Union +from typing import Any, Callable, TypeVar, Union import django from django import forms as df @@ -68,8 +68,8 @@ def timezones(): # Mapping of field types, to strategy objects or functions of (type) -> strategy -_FieldLookUpType = Dict[ - Type[AnyField], +_FieldLookUpType = dict[ + type[AnyField], Union[st.SearchStrategy, Callable[[Any], st.SearchStrategy]], ] _global_field_lookup: _FieldLookUpType = { @@ -319,7 +319,7 @@ def _for_model_multiple_choice(field): def register_field_strategy( - field_type: Type[AnyField], strategy: st.SearchStrategy + field_type: type[AnyField], strategy: st.SearchStrategy ) -> None: """Add an entry to the global field-to-strategy lookup used by :func:`~hypothesis.extra.django.from_field`. diff --git a/contrib/python/hypothesis/py3/hypothesis/extra/django/_impl.py b/contrib/python/hypothesis/py3/hypothesis/extra/django/_impl.py index 09a1c2707cd..b1aa59744c6 100644 --- a/contrib/python/hypothesis/py3/hypothesis/extra/django/_impl.py +++ b/contrib/python/hypothesis/py3/hypothesis/extra/django/_impl.py @@ -11,7 +11,7 @@ import sys import unittest from functools import partial -from typing import TYPE_CHECKING, Optional, Type, TypeVar, Union +from typing import TYPE_CHECKING, Optional, TypeVar, Union from django import forms as df, test as dt from django.contrib.staticfiles import testing as dst @@ -66,7 +66,7 @@ class StaticLiveServerTestCase(HypothesisTestCase, dst.StaticLiveServerTestCase) @defines_strategy() def from_model( - model: Type[ModelT], /, **field_strategies: Union[st.SearchStrategy, EllipsisType] + model: type[ModelT], /, **field_strategies: Union[st.SearchStrategy, EllipsisType] ) -> st.SearchStrategy[ModelT]: """Return a strategy for examples of ``model``. @@ -136,7 +136,7 @@ def _models_impl(draw, strat): @defines_strategy() def from_form( - form: Type[df.Form], + form: type[df.Form], form_kwargs: Optional[dict] = None, **field_strategies: Union[st.SearchStrategy, EllipsisType], ) -> st.SearchStrategy[df.Form]: diff --git a/contrib/python/hypothesis/py3/hypothesis/extra/ghostwriter.py b/contrib/python/hypothesis/py3/hypothesis/extra/ghostwriter.py index 9179e2d28fe..d1936894ff9 100644 --- a/contrib/python/hypothesis/py3/hypothesis/extra/ghostwriter.py +++ b/contrib/python/hypothesis/py3/hypothesis/extra/ghostwriter.py @@ -82,6 +82,7 @@ import sys import types import warnings from collections import OrderedDict, defaultdict +from collections.abc import Iterable, Mapping from itertools import permutations, zip_longest from keyword import iskeyword as _iskeyword from string import ascii_lowercase @@ -91,16 +92,9 @@ from typing import ( Any, Callable, DefaultDict, - Dict, ForwardRef, - Iterable, - List, - Mapping, NamedTuple, Optional, - Set, - Tuple, - Type, TypeVar, Union, get_args, @@ -156,8 +150,8 @@ except {exceptions}: reject() """.strip() -Except = Union[Type[Exception], Tuple[Type[Exception], ...]] -ImportSet = Set[Union[str, Tuple[str, str]]] +Except = Union[type[Exception], tuple[type[Exception], ...]] +ImportSet = set[Union[str, tuple[str, str]]] _quietly_settings = settings( database=None, deadline=None, @@ -166,7 +160,7 @@ _quietly_settings = settings( ) -def _dedupe_exceptions(exc: Tuple[Type[Exception], ...]) -> Tuple[Type[Exception], ...]: +def _dedupe_exceptions(exc: tuple[type[Exception], ...]) -> tuple[type[Exception], ...]: # This is reminiscent of de-duplication logic I wrote for flake8-bugbear, # but with access to the actual objects we can just check for subclasses. # This lets us print e.g. `Exception` instead of `(Exception, OSError)`. @@ -177,7 +171,7 @@ def _dedupe_exceptions(exc: Tuple[Type[Exception], ...]) -> Tuple[Type[Exception return tuple(sorted(uniques, key=lambda e: e.__name__)) -def _check_except(except_: Except) -> Tuple[Type[Exception], ...]: +def _check_except(except_: Except) -> tuple[type[Exception], ...]: if isinstance(except_, tuple): for i, e in enumerate(except_): if not isinstance(e, type) or not issubclass(e, Exception): @@ -194,7 +188,7 @@ def _check_except(except_: Except) -> Tuple[Type[Exception], ...]: return (except_,) -def _exception_string(except_: Tuple[Type[Exception], ...]) -> Tuple[ImportSet, str]: +def _exception_string(except_: tuple[type[Exception], ...]) -> tuple[ImportSet, str]: if not except_: return set(), "" exceptions = [] @@ -215,7 +209,7 @@ def _check_style(style: str) -> None: raise InvalidArgument(f"Valid styles are 'pytest' or 'unittest', got {style!r}") -def _exceptions_from_docstring(doc: str) -> Tuple[Type[Exception], ...]: +def _exceptions_from_docstring(doc: str) -> tuple[type[Exception], ...]: """Return a tuple of exceptions that the docstring says may be raised. Note that we ignore non-builtin exception types for simplicity, as this is @@ -250,7 +244,7 @@ def _type_from_doc_fragment(token: str) -> Optional[type]: if elems is None and elem_token.endswith("s"): elems = _type_from_doc_fragment(elem_token[:-1]) if elems is not None and coll_token in ("list", "sequence", "collection"): - return List[elems] # type: ignore + return list[elems] # type: ignore # This might be e.g. "array-like of float"; arrays is better than nothing # even if we can't conveniently pass a generic type around. return _type_from_doc_fragment(coll_token) @@ -290,9 +284,8 @@ def _strategy_for(param: inspect.Parameter, docstring: str) -> st.SearchStrategy types = [] for token in re.split(r",? +or +| *, *", doc_type): for prefix in ("default ", "python "): - # `str or None, default "auto"`; `python int or numpy.int64` - if token.startswith(prefix): - token = token[len(prefix) :] + # e.g. `str or None, default "auto"` or `python int or numpy.int64` + token = token.removeprefix(prefix) if not token: continue try: @@ -452,7 +445,7 @@ def _guess_strategy_by_argname(name: str) -> st.SearchStrategy: return st.nothing() -def _get_params_builtin_fn(func: Callable) -> List[inspect.Parameter]: +def _get_params_builtin_fn(func: Callable) -> list[inspect.Parameter]: if ( isinstance(func, (types.BuiltinFunctionType, types.BuiltinMethodType)) and hasattr(func, "__doc__") @@ -484,7 +477,7 @@ def _get_params_builtin_fn(func: Callable) -> List[inspect.Parameter]: return [] -def _get_params_ufunc(func: Callable) -> List[inspect.Parameter]: +def _get_params_ufunc(func: Callable) -> list[inspect.Parameter]: if _is_probably_ufunc(func): # `inspect.signature` results vary for ufunc objects, but we can work out # what the required parameters would look like if it was reliable. @@ -499,7 +492,7 @@ def _get_params_ufunc(func: Callable) -> List[inspect.Parameter]: return [] -def _get_params(func: Callable) -> Dict[str, inspect.Parameter]: +def _get_params(func: Callable) -> dict[str, inspect.Parameter]: """Get non-vararg parameters of `func` as an ordered dict.""" try: params = list(get_signature(func).parameters.values()) @@ -523,7 +516,7 @@ def _get_params(func: Callable) -> Dict[str, inspect.Parameter]: def _params_to_dict( params: Iterable[inspect.Parameter], -) -> Dict[str, inspect.Parameter]: +) -> dict[str, inspect.Parameter]: var_param_kinds = (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD) return OrderedDict((p.name, p) for p in params if p.kind not in var_param_kinds) @@ -549,7 +542,7 @@ def _with_any_registered(): def _get_strategies( *funcs: Callable, pass_result_to_next_func: bool = False -) -> Dict[str, st.SearchStrategy]: +) -> dict[str, st.SearchStrategy]: """Return a dict of strategies for the union of arguments to `funcs`. If `pass_result_to_next_func` is True, assume that the result of each function @@ -559,7 +552,7 @@ def _get_strategies( This dict is used to construct our call to the `@given(...)` decorator. """ assert funcs, "Must pass at least one function" - given_strategies: Dict[str, st.SearchStrategy] = {} + given_strategies: dict[str, st.SearchStrategy] = {} for i, f in enumerate(funcs): params = _get_params(f) if pass_result_to_next_func and i >= 1: @@ -636,8 +629,6 @@ def _imports_for_strategy(strategy): for imp in _imports_for_object(_strip_typevars(arg)) } if re.match(r"from_(type|regex)\(", repr(strategy)): - if repr(strategy).startswith("from_type("): - return {module for module, _ in imports} return imports elif _get_module(strategy.function).startswith("hypothesis.extra."): module = _get_module(strategy.function).replace("._array_helpers", ".numpy") @@ -738,7 +729,7 @@ def _valid_syntax_repr(strategy): # When we ghostwrite for a module, we want to treat that as the __module__ for # each function, rather than whichever internal file it was actually defined in. -KNOWN_FUNCTION_LOCATIONS: Dict[object, str] = {} +KNOWN_FUNCTION_LOCATIONS: dict[object, str] = {} def _get_module_helper(obj): @@ -835,13 +826,13 @@ def _make_test_body( *funcs: Callable, ghost: str, test_body: str, - except_: Tuple[Type[Exception], ...], + except_: tuple[type[Exception], ...], assertions: str = "", style: str, given_strategies: Optional[Mapping[str, Union[str, st.SearchStrategy]]] = None, imports: Optional[ImportSet] = None, annotate: bool, -) -> Tuple[ImportSet, str]: +) -> tuple[ImportSet, str]: # A set of modules to import - we might add to this later. The import code # is written later, so we can have one import section for multiple magic() # test functions. @@ -902,7 +893,7 @@ def _make_test_body( def _annotate_args( argnames: Iterable[str], funcs: Iterable[Callable], imports: ImportSet ) -> Iterable[str]: - arg_parameters: DefaultDict[str, Set[Any]] = defaultdict(set) + arg_parameters: DefaultDict[str, set[Any]] = defaultdict(set) for func in funcs: try: params = tuple(get_signature(func, eval_str=True).parameters.values()) @@ -925,7 +916,7 @@ def _annotate_args( class _AnnotationData(NamedTuple): type_name: str - imports: Set[str] + imports: set[str] def _parameters_to_annotation_name( @@ -952,7 +943,7 @@ def _parameters_to_annotation_name( def _join_generics( - origin_type_data: Optional[Tuple[str, Set[str]]], + origin_type_data: Optional[tuple[str, set[str]]], annotations: Iterable[Optional[_AnnotationData]], ) -> Optional[_AnnotationData]: if origin_type_data is None: @@ -979,9 +970,9 @@ def _join_generics( def _join_argument_annotations( annotations: Iterable[Optional[_AnnotationData]], -) -> Optional[Tuple[List[str], Set[str]]]: - imports: Set[str] = set() - arg_types: List[str] = [] +) -> Optional[tuple[list[str], set[str]]]: + imports: set[str] = set() + arg_types: list[str] = [] for annotation in annotations: if annotation is None: @@ -1137,10 +1128,10 @@ ROUNDTRIP_PAIRS = ( ) -def _get_testable_functions(thing: object) -> Dict[str, Callable]: +def _get_testable_functions(thing: object) -> dict[str, Callable]: by_name = {} if callable(thing): - funcs: List[Optional[Any]] = [thing] + funcs: list[Optional[Any]] = [thing] elif isinstance(thing, types.ModuleType): if hasattr(thing, "__all__"): funcs = [getattr(thing, name, None) for name in thing.__all__] @@ -1228,7 +1219,7 @@ def magic( for k in sorted(sys.modules, key=len): if ( k.startswith(f"{thing.__name__}.") - and "._" not in k[len(thing.__name__) :] + and "._" not in k.removeprefix(thing.__name__) and not k.startswith(tuple(f"{m}." for m in mods)) and _get_testable_functions(sys.modules[k]) ): @@ -1735,10 +1726,10 @@ def _make_binop_body( commutative: bool = True, identity: Union[X, EllipsisType, None] = ..., distributes_over: Optional[Callable[[X, X], X]] = None, - except_: Tuple[Type[Exception], ...], + except_: tuple[type[Exception], ...], style: str, annotate: bool, -) -> Tuple[ImportSet, str]: +) -> tuple[ImportSet, str]: strategies = _get_strategies(func) operands, b = (strategies.pop(p) for p in list(_get_params(func))[:2]) if repr(operands) != repr(b): diff --git a/contrib/python/hypothesis/py3/hypothesis/extra/lark.py b/contrib/python/hypothesis/py3/hypothesis/extra/lark.py index 37b5f5d4013..9b26519a6e7 100644 --- a/contrib/python/hypothesis/py3/hypothesis/extra/lark.py +++ b/contrib/python/hypothesis/py3/hypothesis/extra/lark.py @@ -25,7 +25,7 @@ your own at all. """ from inspect import signature -from typing import Dict, Optional +from typing import Optional import lark from lark.grammar import NonTerminal, Terminal @@ -201,7 +201,7 @@ def from_lark( grammar: lark.lark.Lark, *, start: Optional[str] = None, - explicit: Optional[Dict[str, st.SearchStrategy[str]]] = None, + explicit: Optional[dict[str, st.SearchStrategy[str]]] = None, alphabet: st.SearchStrategy[str] = st.characters(codec="utf-8"), ) -> st.SearchStrategy[str]: """A strategy for strings accepted by the given context-free grammar. diff --git a/contrib/python/hypothesis/py3/hypothesis/extra/numpy.py b/contrib/python/hypothesis/py3/hypothesis/extra/numpy.py index b6e75d3efa4..a94471769ba 100644 --- a/contrib/python/hypothesis/py3/hypothesis/extra/numpy.py +++ b/contrib/python/hypothesis/py3/hypothesis/extra/numpy.py @@ -11,20 +11,8 @@ import importlib import math import types -from typing import ( - TYPE_CHECKING, - Any, - Literal, - Mapping, - Optional, - Sequence, - Tuple, - Type, - TypeVar, - Union, - cast, - overload, -) +from collections.abc import Mapping, Sequence +from typing import TYPE_CHECKING, Any, Literal, Optional, TypeVar, Union, cast, overload import numpy as np @@ -1190,7 +1178,7 @@ def integer_array_indices( shape: Shape, *, result_shape: st.SearchStrategy[Shape] = array_shapes(), -) -> "st.SearchStrategy[Tuple[NDArray[np.signedinteger[Any]], ...]]": ... +) -> "st.SearchStrategy[tuple[NDArray[np.signedinteger[Any]], ...]]": ... @overload @@ -1200,7 +1188,7 @@ def integer_array_indices( *, result_shape: st.SearchStrategy[Shape] = array_shapes(), dtype: "np.dtype[I]", -) -> "st.SearchStrategy[Tuple[NDArray[I], ...]]": ... +) -> "st.SearchStrategy[tuple[NDArray[I], ...]]": ... @defines_strategy() @@ -1209,7 +1197,7 @@ def integer_array_indices( *, result_shape: st.SearchStrategy[Shape] = array_shapes(), dtype: "np.dtype[I] | np.dtype[np.signedinteger[Any]]" = np.dtype(int), -) -> "st.SearchStrategy[Tuple[NDArray[I], ...]]": +) -> "st.SearchStrategy[tuple[NDArray[I], ...]]": """Return a search strategy for tuples of integer-arrays that, when used to index into an array of shape ``shape``, given an array whose shape was drawn from ``result_shape``. @@ -1314,7 +1302,7 @@ def _dtype_from_args(args): return np.dtype(dtype) -def _from_type(thing: Type[Ex]) -> Optional[st.SearchStrategy[Ex]]: +def _from_type(thing: type[Ex]) -> Optional[st.SearchStrategy[Ex]]: """Called by st.from_type to try to infer a strategy for thing using numpy. If we can infer a numpy-specific strategy for thing, we return that; otherwise, diff --git a/contrib/python/hypothesis/py3/hypothesis/extra/pandas/impl.py b/contrib/python/hypothesis/py3/hypothesis/extra/pandas/impl.py index 16ef90fc63e..5beeede287d 100644 --- a/contrib/python/hypothesis/py3/hypothesis/extra/pandas/impl.py +++ b/contrib/python/hypothesis/py3/hypothesis/extra/pandas/impl.py @@ -9,9 +9,10 @@ # obtain one at https://mozilla.org/MPL/2.0/. from collections import OrderedDict, abc +from collections.abc import Sequence from copy import copy from datetime import datetime, timedelta -from typing import Any, Generic, List, Optional, Sequence, Set, Union +from typing import Any, Generic, Optional, Union import attr import numpy as np @@ -389,7 +390,7 @@ def columns( elements: Optional[st.SearchStrategy[Ex]] = None, fill: Optional[st.SearchStrategy[Ex]] = None, unique: bool = False, -) -> List[column[Ex]]: +) -> list[column[Ex]]: """A convenience function for producing a list of :class:`column` objects of the same general shape. @@ -400,7 +401,7 @@ def columns( create the columns. """ if isinstance(names_or_number, (int, float)): - names: List[Union[int, str, None]] = [None] * names_or_number + names: list[Union[int, str, None]] = [None] * names_or_number else: names = list(names_or_number) return [ @@ -554,7 +555,7 @@ def data_frames( cols = try_convert(tuple, columns, "columns") rewritten_columns = [] - column_names: Set[str] = set() + column_names: set[str] = set() for i, c in enumerate(cols): check_type(column, c, f"columns[{i}]") diff --git a/contrib/python/hypothesis/py3/hypothesis/extra/redis.py b/contrib/python/hypothesis/py3/hypothesis/extra/redis.py index e0f1c1b8512..516c2d88e44 100644 --- a/contrib/python/hypothesis/py3/hypothesis/extra/redis.py +++ b/contrib/python/hypothesis/py3/hypothesis/extra/redis.py @@ -8,9 +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/. +from collections.abc import Iterable from contextlib import contextmanager from datetime import timedelta -from typing import Iterable from redis import Redis diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/charmap.py b/contrib/python/hypothesis/py3/hypothesis/internal/charmap.py index 468beeb5e82..8a1e98ef180 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/charmap.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/charmap.py @@ -16,15 +16,14 @@ import sys import tempfile import unicodedata from functools import lru_cache -from typing import Dict, Tuple from hypothesis.configuration import storage_directory from hypothesis.control import _current_build_context from hypothesis.errors import InvalidArgument from hypothesis.internal.intervalsets import IntervalSet -intervals = Tuple[Tuple[int, int], ...] -cache_type = Dict[Tuple[Tuple[str, ...], int, int, intervals], IntervalSet] +intervals = tuple[tuple[int, int], ...] +cache_type = dict[tuple[tuple[str, ...], int, int, intervals], IntervalSet] def charmap_file(fname="charmap"): diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/compat.py b/contrib/python/hypothesis/py3/hypothesis/internal/compat.py index df1ba067e97..baa9284bce1 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/compat.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/compat.py @@ -17,7 +17,7 @@ import sys import sysconfig import typing from functools import partial -from typing import Any, ForwardRef, List, Optional, TypedDict as TypedDict, get_args +from typing import Any, ForwardRef, Optional, TypedDict as TypedDict, get_args try: BaseExceptionGroup = BaseExceptionGroup @@ -150,16 +150,14 @@ def get_type_hints(thing): ) return {k: v for k, v in get_type_hints(thing.func).items() if k not in bound} - kwargs = {} if sys.version_info[:2] < (3, 9) else {"include_extras": True} - try: - hints = typing.get_type_hints(thing, **kwargs) + hints = typing.get_type_hints(thing, include_extras=True) except (AttributeError, TypeError, NameError): # pragma: no cover hints = {} if inspect.isclass(thing): try: - hints.update(typing.get_type_hints(thing.__init__, **kwargs)) + hints.update(typing.get_type_hints(thing.__init__, include_extras=True)) except (TypeError, NameError, AttributeError): pass @@ -220,7 +218,7 @@ def ceil(x): return y -def extract_bits(x: int, /, width: Optional[int] = None) -> List[int]: +def extract_bits(x: int, /, width: Optional[int] = None) -> list[int]: assert x >= 0 result = [] while x: @@ -232,7 +230,7 @@ def extract_bits(x: int, /, width: Optional[int] = None) -> List[int]: return result -# int.bit_count was added sometime around python 3.9 +# int.bit_count was added in python 3.10 try: bit_count = int.bit_count except AttributeError: # pragma: no cover diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/choicetree.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/choicetree.py index d7645e18891..0ba8ab819b7 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/choicetree.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/choicetree.py @@ -9,8 +9,9 @@ # obtain one at https://mozilla.org/MPL/2.0/. from collections import defaultdict +from collections.abc import Iterable, Sequence from random import Random -from typing import Callable, Dict, Iterable, List, Optional, Sequence +from typing import Callable, List, Optional from hypothesis.internal.conjecture.junkdrawer import LazySequenceCopy @@ -144,7 +145,7 @@ class ChoiceTree: class TreeNode: def __init__(self) -> None: - self.children: Dict[int, TreeNode] = defaultdict(TreeNode) + self.children: dict[int, TreeNode] = defaultdict(TreeNode) self.live_child_count: "Optional[int]" = None self.n: "Optional[int]" = None diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py index 2d24f984e5c..3664d767296 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/data.py @@ -13,6 +13,7 @@ import contextlib import math import time from collections import defaultdict +from collections.abc import Iterable, Iterator, Sequence from enum import IntEnum from random import Random from sys import float_info @@ -20,19 +21,9 @@ from typing import ( TYPE_CHECKING, Any, Callable, - DefaultDict, - Dict, - FrozenSet, - Iterable, - Iterator, - List, Literal, NoReturn, Optional, - Sequence, - Set, - Tuple, - Type, TypedDict, TypeVar, Union, @@ -86,10 +77,10 @@ else: TOP_LABEL = calc_label_from_name("top") -InterestingOrigin = Tuple[ - Type[BaseException], str, int, Tuple[Any, ...], Tuple[Tuple[Any, ...], ...] +InterestingOrigin = tuple[ + type[BaseException], str, int, tuple[Any, ...], tuple[tuple[Any, ...], ...] ] -TargetObservations = Dict[str, Union[int, float]] +TargetObservations = dict[str, Union[int, float]] T = TypeVar("T") @@ -129,7 +120,7 @@ IRKWargsType: TypeAlias = Union[ ] IRTypeName: TypeAlias = Literal["integer", "string", "boolean", "float", "bytes"] # index, ir_type, kwargs, forced -MisalignedAt: TypeAlias = Tuple[int, IRTypeName, IRKWargsType, Optional[IRType]] +MisalignedAt: TypeAlias = tuple[int, IRTypeName, IRKWargsType, Optional[IRType]] class ExtraInformation: @@ -161,7 +152,7 @@ class StructuralCoverageTag: label: int = attr.ib() -STRUCTURAL_COVERAGE_CACHE: Dict[int, StructuralCoverageTag] = {} +STRUCTURAL_COVERAGE_CACHE: dict[int, StructuralCoverageTag] = {} def structural_coverage(label: int) -> StructuralCoverageTag: @@ -330,7 +321,7 @@ class Example: return self.ir_end - self.ir_start @property - def children(self) -> "List[Example]": + def children(self) -> "list[Example]": """The list of all examples with this as a parent, in increasing index order.""" return [self.owner[i] for i in self.owner.children[self.index]] @@ -346,7 +337,7 @@ class ExampleProperty: """ def __init__(self, examples: "Examples"): - self.example_stack: "List[int]" = [] + self.example_stack: "list[int]" = [] self.examples = examples self.bytes_read = 0 self.example_count = 0 @@ -415,7 +406,7 @@ class ExampleProperty: return self.result -def calculated_example_property(cls: Type[ExampleProperty]) -> Any: +def calculated_example_property(cls: type[ExampleProperty]) -> Any: """Given an ``ExampleProperty`` as above we use this decorator to transform it into a lazy property on the ``Examples`` class, which has as its value the result of calling ``cls.run()``, @@ -458,10 +449,10 @@ class ExampleRecord: """ def __init__(self) -> None: - self.labels: List[int] = [] - self.__index_of_labels: "Optional[Dict[int, int]]" = {} + self.labels: list[int] = [] + self.__index_of_labels: "dict[int, int] | None" = {} self.trail = IntList() - self.ir_nodes: List[IRNode] = [] + self.ir_nodes: list[IRNode] = [] def freeze(self) -> None: self.__index_of_labels = None @@ -522,7 +513,7 @@ class Examples: STOP_EXAMPLE_DISCARD_RECORD ) + record.trail.count(STOP_EXAMPLE_NO_DISCARD_RECORD) self.blocks = blocks - self.__children: "Optional[List[Sequence[int]]]" = None + self.__children: "list[Sequence[int]] | None" = None class _starts_and_ends(ExampleProperty): def begin(self) -> None: @@ -535,10 +526,10 @@ class Examples: def stop_example(self, i: int, *, discarded: bool) -> None: self.ends[i] = self.bytes_read - def finish(self) -> Tuple[IntList, IntList]: + def finish(self) -> tuple[IntList, IntList]: return (self.starts, self.ends) - starts_and_ends: "Tuple[IntList, IntList]" = calculated_example_property( + starts_and_ends: "tuple[IntList, IntList]" = calculated_example_property( _starts_and_ends ) @@ -561,10 +552,10 @@ class Examples: def stop_example(self, i: int, *, discarded: bool) -> None: self.ends[i] = self.ir_node_count - def finish(self) -> Tuple[IntList, IntList]: + def finish(self) -> tuple[IntList, IntList]: return (self.starts, self.ends) - ir_starts_and_ends: "Tuple[IntList, IntList]" = calculated_example_property( + ir_starts_and_ends: "tuple[IntList, IntList]" = calculated_example_property( _ir_starts_and_ends ) @@ -578,21 +569,21 @@ class Examples: class _discarded(ExampleProperty): def begin(self) -> None: - self.result: "Set[int]" = set() + self.result: "set[int]" = set() - def finish(self) -> FrozenSet[int]: + def finish(self) -> frozenset[int]: return frozenset(self.result) def stop_example(self, i: int, *, discarded: bool) -> None: if discarded: self.result.add(i) - discarded: FrozenSet[int] = calculated_example_property(_discarded) + discarded: frozenset[int] = calculated_example_property(_discarded) class _trivial(ExampleProperty): def begin(self) -> None: self.nontrivial = IntList.of_length(len(self.examples)) - self.result: "Set[int]" = set() + self.result: "set[int]" = set() def block(self, i: int) -> None: if not self.examples.blocks.trivial(i): @@ -605,10 +596,10 @@ class Examples: else: self.result.add(i) - def finish(self) -> FrozenSet[int]: + def finish(self) -> frozenset[int]: return frozenset(self.result) - trivial: FrozenSet[int] = calculated_example_property(_trivial) + trivial: frozenset[int] = calculated_example_property(_trivial) class _parentage(ExampleProperty): def stop_example(self, i: int, *, discarded: bool) -> None: @@ -633,7 +624,7 @@ class Examples: def ir_node(self, ir_node: "IRNode") -> None: self.result.append(ir_node) - ir_tree_nodes: "List[IRNode]" = calculated_example_property(_ir_tree_nodes) + ir_tree_nodes: "list[IRNode]" = calculated_example_property(_ir_tree_nodes) class _label_indices(ExampleProperty): def start_example(self, i: int, label_index: int) -> None: @@ -643,7 +634,7 @@ class Examples: class _mutator_groups(ExampleProperty): def begin(self) -> None: - self.groups: "Dict[int, Set[Tuple[int, int]]]" = defaultdict(set) + self.groups: "dict[int, set[tuple[int, int]]]" = defaultdict(set) def start_example(self, i: int, label_index: int) -> None: # TODO should we discard start == end cases? occurs for eg st.data() @@ -652,17 +643,17 @@ class Examples: key = (self.examples[i].ir_start, self.examples[i].ir_end) self.groups[label_index].add(key) - def finish(self) -> Iterable[Set[Tuple[int, int]]]: + def finish(self) -> Iterable[set[tuple[int, int]]]: # Discard groups with only one example, since the mutator can't # do anything useful with them. return [g for g in self.groups.values() if len(g) >= 2] - mutator_groups: List[Set[Tuple[int, int]]] = calculated_example_property( + mutator_groups: list[set[tuple[int, int]]] = calculated_example_property( _mutator_groups ) @property - def children(self) -> List[Sequence[int]]: + def children(self) -> list[Sequence[int]]: if self.__children is None: children = [IntList() for _ in range(len(self))] for i, p in enumerate(self.parentage): @@ -722,7 +713,7 @@ class Block: all_zero: bool = attr.ib(repr=False) @property - def bounds(self) -> Tuple[int, int]: + def bounds(self) -> tuple[int, int]: return (self.start, self.end) @property @@ -753,7 +744,7 @@ class Blocks: __slots__ = ("endpoints", "owner", "__blocks", "__count", "__sparse") owner: "Union[ConjectureData, ConjectureResult, None]" - __blocks: Union[Dict[int, Block], List[Optional[Block]]] + __blocks: Union[dict[int, Block], list[Optional[Block]]] def __init__(self, owner: "ConjectureData") -> None: self.owner = owner @@ -788,7 +779,7 @@ class Blocks: """Equivalent to self[i].end.""" return self.endpoints[i] - def all_bounds(self) -> Iterable[Tuple[int, int]]: + def all_bounds(self) -> Iterable[tuple[int, int]]: """Equivalent to [(b.start, b.end) for b in self].""" prev = 0 for e in self.endpoints: @@ -837,7 +828,7 @@ class Blocks: # stop being sparse and want to use most of the blocks. Switch # over to a list at that point. if self.__sparse and len(self.__blocks) * 2 >= len(self): - new_blocks: "List[Optional[Block]]" = [None] * len(self) + new_blocks: "list[Block | None]" = [None] * len(self) assert isinstance(self.__blocks, dict) for k, v in self.__blocks.items(): new_blocks[k] = v @@ -894,7 +885,7 @@ class Blocks: yield self[i] def __repr__(self) -> str: - parts: "List[str]" = [] + parts: "list[str]" = [] for i in range(len(self)): b = self.__known_block(i) if b is None: @@ -1172,11 +1163,11 @@ class ConjectureResult: extra_information: Optional[ExtraInformation] = attr.ib() has_discards: bool = attr.ib() target_observations: TargetObservations = attr.ib() - tags: FrozenSet[StructuralCoverageTag] = attr.ib() - forced_indices: FrozenSet[int] = attr.ib(repr=False) + tags: frozenset[StructuralCoverageTag] = attr.ib() + forced_indices: frozenset[int] = attr.ib(repr=False) examples: Examples = 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) + arg_slices: set[tuple[int, int]] = attr.ib(repr=False) + slice_comments: dict[tuple[int, int], str] = attr.ib(repr=False) misaligned_at: Optional[MisalignedAt] = attr.ib(repr=False) index: int = attr.ib(init=False) @@ -1200,7 +1191,7 @@ _Lifetime: TypeAlias = Literal["test_case", "test_function"] class _BackendInfoMsg(TypedDict): type: str title: str - content: Union[str, Dict[str, Any]] + content: Union[str, dict[str, Any]] class PrimitiveProvider(abc.ABC): @@ -1254,11 +1245,12 @@ class PrimitiveProvider(abc.ABC): symbolic before calling `realize`, so you should handle the case where `value` is non-symbolic. - The returned value should be non-symbolic. + The returned value should be non-symbolic. If you cannot provide a value, + raise hypothesis.errors.BackendCannotProceed("discard_test_case") """ return value - def observe_test_case(self) -> Dict[str, Any]: + def observe_test_case(self) -> dict[str, Any]: """Called at the end of the test case when observability mode is active. The return value should be a non-symbolic json-encodable dictionary, @@ -1864,12 +1856,12 @@ class HypothesisProvider(PrimitiveProvider): max_value: float, allow_nan: bool, smallest_nonzero_magnitude: float, - ) -> Tuple[ + ) -> tuple[ Optional[Sampler], Optional[Literal[0, 1]], Optional[Callable[[float], float]], Optional[Callable[[float], float]], - List[float], + list[float], ]: """ Caches initialization logic for draw_float, as an alternative to @@ -1902,12 +1894,12 @@ class HypothesisProvider(PrimitiveProvider): max_value: float, allow_nan: bool, smallest_nonzero_magnitude: float, - ) -> Tuple[ + ) -> tuple[ Optional[Sampler], Optional[Literal[0, 1]], Optional[Callable[[float], float]], Optional[Callable[[float], float]], - List[float], + list[float], ]: if smallest_nonzero_magnitude == 0.0: # pragma: no cover raise FloatingPointError( @@ -1973,7 +1965,7 @@ class ConjectureData: @classmethod def for_buffer( cls, - buffer: Union[List[int], bytes], + buffer: Union[list[int], bytes], *, observer: Optional[DataObserver] = None, provider: Union[type, PrimitiveProvider] = HypothesisProvider, @@ -1985,7 +1977,7 @@ class ConjectureData: @classmethod def for_ir_tree( cls, - ir_tree_prefix: List[IRNode], + ir_tree_prefix: list[IRNode], *, observer: Optional[DataObserver] = None, provider: Union[type, PrimitiveProvider] = HypothesisProvider, @@ -2005,12 +1997,12 @@ class ConjectureData: def __init__( self, max_length: int, - prefix: Union[List[int], bytes, bytearray], + prefix: Union[list[int], bytes, bytearray], *, random: Optional[Random], observer: Optional[DataObserver] = None, provider: Union[type, PrimitiveProvider] = HypothesisProvider, - ir_tree_prefix: Optional[List[IRNode]] = None, + ir_tree_prefix: Optional[list[IRNode]] = None, ) -> None: if observer is None: observer = DataObserver() @@ -2037,11 +2029,11 @@ class ConjectureData: global_test_counter += 1 self.start_time = time.perf_counter() self.gc_start_time = gc_cumulative_time() - self.events: Dict[str, Union[str, int, float]] = {} - self.forced_indices: "Set[int]" = set() + self.events: dict[str, Union[str, int, float]] = {} + self.forced_indices: "set[int]" = set() self.interesting_origin: Optional[InterestingOrigin] = None - self.draw_times: "Dict[str, float]" = {} - self._stateful_run_times: "DefaultDict[str, float]" = defaultdict(float) + self.draw_times: "dict[str, float]" = {} + self._stateful_run_times: "defaultdict[str, float]" = defaultdict(float) self.max_depth = 0 self.has_discards = False @@ -2058,8 +2050,8 @@ class ConjectureData: # Tags which indicate something about which part of the search space # this example is in. These are used to guide generation. - self.tags: "Set[StructuralCoverageTag]" = set() - self.labels_for_structure_stack: "List[Set[int]]" = [] + self.tags: "set[StructuralCoverageTag]" = set() + self.labels_for_structure_stack: "list[set[int]]" = [] # Normally unpopulated but we need this in the niche case # that self.as_result() is Overrun but we still want the @@ -2073,9 +2065,9 @@ class ConjectureData: # Slice indices for discrete reportable parts that which-parts-matter can # try varying, to report if the minimal example always fails anyway. - self.arg_slices: Set[Tuple[int, int]] = set() - self.slice_comments: Dict[Tuple[int, int], str] = {} - self._observability_args: Dict[str, Any] = {} + self.arg_slices: set[tuple[int, int]] = set() + self.slice_comments: dict[tuple[int, int], str] = {} + self._observability_args: dict[str, Any] = {} self._observability_predicates: defaultdict = defaultdict( lambda: {"satisfied": 0, "unsatisfied": 0} ) diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/datatree.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/datatree.py index 87a0c988c88..5ec38c2edf4 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/datatree.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/datatree.py @@ -10,7 +10,7 @@ import itertools import math -from typing import List, Optional, Union +from typing import Optional, Union import attr @@ -418,9 +418,9 @@ class TreeNode: # The kwargs, value, and ir_types of the nodes stored here. These always # have the same length. The values at index i belong to node i. - kwargs: List[IRKWargsType] = attr.ib(factory=list) - values: List[IRType] = attr.ib(factory=list) - ir_types: List[IRTypeName] = attr.ib(factory=list) + kwargs: list[IRKWargsType] = attr.ib(factory=list) + values: list[IRType] = attr.ib(factory=list) + ir_types: list[IRTypeName] = attr.ib(factory=list) # The indices of nodes which had forced values. # diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/engine.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/engine.py index 8c8fd18add9..7cf4e72ed85 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/engine.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/engine.py @@ -13,23 +13,19 @@ import math import textwrap import time from collections import defaultdict -from contextlib import contextmanager +from collections.abc import Generator +from contextlib import contextmanager, suppress from datetime import timedelta from enum import Enum from random import Random, getrandbits from typing import ( Any, Callable, - Dict, Final, - FrozenSet, - Generator, List, Literal, NoReturn, Optional, - Set, - Tuple, Union, cast, overload, @@ -41,6 +37,7 @@ from hypothesis import HealthCheck, Phase, Verbosity, settings as Settings from hypothesis._settings import local_settings from hypothesis.database import ExampleDatabase from hypothesis.errors import ( + BackendCannotProceed, FlakyReplay, HypothesisException, InvalidArgument, @@ -96,7 +93,7 @@ BUFFER_SIZE: Final[int] = 8 * 1024 # (but make it monkeypatchable, for the rare users who need to keep on shrinking) MAX_SHRINKING_SECONDS: Final[int] = 300 -Ls: TypeAlias = List["Ls | int"] +Ls: TypeAlias = list["Ls | int"] @attr.s @@ -116,7 +113,9 @@ class HealthCheckState: """Return a terminal report describing what was slow.""" if not self.draw_times: return "" - width = max(len(k[len("generate:") :].strip(": ")) for k in self.draw_times) + width = max( + len(k.removeprefix("generate:").removesuffix(": ")) for k in self.draw_times + ) out = [f"\n {'':^{width}} count | fraction | slowest draws (seconds)"] args_in_order = sorted(self.draw_times.items(), key=lambda kv: -sum(kv[1])) for i, (argname, times) in enumerate(args_in_order): # pragma: no branch @@ -131,7 +130,7 @@ class HealthCheckState: # Compute the row to report, omitting times <1ms to focus on slow draws reprs = [f"{t:>6.3f}," for t in sorted(times)[-5:] if t > 5e-4] desc = " ".join(([" -- "] * 5 + reprs)[-5:]).rstrip(",") - arg = argname[len("generate:") :].strip(": ") # removeprefix in py3.9 + arg = argname.removeprefix("generate:").removesuffix(": ") out.append( f" {arg:^{width}} | {len(times):>4} | " f"{math.fsum(times)/self.total_draw_time:>7.0%} | {desc}" @@ -177,14 +176,14 @@ class CallStats(TypedDict): runtime: float drawtime: float gctime: float - events: List[str] + events: list[str] PhaseStatistics = TypedDict( "PhaseStatistics", { "duration-seconds": float, - "test-cases": List[CallStats], + "test-cases": list[CallStats], "distinct-failures": int, "shrinks-successful": int, }, @@ -196,7 +195,7 @@ StatisticsDict = TypedDict( "reuse-phase": NotRequired[PhaseStatistics], "shrink-phase": NotRequired[PhaseStatistics], "stopped-because": NotRequired[str], - "targets": NotRequired[Dict[str, float]], + "targets": NotRequired[dict[str, float]], "nodeid": NotRequired[str], }, ) @@ -227,16 +226,16 @@ class ConjectureRunner: # which transfer to the global dict at the end of each phase. self._current_phase: str = "(not a phase)" self.statistics: StatisticsDict = {} - self.stats_per_test_case: List[CallStats] = [] + self.stats_per_test_case: list[CallStats] = [] # At runtime, the keys are only ever type `InterestingOrigin`, but can be `None` during tests. - self.interesting_examples: Dict[InterestingOrigin, ConjectureResult] = {} + self.interesting_examples: dict[InterestingOrigin, ConjectureResult] = {} # We use call_count because there may be few possible valid_examples. self.first_bug_found_at: Optional[int] = None self.last_bug_found_at: Optional[int] = None # At runtime, the keys are only ever type `InterestingOrigin`, but can be `None` during tests. - self.shrunk_examples: Set[Optional[InterestingOrigin]] = set() + self.shrunk_examples: set[Optional[InterestingOrigin]] = set() self.health_check_state: Optional[HealthCheckState] = None @@ -249,7 +248,7 @@ class ConjectureRunner: self.best_observed_targets: "defaultdict[str, float]" = defaultdict( lambda: NO_SCORE ) - self.best_examples_of_observed_targets: Dict[str, ConjectureResult] = {} + self.best_examples_of_observed_targets: dict[str, ConjectureResult] = {} # We keep the pareto front in the example database if we have one. This # is only marginally useful at present, but speeds up local development @@ -270,6 +269,9 @@ class ConjectureRunner: self.__pending_call_explanation: Optional[str] = None self._switch_to_hypothesis_provider: bool = False + self.__failed_realize_count = 0 + self._verified_by = None # note unsound verification by alt backends + def explain_next_call_as(self, explanation: str) -> None: self.__pending_call_explanation = explanation @@ -328,9 +330,9 @@ class ConjectureRunner: def _cache_key_ir( self, *, - nodes: Optional[List[IRNode]] = None, + nodes: Optional[list[IRNode]] = None, data: Union[ConjectureData, ConjectureResult, None] = None, - ) -> Tuple[Tuple[Any, ...], ...]: + ) -> tuple[tuple[Any, ...], ...]: assert (nodes is not None) ^ (data is not None) if data is not None: nodes = data.examples.ir_tree_nodes @@ -371,7 +373,7 @@ class ConjectureRunner: self.__data_cache_ir[key] = result def cached_test_function_ir( - self, nodes: List[IRNode], *, error_on_discard: bool = False + self, nodes: list[IRNode], *, error_on_discard: bool = False ) -> Union[ConjectureResult, _Overrun]: key = self._cache_key_ir(nodes=nodes) try: @@ -425,6 +427,18 @@ class ConjectureRunner: except KeyboardInterrupt: interrupted = True raise + except BackendCannotProceed as exc: + if exc.scope in ("verified", "exhausted"): + self._switch_to_hypothesis_provider = True + if exc.scope == "verified": + self._verified_by = self.settings.backend + elif exc.scope == "discard_test_case": + self.__failed_realize_count += 1 + if ( + self.__failed_realize_count > 10 + and (self.__failed_realize_count / self.call_count) > 0.2 + ): + self._switch_to_hypothesis_provider = True except BaseException: self.save_buffer(data.buffer) raise @@ -516,7 +530,9 @@ class ConjectureRunner: # drive the ir tree through the test function to convert it # to a buffer initial_origin = data.interesting_origin - initial_traceback = data.extra_information._expected_traceback # type: ignore + initial_traceback = getattr( + data.extra_information, "_expected_traceback", None + ) data = ConjectureData.for_ir_tree(data.examples.ir_tree_nodes) self.__stoppable_test_function(data) data.freeze() @@ -528,7 +544,11 @@ class ConjectureRunner: data.status.INVALID: "failed filters", data.status.OVERRUN: "overran", }[data.status] - wrapped_tb = textwrap.indent(initial_traceback, " | ") + wrapped_tb = ( + "" + if initial_traceback is None + else textwrap.indent(initial_traceback, " | ") + ) raise FlakyReplay( f"Inconsistent results from replaying a failing test case!\n" f"{wrapped_tb}on backend={self.settings.backend!r} but " @@ -724,7 +744,7 @@ class ConjectureRunner: if not self.report_debug_info: return - stack: List[Ls] = [[]] + stack: list[Ls] = [[]] def go(ex: Example) -> None: if ex.length == 0: @@ -976,7 +996,8 @@ class ConjectureRunner: # once more things are on the ir. if self.settings.backend != "hypothesis": data = self.new_conjecture_data(prefix=b"", max_length=BUFFER_SIZE) - self.test_function(data) + with suppress(BackendCannotProceed): + self.test_function(data) continue self._current_phase = "generate" @@ -1238,7 +1259,7 @@ class ConjectureRunner: def new_conjecture_data_ir( self, - ir_tree_prefix: List[IRNode], + ir_tree_prefix: list[IRNode], *, observer: Optional[DataObserver] = None, max_length: Optional[int] = None, @@ -1467,7 +1488,7 @@ class ConjectureRunner: self.__data_cache[buffer] = result return result - def passing_buffers(self, prefix: bytes = b"") -> FrozenSet[bytes]: + def passing_buffers(self, prefix: bytes = b"") -> frozenset[bytes]: """Return a collection of bytestrings which cause the test to pass. Optionally restrict this by a certain prefix, which is useful for explain mode. diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/junkdrawer.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/junkdrawer.py index 39382637db5..2c46a952239 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/junkdrawer.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/junkdrawer.py @@ -17,19 +17,15 @@ import gc import sys import time import warnings +from collections.abc import Iterable, Iterator, Sequence from random import Random from typing import ( Any, Callable, - Dict, Generic, - Iterable, - Iterator, List, Literal, Optional, - Sequence, - Tuple, TypeVar, Union, overload, @@ -54,14 +50,14 @@ def array_or_list( def replace_all( ls: Sequence[T], - replacements: Iterable[Tuple[int, int, Sequence[T]]], -) -> List[T]: + replacements: Iterable[tuple[int, int, Sequence[T]]], +) -> list[T]: """Substitute multiple replacement values into a list. Replacements is a list of (start, end, value) triples. """ - result: List[T] = [] + result: list[T] = [] prev = 0 offset = 0 for u, v, r in replacements: @@ -208,7 +204,7 @@ class LazySequenceCopy: def __init__(self, values: Sequence[int]): self.__values = values self.__len = len(values) - self.__mask: Optional[Dict[int, int]] = None + self.__mask: Optional[dict[int, int]] = None self.__popped_indices: Optional[SortedList] = None def __len__(self) -> int: @@ -434,7 +430,7 @@ def gc_cumulative_time() -> float: if hasattr(gc, "callbacks"): # CPython def gc_callback( - phase: Literal["start", "stop"], info: Dict[str, int] + phase: Literal["start", "stop"], info: dict[str, int] ) -> None: global _gc_start, _gc_cumulative_time try: diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py index d1084e4cdd4..a1a34249638 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/shrinker.py @@ -9,7 +9,7 @@ # obtain one at https://mozilla.org/MPL/2.0/. from collections import defaultdict -from typing import TYPE_CHECKING, Callable, Dict, Optional, Tuple, TypeVar, Union +from typing import TYPE_CHECKING, Callable, Optional, TypeVar, Union import attr @@ -45,7 +45,7 @@ if TYPE_CHECKING: SortKeyT = TypeVar("SortKeyT", str, bytes) -def sort_key(buffer: SortKeyT) -> Tuple[int, SortKeyT]: +def sort_key(buffer: SortKeyT) -> tuple[int, SortKeyT]: """Returns a sort key such that "simpler" buffers are smaller than "more complicated" ones. @@ -71,7 +71,7 @@ def sort_key(buffer: SortKeyT) -> Tuple[int, SortKeyT]: return (len(buffer), buffer) -SHRINK_PASS_DEFINITIONS: Dict[str, "ShrinkPassDefinition"] = {} +SHRINK_PASS_DEFINITIONS: dict[str, "ShrinkPassDefinition"] = {} @attr.s() @@ -313,7 +313,7 @@ class Shrinker: self.initial_misaligned = self.engine.misaligned_count self.calls_at_last_shrink = self.initial_calls - self.passes_by_name: Dict[str, ShrinkPass] = {} + self.passes_by_name: dict[str, ShrinkPass] = {} # Because the shrinker is also used to `pareto_optimise` in the target phase, # we sometimes want to allow extending buffers instead of aborting at the end. diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/utils.py b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/utils.py index 3724c82177d..c08cb7e094e 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/utils.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/conjecture/utils.py @@ -13,8 +13,9 @@ import hashlib import heapq import sys from collections import OrderedDict, abc +from collections.abc import Sequence from functools import lru_cache -from typing import TYPE_CHECKING, List, Optional, Sequence, Tuple, Type, TypeVar, Union +from typing import TYPE_CHECKING, List, Optional, TypeVar, Union from hypothesis.errors import InvalidArgument from hypothesis.internal.compat import int_from_bytes @@ -56,7 +57,7 @@ def identity(v: T) -> T: def check_sample( - values: Union[Type[enum.Enum], Sequence[T]], strategy_name: str + values: Union[type[enum.Enum], Sequence[T]], strategy_name: str ) -> Sequence[T]: if "numpy" in sys.modules and isinstance(values, sys.modules["numpy"].ndarray): if values.ndim != 1: @@ -103,7 +104,7 @@ class Sampler: shrinking the chosen element. """ - table: List[Tuple[int, int, float]] # (base_idx, alt_idx, alt_chance) + table: list[tuple[int, int, float]] # (base_idx, alt_idx, alt_chance) def __init__(self, weights: Sequence[float], *, observe: bool = True): self.observe = observe @@ -158,7 +159,7 @@ class Sampler: while small: table[small.pop()][2] = zero - self.table: "List[Tuple[int, int, float]]" = [] + self.table: "list[tuple[int, int, float]]" = [] for base, alternate, alternate_chance in table: # type: ignore assert isinstance(base, int) assert isinstance(alternate, int) or alternate is None diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/coverage.py b/contrib/python/hypothesis/py3/hypothesis/internal/coverage.py index 57b9f8ed048..5f219d64078 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/coverage.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/coverage.py @@ -12,7 +12,7 @@ import json import os import sys from contextlib import contextmanager -from typing import Callable, Dict, Set, Tuple, TypeVar +from typing import Callable, TypeVar from hypothesis.internal.reflection import proxies @@ -30,7 +30,7 @@ itself and has essentially no overhead. """ Func = TypeVar("Func", bound=Callable) -pretty_file_name_cache: Dict[str, str] = {} +pretty_file_name_cache: dict[str, str] = {} def pretty_file_name(f): @@ -55,7 +55,7 @@ if IN_COVERAGE_TESTS: # By this point, "branch-check" should have already been deleted by the # tox config. We can't delete it here because of #1718. - written: Set[Tuple[str, bool]] = set() + written: set[tuple[str, bool]] = set() def record_branch(name, value): key = (name, value) diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/entropy.py b/contrib/python/hypothesis/py3/hypothesis/internal/entropy.py index fe7d3f8347a..21d17c465d3 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/entropy.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/entropy.py @@ -13,8 +13,9 @@ import gc import random import sys import warnings +from collections.abc import Hashable from itertools import count -from typing import TYPE_CHECKING, Any, Callable, Hashable, Tuple +from typing import TYPE_CHECKING, Any, Callable from weakref import WeakValueDictionary import hypothesis.core @@ -147,7 +148,7 @@ def register_random(r: RandomLike) -> None: def get_seeder_and_restorer( seed: Hashable = 0, -) -> Tuple[Callable[[], None], Callable[[], None]]: +) -> tuple[Callable[[], None], Callable[[], None]]: """Return a pair of functions which respectively seed all and restore the state of all registered PRNGs. diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/escalation.py b/contrib/python/hypothesis/py3/hypothesis/internal/escalation.py index d18b2546fd7..0fa64541671 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 functools import partial from inspect import getframeinfo from pathlib import Path -from typing import Dict, NamedTuple, Optional, Tuple, Type +from typing import NamedTuple, Optional import hypothesis from hypothesis.errors import _Trimmable @@ -49,7 +49,7 @@ def belongs_to(package): return accept -FILE_CACHE: Dict[bytes, bool] = {} +FILE_CACHE: dict[bytes, bool] = {} is_hypothesis_file = belongs_to(hypothesis) @@ -91,7 +91,7 @@ class InterestingOrigin(NamedTuple): # location, but have extracted this logic in order to see through `except ...:` # blocks and understand the __cause__ (`raise x from y`) or __context__ that # first raised an exception as well as PEP-654 exception groups. - exc_type: Type[BaseException] + exc_type: type[BaseException] filename: Optional[str] lineno: Optional[int] context: "InterestingOrigin | tuple[()]" @@ -109,7 +109,7 @@ class InterestingOrigin(NamedTuple): @classmethod def from_exception( - cls, exception: BaseException, /, seen: Tuple[BaseException, ...] = () + cls, exception: BaseException, /, seen: tuple[BaseException, ...] = () ) -> "InterestingOrigin": filename, lineno = None, None if tb := get_trimmed_traceback(exception): diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/filtering.py b/contrib/python/hypothesis/py3/hypothesis/internal/filtering.py index bfe858ccfe2..f10ff8cfcd7 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/filtering.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/filtering.py @@ -26,10 +26,11 @@ import ast import inspect import math import operator +from collections.abc import Collection from decimal import Decimal from fractions import Fraction from functools import partial -from typing import Any, Callable, Collection, Dict, NamedTuple, Optional, TypeVar +from typing import Any, Callable, NamedTuple, Optional, TypeVar from hypothesis.internal.compat import ceil, floor from hypothesis.internal.floats import next_down, next_up @@ -60,7 +61,7 @@ class ConstructivePredicate(NamedTuple): for each numeric type, for strings, for bytes, for collection sizes, etc. """ - kwargs: Dict[str, Any] + kwargs: dict[str, Any] predicate: Optional[Predicate] @classmethod @@ -235,9 +236,9 @@ def get_numeric_predicate_bounds(predicate: Predicate) -> ConstructivePredicate: options = { # We're talking about op(arg, x) - the reverse of our usual intuition! operator.lt: {"min_value": arg, "exclude_min": True}, # lambda x: arg < x - operator.le: {"min_value": arg}, # lambda x: arg <= x - operator.eq: {"min_value": arg, "max_value": arg}, # lambda x: arg == x - operator.ge: {"max_value": arg}, # lambda x: arg >= x + operator.le: {"min_value": arg}, # lambda x: arg <= x + operator.eq: {"min_value": arg, "max_value": arg}, # lambda x: arg == x + operator.ge: {"max_value": arg}, # lambda x: arg >= x operator.gt: {"max_value": arg, "exclude_max": True}, # lambda x: arg > x # Special-case our default predicates for length bounds min_len: {"min_value": arg, "len": True}, diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/floats.py b/contrib/python/hypothesis/py3/hypothesis/internal/floats.py index 79e6433dca8..3ef5d6c030e 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/floats.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/floats.py @@ -11,16 +11,7 @@ import math import struct from sys import float_info -from typing import ( - TYPE_CHECKING, - Callable, - Dict, - Literal, - Optional, - SupportsFloat, - Tuple, - Union, -) +from typing import TYPE_CHECKING, Callable, Literal, Optional, SupportsFloat, Union if TYPE_CHECKING: from typing import TypeAlias @@ -35,13 +26,13 @@ Width: "TypeAlias" = Literal[16, 32, 64] # Format codes for (int, float) sized types, used for byte-wise casts. # See https://docs.python.org/3/library/struct.html#format-characters -STRUCT_FORMATS: Dict[int, Tuple[UnsignedIntFormat, FloatFormat]] = { +STRUCT_FORMATS: dict[int, tuple[UnsignedIntFormat, FloatFormat]] = { 16: ("!H", "!e"), 32: ("!I", "!f"), 64: ("!Q", "!d"), } -TO_SIGNED_FORMAT: Dict[UnsignedIntFormat, SignedIntFormat] = { +TO_SIGNED_FORMAT: dict[UnsignedIntFormat, SignedIntFormat] = { "!H": "!h", "!I": "!i", "!Q": "!q", @@ -138,7 +129,7 @@ def next_up_normal(value: float, width: int, *, allow_subnormal: bool) -> float: # Smallest positive non-zero numbers that is fully representable by an # IEEE-754 float, calculated with the width's associated minimum exponent. # Values from https://en.wikipedia.org/wiki/IEEE_754#Basic_and_interchange_formats -width_smallest_normals: Dict[int, float] = { +width_smallest_normals: dict[int, float] = { 16: 2 ** -(2 ** (5 - 1) - 2), 32: 2 ** -(2 ** (8 - 1) - 2), 64: 2 ** -(2 ** (11 - 1) - 2), diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/observability.py b/contrib/python/hypothesis/py3/hypothesis/internal/observability.py index f8b922dfcaf..2ad2d4dd5a2 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/observability.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/observability.py @@ -17,13 +17,13 @@ import time import warnings from datetime import date, timedelta from functools import lru_cache -from typing import Any, Callable, Dict, List, Optional +from typing import Any, Callable, Optional from hypothesis.configuration import storage_directory from hypothesis.errors import HypothesisWarning from hypothesis.internal.conjecture.data import ConjectureData, Status -TESTCASE_CALLBACKS: List[Callable[[dict], None]] = [] +TESTCASE_CALLBACKS: list[Callable[[dict], None]] = [] def deliver_json_blob(value: dict) -> None: @@ -39,10 +39,10 @@ def make_testcase( how_generated: str, string_repr: str = "<unknown>", arguments: Optional[dict] = None, - timing: Dict[str, float], - coverage: Optional[Dict[str, List[int]]] = None, + timing: dict[str, float], + coverage: Optional[dict[str, list[int]]] = None, phase: Optional[str] = None, - backend_metadata: Optional[Dict[str, Any]] = None, + backend_metadata: Optional[dict[str, Any]] = None, ) -> dict: if data.interesting_origin: status_reason = str(data.interesting_origin) @@ -109,9 +109,8 @@ def _system_metadata(): OBSERVABILITY_COLLECT_COVERAGE = ( "HYPOTHESIS_EXPERIMENTAL_OBSERVABILITY_NOCOVER" not in os.environ ) -if OBSERVABILITY_COLLECT_COVERAGE is False and sys.version_info[:2] >= ( - 3, - 12, +if OBSERVABILITY_COLLECT_COVERAGE is False and ( + sys.version_info[:2] >= (3, 12) ): # pragma: no cover warnings.warn( "Coverage data collection should be quite fast in Python 3.12 or later " @@ -119,9 +118,8 @@ if OBSERVABILITY_COLLECT_COVERAGE is False and sys.version_info[:2] >= ( HypothesisWarning, stacklevel=2, ) -if ( - "HYPOTHESIS_EXPERIMENTAL_OBSERVABILITY" in os.environ - or OBSERVABILITY_COLLECT_COVERAGE is False +if "HYPOTHESIS_EXPERIMENTAL_OBSERVABILITY" in os.environ or ( + OBSERVABILITY_COLLECT_COVERAGE is False ): # pragma: no cover TESTCASE_CALLBACKS.append(_deliver_to_file) diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/reflection.py b/contrib/python/hypothesis/py3/hypothesis/internal/reflection.py index b3bb52c67ee..dbc987e9c70 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/reflection.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/reflection.py @@ -21,18 +21,19 @@ import sys import textwrap import types import warnings +from collections.abc import MutableMapping from functools import partial, wraps from io import StringIO from keyword import iskeyword from random import _inst as global_random_instance from tokenize import COMMENT, detect_encoding, generate_tokens, untokenize from types import ModuleType -from typing import TYPE_CHECKING, Any, Callable, MutableMapping +from typing import TYPE_CHECKING, Any, Callable from unittest.mock import _patch as PatchType from weakref import WeakKeyDictionary from hypothesis.errors import HypothesisWarning -from hypothesis.internal.compat import PYPY, is_typed_named_tuple +from hypothesis.internal.compat import is_typed_named_tuple from hypothesis.utils.conventions import not_set from hypothesis.vendor.pretty import pretty @@ -67,14 +68,7 @@ def _clean_source(src: str) -> bytes: # lines - i.e. any decorators, so that adding `@example()`s keeps the same key. try: funcdef = ast.parse(src).body[0] - if sys.version_info[:2] == (3, 8) and PYPY: - # We can't get a line number of the (async) def here, so as a best-effort - # approximation we'll use str.split instead and hope for the best. - tag = "async def " if isinstance(funcdef, ast.AsyncFunctionDef) else "def " - if tag in src: - src = tag + src.split(tag, maxsplit=1)[1] - else: - src = "".join(src.splitlines(keepends=True)[funcdef.lineno - 1 :]) + src = "".join(src.splitlines(keepends=True)[funcdef.lineno - 1 :]) except Exception: pass # Remove blank lines and use the tokenize module to strip out comments, @@ -166,16 +160,6 @@ def get_signature( parameters=[v for k, v in sig.parameters.items() if k != "self"] ) return sig - if sys.version_info[:2] <= (3, 8) and inspect.isclass(target): - # Workaround for subclasses of typing.Generic on Python <= 3.8 - from hypothesis.strategies._internal.types import is_generic_type - - if is_generic_type(target): - sig = inspect.signature(target.__init__) - check_signature(sig) - return sig.replace( - parameters=[v for k, v in sig.parameters.items() if k != "self"] - ) # eval_str is only supported by Python 3.10 and newer if sys.version_info[:2] >= (3, 10): sig = inspect.signature( @@ -257,7 +241,7 @@ def ast_arguments_matches_signature(args, sig): assert isinstance(args, ast.arguments) assert isinstance(sig, inspect.Signature) expected = [] - for node in getattr(args, "posonlyargs", ()): # New in Python 3.8 + for node in args.posonlyargs: expected.append((node.arg, inspect.Parameter.POSITIONAL_ONLY)) for node in args.args: expected.append((node.arg, inspect.Parameter.POSITIONAL_OR_KEYWORD)) diff --git a/contrib/python/hypothesis/py3/hypothesis/internal/scrutineer.py b/contrib/python/hypothesis/py3/hypothesis/internal/scrutineer.py index 8f7aa4c5034..5586e428089 100644 --- a/contrib/python/hypothesis/py3/hypothesis/internal/scrutineer.py +++ b/contrib/python/hypothesis/py3/hypothesis/internal/scrutineer.py @@ -15,10 +15,11 @@ import subprocess import sys import types from collections import defaultdict +from collections.abc import Iterable from functools import lru_cache, reduce from os import sep from pathlib import Path -from typing import TYPE_CHECKING, Dict, Iterable, List, Optional, Set, Tuple +from typing import TYPE_CHECKING, Optional from hypothesis._settings import Phase, Verbosity from hypothesis.internal.compat import PYPY @@ -29,9 +30,9 @@ if TYPE_CHECKING: else: TypeAlias = object -Location: TypeAlias = Tuple[str, int] -Branch: TypeAlias = Tuple[Optional[Location], Location] -Trace: TypeAlias = Set[Branch] +Location: TypeAlias = tuple[str, int] +Branch: TypeAlias = tuple[Optional[Location], Location] +Trace: TypeAlias = set[Branch] @lru_cache(maxsize=None) @@ -250,16 +251,7 @@ def _get_git_repo_root() -> Path: return Path(where) -if sys.version_info[:2] <= (3, 8): - - def is_relative_to(self, other): - return other == self or other in self.parents - -else: - is_relative_to = Path.is_relative_to - - -def tractable_coverage_report(trace: Trace) -> Dict[str, List[int]]: +def tractable_coverage_report(trace: Trace) -> dict[str, list[int]]: """Report a simple coverage map which is (probably most) of the user's code.""" coverage: dict = {} t = dict(trace) @@ -272,6 +264,6 @@ def tractable_coverage_report(trace: Trace) -> Dict[str, List[int]]: k: sorted(v) for k, v in coverage.items() if stdlib_fragment not in k - and is_relative_to(p := Path(k), _get_git_repo_root()) + and (p := Path(k)).is_relative_to(_get_git_repo_root()) and "site-packages" not in p.parts } diff --git a/contrib/python/hypothesis/py3/hypothesis/provisional.py b/contrib/python/hypothesis/py3/hypothesis/provisional.py index aac93b6a0e3..06e829f3c42 100644 --- a/contrib/python/hypothesis/py3/hypothesis/provisional.py +++ b/contrib/python/hypothesis/py3/hypothesis/provisional.py @@ -19,6 +19,7 @@ definitions it links to. If not, report the bug! # https://tools.ietf.org/html/rfc3696 import string +from functools import lru_cache from importlib import resources from typing import Optional @@ -31,20 +32,17 @@ URL_SAFE_CHARACTERS = frozenset(string.ascii_letters + string.digits + "$-_.+!*' FRAGMENT_SAFE_CHARACTERS = URL_SAFE_CHARACTERS | {"?", "/"} -# This file is sourced from http://data.iana.org/TLD/tlds-alpha-by-domain.txt -# The file contains additional information about the date that it was last updated. -try: # pragma: no cover +@lru_cache(maxsize=1) +def get_top_level_domains() -> tuple[str, ...]: + # This file is sourced from http://data.iana.org/TLD/tlds-alpha-by-domain.txt + # The file contains additional information about the date that it was last updated. traversable = resources.files("hypothesis.vendor") / "tlds-alpha-by-domain.txt" _comment, *_tlds = traversable.read_text(encoding="utf-8").splitlines() -except (AttributeError, ValueError): # pragma: no cover # .files() was added in 3.9 - _comment, *_tlds = resources.read_text( - "hypothesis.vendor", "tlds-alpha-by-domain.txt", encoding="utf-8" - ).splitlines() -assert _comment.startswith("#") + assert _comment.startswith("#") -# Remove special-use domain names from the list. For more discussion -# see https://github.com/HypothesisWorks/hypothesis/pull/3572 -TOP_LEVEL_DOMAINS = ["COM", *sorted((d for d in _tlds if d != "ARPA"), key=len)] + # Remove special-use domain names from the list. For more discussion + # see https://github.com/HypothesisWorks/hypothesis/pull/3572 + return ("COM", *sorted((d for d in _tlds if d != "ARPA"), key=len)) class DomainNameStrategy(st.SearchStrategy): @@ -106,7 +104,7 @@ class DomainNameStrategy(st.SearchStrategy): # prevent us from generating at least a 1 character subdomain. # 3 - Randomize the TLD between upper and lower case characters. domain = data.draw( - st.sampled_from(TOP_LEVEL_DOMAINS) + st.sampled_from(get_top_level_domains()) .filter(lambda tld: len(tld) + 2 <= self.max_length) .flatmap( lambda tld: st.tuples( diff --git a/contrib/python/hypothesis/py3/hypothesis/stateful.py b/contrib/python/hypothesis/py3/hypothesis/stateful.py index 190d7076fd8..7c60d2752f0 100644 --- a/contrib/python/hypothesis/py3/hypothesis/stateful.py +++ b/contrib/python/hypothesis/py3/hypothesis/stateful.py @@ -17,22 +17,12 @@ execution to date. """ import collections import inspect +from collections.abc import Iterable, Sequence from copy import copy from functools import lru_cache from io import StringIO from time import perf_counter -from typing import ( - Any, - Callable, - ClassVar, - Dict, - Iterable, - List, - Optional, - Sequence, - Union, - overload, -) +from typing import Any, Callable, ClassVar, Optional, Union, overload from unittest import TestCase import attr @@ -274,17 +264,17 @@ class RuleBasedStateMachine(metaclass=StateMachineMeta): At any given point a random applicable rule will be executed. """ - _rules_per_class: ClassVar[Dict[type, List[classmethod]]] = {} - _invariants_per_class: ClassVar[Dict[type, List[classmethod]]] = {} - _initializers_per_class: ClassVar[Dict[type, List[classmethod]]] = {} + _rules_per_class: ClassVar[dict[type, list[classmethod]]] = {} + _invariants_per_class: ClassVar[dict[type, list[classmethod]]] = {} + _initializers_per_class: ClassVar[dict[type, list[classmethod]]] = {} def __init__(self) -> None: if not self.rules(): raise InvalidDefinition(f"Type {type(self).__name__} defines no rules") - self.bundles: Dict[str, list] = {} + self.bundles: dict[str, list] = {} self.names_counters: collections.Counter = collections.Counter() self.names_list: list[str] = [] - self.names_to_values: Dict[str, Any] = {} + self.names_to_values: dict[str, Any] = {} self.__stream = StringIO() self.__printer = RepresentationPrinter( self.__stream, context=_current_build_context.value @@ -651,15 +641,15 @@ _RuleWrapper = Callable[[_RuleType[Ex]], _RuleType[Ex]] # a `SearchStrategy`, which the concrete implementation does not accept. # # Omitted `targets` parameters, where the default value is used, are typed with -# a special `_OmittedArgument` type. We cannot type them as `Tuple[()]`, because -# `Tuple[()]` is a subtype of `Sequence[Bundle[Ex]]`, leading to signature +# a special `_OmittedArgument` type. We cannot type them as `tuple[()]`, because +# `tuple[()]` is a subtype of `Sequence[Bundle[Ex]]`, leading to signature # overlaps with incompatible return types. The `_OmittedArgument` type will never be # encountered at runtime, and exists solely to annotate the default of `targets`. # PEP 661 (Sentinel Values) might provide a more elegant alternative in the future. # -# We could've also annotated `targets` as `Tuple[_OmittedArgument]`, but then when +# We could've also annotated `targets` as `tuple[_OmittedArgument]`, but then when # both `target` and `targets` are provided, mypy describes the type error as an -# invalid argument type for `targets` (expected `Tuple[_OmittedArgument]`, got ...). +# invalid argument type for `targets` (expected `tuple[_OmittedArgument]`, got ...). # By annotating it as a bare `_OmittedArgument` type, mypy's error will warn that # there is no overloaded signature matching the call, which is more descriptive. # diff --git a/contrib/python/hypothesis/py3/hypothesis/statistics.py b/contrib/python/hypothesis/py3/hypothesis/statistics.py index bd3f4e3e303..cee3c2b9ef5 100644 --- a/contrib/python/hypothesis/py3/hypothesis/statistics.py +++ b/contrib/python/hypothesis/py3/hypothesis/statistics.py @@ -10,7 +10,8 @@ import math from collections import Counter -from typing import TYPE_CHECKING, Dict, Iterable, List, cast +from collections.abc import Iterable +from typing import TYPE_CHECKING, cast from hypothesis._settings import Phase from hypothesis.utils.dynamicvariables import DynamicVariable @@ -27,7 +28,7 @@ def note_statistics(stats_dict: "StatisticsDict") -> None: callback(stats_dict) -def describe_targets(best_targets: Dict[str, float]) -> List[str]: +def describe_targets(best_targets: dict[str, float]) -> list[str]: """Return a list of lines describing the results of `target`, if any.""" # These lines are included in the general statistics description below, # but also printed immediately below failing examples to alleviate the diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/collections.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/collections.py index 5d433906307..e647ec07fbe 100644 --- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/collections.py +++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/collections.py @@ -9,7 +9,8 @@ # obtain one at https://mozilla.org/MPL/2.0/. import copy -from typing import Any, Iterable, Tuple, overload +from collections.abc import Iterable +from typing import Any, overload from hypothesis.errors import InvalidArgument from hypothesis.internal.conjecture import utils as cu @@ -64,26 +65,26 @@ class TupleStrategy(SearchStrategy): @overload -def tuples() -> SearchStrategy[Tuple[()]]: # pragma: no cover +def tuples() -> SearchStrategy[tuple[()]]: # pragma: no cover ... @overload -def tuples(__a1: SearchStrategy[Ex]) -> SearchStrategy[Tuple[Ex]]: # pragma: no cover +def tuples(__a1: SearchStrategy[Ex]) -> SearchStrategy[tuple[Ex]]: # pragma: no cover ... @overload def tuples( __a1: SearchStrategy[Ex], __a2: SearchStrategy[T] -) -> SearchStrategy[Tuple[Ex, T]]: # pragma: no cover +) -> SearchStrategy[tuple[Ex, T]]: # pragma: no cover ... @overload def tuples( __a1: SearchStrategy[Ex], __a2: SearchStrategy[T], __a3: SearchStrategy[T3] -) -> SearchStrategy[Tuple[Ex, T, T3]]: # pragma: no cover +) -> SearchStrategy[tuple[Ex, T, T3]]: # pragma: no cover ... @@ -93,7 +94,7 @@ def tuples( __a2: SearchStrategy[T], __a3: SearchStrategy[T3], __a4: SearchStrategy[T4], -) -> SearchStrategy[Tuple[Ex, T, T3, T4]]: # pragma: no cover +) -> SearchStrategy[tuple[Ex, T, T3, T4]]: # pragma: no cover ... @@ -104,20 +105,20 @@ def tuples( __a3: SearchStrategy[T3], __a4: SearchStrategy[T4], __a5: SearchStrategy[T5], -) -> SearchStrategy[Tuple[Ex, T, T3, T4, T5]]: # pragma: no cover +) -> SearchStrategy[tuple[Ex, T, T3, T4, T5]]: # pragma: no cover ... @overload def tuples( *args: SearchStrategy[Any], -) -> SearchStrategy[Tuple[Any, ...]]: # pragma: no cover +) -> SearchStrategy[tuple[Any, ...]]: # pragma: no cover ... @cacheable @defines_strategy() -def tuples(*args: SearchStrategy[Any]) -> SearchStrategy[Tuple[Any, ...]]: +def tuples(*args: SearchStrategy[Any]) -> SearchStrategy[tuple[Any, ...]]: """Return a strategy which generates a tuple of the same length as args by generating the value at index i from args[i]. diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py index 7fdeedb4976..e652116ed5c 100644 --- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py +++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/core.py @@ -18,30 +18,22 @@ import string import sys import typing import warnings +from collections.abc import Collection, Hashable, Iterable, Sequence from contextvars import ContextVar from decimal import Context, Decimal, localcontext from fractions import Fraction from functools import reduce from inspect import Parameter, Signature, isabstract, isclass -from types import FunctionType +from re import Pattern +from types import FunctionType, GenericAlias from typing import ( + Annotated, Any, AnyStr, Callable, - Collection, - Dict, - FrozenSet, - Hashable, - Iterable, - List, Literal, Optional, - Pattern, Protocol, - Sequence, - Set, - Tuple, - Type, TypeVar, Union, get_args, @@ -180,21 +172,21 @@ def sampled_from(elements: Sequence[T]) -> SearchStrategy[T]: # pragma: no cove @overload -def sampled_from(elements: Type[enum.Enum]) -> SearchStrategy[Any]: # pragma: no cover +def sampled_from(elements: type[enum.Enum]) -> SearchStrategy[Any]: # pragma: no cover # `SearchStrategy[Enum]` is unreliable due to metaclass issues. ... @overload def sampled_from( - elements: Union[Type[enum.Enum], Sequence[Any]] + elements: Union[type[enum.Enum], Sequence[Any]] ) -> SearchStrategy[Any]: # pragma: no cover ... @defines_strategy(try_non_lazy=True) def sampled_from( - elements: Union[Type[enum.Enum], Sequence[Any]] + elements: Union[type[enum.Enum], Sequence[Any]] ) -> SearchStrategy[Any]: """Returns a strategy which generates any value present in ``elements``. @@ -285,10 +277,10 @@ def lists( unique_by: Union[ None, Callable[[Ex], Hashable], - Tuple[Callable[[Ex], Hashable], ...], + tuple[Callable[[Ex], Hashable], ...], ] = None, unique: bool = False, -) -> SearchStrategy[List[Ex]]: +) -> SearchStrategy[list[Ex]]: """Returns a list containing values drawn from elements with length in the interval [min_size, max_size] (no bounds in that direction if these are None). If max_size is 0, only the empty list will be drawn. @@ -419,7 +411,7 @@ def sets( *, min_size: int = 0, max_size: Optional[int] = None, -) -> SearchStrategy[Set[Ex]]: +) -> SearchStrategy[set[Ex]]: """This has the same behaviour as lists, but returns sets instead. Note that Hypothesis cannot tell if values are drawn from elements @@ -441,7 +433,7 @@ def frozensets( *, min_size: int = 0, max_size: Optional[int] = None, -) -> SearchStrategy[FrozenSet[Ex]]: +) -> SearchStrategy[frozenset[Ex]]: """This is identical to the sets function but instead returns frozensets.""" return lists( @@ -473,7 +465,7 @@ def iterables( unique_by: Union[ None, Callable[[Ex], Hashable], - Tuple[Callable[[Ex], Hashable], ...], + tuple[Callable[[Ex], Hashable], ...], ] = None, unique: bool = False, ) -> SearchStrategy[Iterable[Ex]]: @@ -495,10 +487,10 @@ def iterables( @defines_strategy() def fixed_dictionaries( - mapping: Dict[T, SearchStrategy[Ex]], + mapping: dict[T, SearchStrategy[Ex]], *, - optional: Optional[Dict[T, SearchStrategy[Ex]]] = None, -) -> SearchStrategy[Dict[T, Ex]]: + optional: Optional[dict[T, SearchStrategy[Ex]]] = None, +) -> SearchStrategy[dict[T, Ex]]: """Generates a dictionary of the same type as mapping with a fixed set of keys mapping to strategies. ``mapping`` must be a dict subclass. @@ -542,7 +534,7 @@ def dictionaries( dict_class: type = dict, min_size: int = 0, max_size: Optional[int] = None, -) -> SearchStrategy[Dict[Ex, T]]: +) -> SearchStrategy[dict[Ex, T]]: # Describing the exact dict_class to Mypy drops the key and value types, # so we report Dict[K, V] instead of Mapping[Any, Any] for now. Sorry! """Generates dictionaries of type ``dict_class`` with keys drawn from the ``keys`` @@ -1139,7 +1131,7 @@ def builds( @cacheable @defines_strategy(never_lazy=True) -def from_type(thing: Type[Ex_Inv]) -> SearchStrategy[Ex_Inv]: +def from_type(thing: type[Ex_Inv]) -> SearchStrategy[Ex_Inv]: """Looks up the appropriate search strategy for the given type. ``from_type`` is used internally to fill in missing arguments to @@ -1199,7 +1191,7 @@ def from_type(thing: Type[Ex_Inv]) -> SearchStrategy[Ex_Inv]: return _from_type_deferred(thing) -def _from_type_deferred(thing: Type[Ex]) -> SearchStrategy[Ex]: +def _from_type_deferred(thing: type[Ex]) -> SearchStrategy[Ex]: # This tricky little dance is because we want to show the repr of the actual # underlying strategy wherever possible, as a form of user education, but # would prefer to fall back to the default "from_type(...)" repr instead of @@ -1224,7 +1216,7 @@ def _from_type_deferred(thing: Type[Ex]) -> SearchStrategy[Ex]: _recurse_guard: ContextVar = ContextVar("recurse_guard") -def _from_type(thing: Type[Ex]) -> SearchStrategy[Ex]: +def _from_type(thing: type[Ex]) -> SearchStrategy[Ex]: # TODO: We would like to move this to the top level, but pending some major # refactoring it's hard to do without creating circular imports. from hypothesis.strategies._internal import types @@ -1335,10 +1327,19 @@ def _from_type(thing: Type[Ex]) -> SearchStrategy[Ex]: strategy = as_strategy(types._global_type_lookup[thing], thing) if strategy is not NotImplemented: return strategy + elif ( + isinstance(thing, GenericAlias) + and (to := get_origin(thing)) in types._global_type_lookup + ): + strategy = as_strategy(types._global_type_lookup[to], thing) + if strategy is not NotImplemented: + return strategy except TypeError: # pragma: no cover - # This is due to a bizarre divergence in behaviour under Python 3.9.0: + # This was originally due to a bizarre divergence in behaviour on Python 3.9.0: # typing.Callable[[], foo] has __args__ = (foo,) but collections.abc.Callable # has __args__ = ([], foo); and as a result is non-hashable. + # We've kept it because we turn out to have more type errors from... somewhere. + # FIXME: investigate that, maybe it should be fixed more precisely? pass if ( hasattr(typing, "_TypedDictMeta") @@ -1360,7 +1361,7 @@ def _from_type(thing: Type[Ex]) -> SearchStrategy[Ex]: qualifiers = [] while True: annotation_origin = types.extended_get_origin(annotation_type) - if annotation_origin in types.AnnotatedTypes: + if annotation_origin is Annotated: if annotation_args := get_args(annotation_type): annotation_type = annotation_args[0] else: @@ -1425,9 +1426,7 @@ def _from_type(thing: Type[Ex]) -> SearchStrategy[Ex]: # because there are several special cases that don't play well with # subclass and instance checks. if isinstance(thing, types.typing_root_type) or ( - sys.version_info[:2] >= (3, 9) - and isinstance(get_origin(thing), type) - and get_args(thing) + isinstance(get_origin(thing), type) and get_args(thing) ): return types.from_typing_type(thing) # If it's not from the typing module, we get all registered types that are @@ -1703,7 +1702,7 @@ 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] = [] + 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): @@ -1761,7 +1760,7 @@ class PermutationStrategy(SearchStrategy): @defines_strategy() -def permutations(values: Sequence[T]) -> SearchStrategy[List[T]]: +def permutations(values: Sequence[T]) -> SearchStrategy[list[T]]: """Return a strategy which returns permutations of the ordered collection ``values``. @@ -1795,7 +1794,7 @@ class DrawFn(Protocol): .. code-block:: python @composite - def list_and_index(draw: DrawFn) -> Tuple[int, str]: + def list_and_index(draw: DrawFn) -> tuple[int, str]: i = draw(integers()) # type inferred as 'int' s = draw(text()) # type inferred as 'str' return i, s @@ -2217,8 +2216,8 @@ def data() -> SearchStrategy[DataObject]: def register_type_strategy( - custom_type: Type[Ex], - strategy: Union[SearchStrategy[Ex], Callable[[Type[Ex]], SearchStrategy[Ex]]], + custom_type: type[Ex], + strategy: Union[SearchStrategy[Ex], Callable[[type[Ex]], SearchStrategy[Ex]]], ) -> None: """Add an entry to the global type-to-strategy lookup. diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/datetime.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/datetime.py index 427d8c5ed24..12f5154ad2f 100644 --- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/datetime.py +++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/datetime.py @@ -9,8 +9,10 @@ # obtain one at https://mozilla.org/MPL/2.0/. import datetime as dt +import operator as op +import zoneinfo from calendar import monthrange -from functools import lru_cache +from functools import lru_cache, partial from importlib import resources from pathlib import Path from typing import Optional @@ -18,22 +20,10 @@ from typing import Optional from hypothesis.errors import InvalidArgument 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 +from hypothesis.strategies._internal.misc import just, none, nothing from hypothesis.strategies._internal.strategies import SearchStrategy from hypothesis.strategies._internal.utils import defines_strategy -# The zoneinfo module, required for the timezones() and timezone_keys() -# strategies, is new in Python 3.9 and the backport might be missing. -try: - import zoneinfo -except ImportError: - try: - from backports import zoneinfo # type: ignore - except ImportError: - # We raise an error recommending `pip install hypothesis[zoneinfo]` - # when timezones() or timezone_keys() strategies are actually used. - zoneinfo = None # type: ignore - DATENAMES = ("year", "month", "day") TIMENAMES = ("hour", "minute", "second", "microsecond") @@ -189,8 +179,7 @@ def datetimes( You can construct your own, though we recommend using one of these built-in strategies: - * with Python 3.9 or newer or :pypi:`backports.zoneinfo`: - :func:`hypothesis.strategies.timezones`; + * with the standard library: :func:`hypothesis.strategies.timezones`; * with :pypi:`dateutil <python-dateutil>`: :func:`hypothesis.extra.dateutil.timezones`; or * with :pypi:`pytz`: :func:`hypothesis.extra.pytz.timezones`. @@ -279,6 +268,37 @@ class DateStrategy(SearchStrategy): **draw_capped_multipart(data, self.min_value, self.max_value, DATENAMES) ) + def filter(self, condition): + if ( + isinstance(condition, partial) + and len(args := condition.args) == 1 + and not condition.keywords + and isinstance(arg := args[0], dt.date) + and condition.func in (op.lt, op.le, op.eq, op.ge, op.gt) + ): + try: + arg += dt.timedelta(days={op.lt: 1, op.gt: -1}.get(condition.func, 0)) + except OverflowError: # gt date.max, or lt date.min + return nothing() + lo, hi = { + # We're talking about op(arg, x) - the reverse of our usual intuition! + op.lt: (arg, self.max_value), # lambda x: arg < x + op.le: (arg, self.max_value), # lambda x: arg <= x + op.eq: (arg, arg), # lambda x: arg == x + op.ge: (self.min_value, arg), # lambda x: arg >= x + op.gt: (self.min_value, arg), # lambda x: arg > x + }[condition.func] + lo = max(lo, self.min_value) + hi = min(hi, self.max_value) + print(lo, hi) + if hi < lo: + return nothing() + if lo <= self.min_value and self.max_value <= hi: + return self + return dates(lo, hi) + + return super().filter(condition) + @defines_strategy(force_reusable_values=True) def dates( @@ -353,12 +373,7 @@ def _valid_key_cacheable(tzpath, key): *package_loc, resource_name = key.split("/") package = "tzdata.zoneinfo." + ".".join(package_loc) try: - try: - traversable = resources.files(package) / resource_name - return traversable.exists() - except (AttributeError, ValueError): - # .files() was added in Python 3.9 - return resources.is_resource(package, resource_name) + return (resources.files(package) / resource_name).exists() except ModuleNotFoundError: return False @@ -388,14 +403,9 @@ def timezone_keys( .. note:: - The :mod:`python:zoneinfo` module is new in Python 3.9, so you will need - to install the :pypi:`backports.zoneinfo` module on earlier versions. - - `On Windows, you will also need to install the tzdata package + `The tzdata package is required on Windows <https://docs.python.org/3/library/zoneinfo.html#data-sources>`__. - - ``pip install hypothesis[zoneinfo]`` will install these conditional - dependencies if and only if they are needed. + ``pip install hypothesis[zoneinfo]`` installs it, if and only if needed. On Windows, you may need to access IANA timezone data via the :pypi:`tzdata` package. For non-IANA timezones, such as Windows-native names or GNU TZ @@ -406,11 +416,6 @@ def timezone_keys( # check_type(bool, allow_alias, "allow_alias") # check_type(bool, allow_deprecated, "allow_deprecated") check_type(bool, allow_prefix, "allow_prefix") - if zoneinfo is None: # pragma: no cover - raise ModuleNotFoundError( - "The zoneinfo module is required, but could not be imported. " - "Run `pip install hypothesis[zoneinfo]` and try again." - ) available_timezones = ("UTC", *sorted(zoneinfo.available_timezones())) @@ -448,21 +453,11 @@ def timezones(*, no_cache: bool = False) -> SearchStrategy["zoneinfo.ZoneInfo"]: .. note:: - The :mod:`python:zoneinfo` module is new in Python 3.9, so you will need - to install the :pypi:`backports.zoneinfo` module on earlier versions. - - `On Windows, you will also need to install the tzdata package + `The tzdata package is required on Windows <https://docs.python.org/3/library/zoneinfo.html#data-sources>`__. - - ``pip install hypothesis[zoneinfo]`` will install these conditional - dependencies if and only if they are needed. + ``pip install hypothesis[zoneinfo]`` installs it, if and only if needed. """ check_type(bool, no_cache, "no_cache") - if zoneinfo is None: # pragma: no cover - raise ModuleNotFoundError( - "The zoneinfo module is required, but could not be imported. " - "Run `pip install hypothesis[zoneinfo]` and try again." - ) return timezone_keys().map( zoneinfo.ZoneInfo.no_cache if no_cache else zoneinfo.ZoneInfo ) diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/lazy.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/lazy.py index 8f887293e62..6cb582c5e03 100644 --- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/lazy.py +++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/lazy.py @@ -8,8 +8,8 @@ # 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/. +from collections.abc import MutableMapping from inspect import signature -from typing import MutableMapping from weakref import WeakKeyDictionary from hypothesis.configuration import check_sideeffect_during_initialization diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/random.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/random.py index 30de91e2342..0e874594430 100644 --- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/random.py +++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/random.py @@ -11,7 +11,7 @@ import inspect import math from random import Random -from typing import Any, Dict +from typing import Any import attr @@ -110,7 +110,7 @@ def _randbelow(self, n: int) -> int: # type: ignore STUBS = {f.__name__: f for f in [getrandbits, random, _randbelow]} -SIGNATURES: Dict[str, inspect.Signature] = {} +SIGNATURES: dict[str, inspect.Signature] = {} def sig_of(name): diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strategies.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strategies.py index dc7a129d067..c040f748a5e 100644 --- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strategies.py +++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/strategies.py @@ -11,6 +11,7 @@ import sys import warnings from collections import abc, defaultdict +from collections.abc import Sequence from functools import lru_cache from random import shuffle from typing import ( @@ -18,10 +19,7 @@ from typing import ( Any, Callable, ClassVar, - Dict, Generic, - List, - Sequence, TypeVar, Union, cast, @@ -327,7 +325,7 @@ class SearchStrategy(Generic[Ex]): try: return self.__examples.pop() except (AttributeError, IndexError): - self.__examples: List[Ex] = [] + self.__examples: list[Ex] = [] from hypothesis.core import given @@ -395,7 +393,7 @@ class SearchStrategy(Generic[Ex]): return FilteredStrategy(conditions=(condition,), strategy=self) @property - def branches(self) -> List["SearchStrategy[Ex]"]: + def branches(self) -> list["SearchStrategy[Ex]"]: return [self] def __or__(self, other: "SearchStrategy[T]") -> "SearchStrategy[Union[Ex, T]]": @@ -432,7 +430,7 @@ class SearchStrategy(Generic[Ex]): self.validate_called = False raise - LABELS: ClassVar[Dict[type, int]] = {} + LABELS: ClassVar[dict[type, int]] = {} @property def class_label(self): @@ -850,7 +848,7 @@ class MappedStrategy(SearchStrategy[Ex]): raise UnsatisfiedAssumption @property - def branches(self) -> List[SearchStrategy[Ex]]: + def branches(self) -> list[SearchStrategy[Ex]]: return [ MappedStrategy(strategy, pack=self.pack) for strategy in self.mapped_strategy.branches @@ -1025,7 +1023,7 @@ class FilteredStrategy(SearchStrategy[Ex]): return filter_not_satisfied @property - def branches(self) -> List[SearchStrategy[Ex]]: + def branches(self) -> list[SearchStrategy[Ex]]: return [ FilteredStrategy(strategy=strategy, conditions=self.flat_conditions) for strategy in self.filtered_strategy.branches diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/types.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/types.py index 7288d8b73a3..6dea780c95e 100644 --- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/types.py +++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/types.py @@ -27,10 +27,12 @@ import sys import typing import uuid import warnings +import zoneinfo +from collections.abc import Iterator from functools import partial from pathlib import PurePath from types import FunctionType -from typing import TYPE_CHECKING, Any, Iterator, Tuple, get_args, get_origin +from typing import TYPE_CHECKING, Any, get_args, get_origin from hypothesis import strategies as st from hypothesis.errors import HypothesisWarning, InvalidArgument, ResolutionFailed @@ -38,7 +40,6 @@ from hypothesis.internal.compat import PYPY, BaseExceptionGroup, ExceptionGroup from hypothesis.internal.conjecture.utils import many as conjecture_utils_many from hypothesis.internal.filtering import max_len, min_len from hypothesis.internal.reflection import get_pretty_function_description -from hypothesis.strategies._internal.datetime import zoneinfo # type: ignore from hypothesis.strategies._internal.ipaddress import ( SPECIAL_IPv4_RANGES, SPECIAL_IPv6_RANGES, @@ -72,28 +73,6 @@ except ImportError: except ImportError: _AnnotatedAlias = () -TypeAliasTypes: tuple = () -try: - TypeAliasTypes += (typing.TypeAlias,) -except AttributeError: # pragma: no cover - pass # Is missing for `python<3.10` -try: - TypeAliasTypes += (typing_extensions.TypeAlias,) -except AttributeError: # pragma: no cover - pass # Is missing for `typing_extensions<3.10` - -ClassVarTypes: tuple = (typing.ClassVar,) -try: - ClassVarTypes += (typing_extensions.ClassVar,) -except AttributeError: # pragma: no cover - pass # `typing_extensions` might not be installed - -FinalTypes: tuple = (typing.Final,) -try: - FinalTypes += (typing_extensions.Final,) -except AttributeError: # pragma: no cover - pass # `typing_extensions` might not be installed - ConcatenateTypes: tuple = () try: ConcatenateTypes += (typing.Concatenate,) @@ -162,17 +141,6 @@ except AttributeError: # pragma: no cover pass # `typing_extensions` might not be installed -AnnotatedTypes: tuple = () -try: - AnnotatedTypes += (typing.Annotated,) -except AttributeError: # pragma: no cover - pass # Is missing for `python<3.9` -try: - AnnotatedTypes += (typing_extensions.Annotated,) -except AttributeError: # pragma: no cover - pass # `typing_extensions` might not be installed - - LiteralStringTypes: tuple = () try: LiteralStringTypes += (typing.LiteralString,) # type: ignore @@ -209,21 +177,21 @@ typing_root_type = (typing._Final, typing._GenericAlias) # type: ignore # `Final` is a great example: it just indicates that this value can't be reassigned. NON_RUNTIME_TYPES = ( typing.Any, - *ClassVarTypes, - *TypeAliasTypes, - *FinalTypes, + typing.Annotated, *ConcatenateTypes, *ParamSpecTypes, *TypeGuardTypes, ) for name in ( - "Annotated", + "ClassVar", + "Final", "NoReturn", "Self", "Required", "NotRequired", "ReadOnly", "Never", + "TypeAlias", "TypeVarTuple", "Unpack", ): @@ -343,7 +311,7 @@ def get_constraints_filter_map(): return {} # pragma: no cover -def _get_constraints(args: Tuple[Any, ...]) -> Iterator["at.BaseMetadata"]: +def _get_constraints(args: tuple[Any, ...]) -> Iterator["at.BaseMetadata"]: at = sys.modules.get("annotated_types") for arg in args: if at and isinstance(arg, at.BaseMetadata): @@ -487,20 +455,8 @@ def from_typing_type(thing): else: literals.append(arg) return st.sampled_from(literals) - if is_annotated_type(thing): # pragma: no cover - # This requires Python 3.9+ or the typing_extensions package - annotated_strategy = find_annotated_strategy(thing) - if annotated_strategy is not None: - return annotated_strategy - args = thing.__args__ - assert args, "it's impossible to make an annotated type with no args" - annotated_type = args[0] - return st.from_type(annotated_type) - # Now, confirm that we're dealing with a generic type as we expected - if sys.version_info[:2] < (3, 9) and not isinstance( - thing, typing_root_type - ): # pragma: no cover - raise ResolutionFailed(f"Cannot resolve {thing} to a strategy") + if is_annotated_type(thing): + return find_annotated_strategy(thing) # Some "generic" classes are not generic *in* anything - for example both # Hashable and Sized have `__args__ == ()` @@ -513,13 +469,22 @@ def from_typing_type(thing): # Parametrised generic types have their __origin__ attribute set to the # un-parametrised version, which we need to use in the subclass checks. - # e.g.: typing.List[int].__origin__ == typing.List - # (actually not sure if this is true since Python 3.9 or so) + # i.e.: typing.List[int].__origin__ == list mapping = { k: v for k, v in _global_type_lookup.items() if is_generic_type(k) and try_issubclass(k, thing) } + + # Discard any type which is not it's own origin, where the origin is also in the + # mapping. On old Python versions this could be due to redefinition of types + # between collections.abc and typing, but the logic seems reasonable to keep in + # case of similar situations now that's been fixed. + for t in sorted(mapping, key=type_sorting_key): + origin = get_origin(t) + if origin is not t and origin in mapping: + mapping.pop(t) + # Drop some unusual cases for simplicity, including tuples or its # subclasses (e.g. namedtuple) if len(mapping) > 1: @@ -528,24 +493,21 @@ def from_typing_type(thing): tuple_types = [ t for t in mapping - if (isinstance(t, type) and issubclass(t, tuple)) or t is typing.Tuple + if (isinstance(t, type) and issubclass(t, tuple)) or get_origin(t) is tuple ] if len(mapping) > len(tuple_types): for tuple_type in tuple_types: mapping.pop(tuple_type) - # After we drop Python 3.8 and can rely on having generic builtin types, we'll - # be able to simplify this logic by dropping the typing-module handling. - if {dict, set, typing.Dict, typing.Set}.intersection(mapping): + if {dict, set}.intersection(mapping): # ItemsView can cause test_lookup.py::test_specialised_collection_types # to fail, due to weird isinstance behaviour around the elements. mapping.pop(collections.abc.ItemsView, None) mapping.pop(typing.ItemsView, None) - if {collections.deque, typing.Deque}.intersection(mapping) and len(mapping) > 1: + if {collections.deque}.intersection(mapping) and len(mapping) > 1: # Resolving generic sequences to include a deque is more trouble for e.g. # the ghostwriter than it's worth, via undefined names in the repr. mapping.pop(collections.deque, None) - mapping.pop(typing.Deque, None) if len(mapping) > 1: # issubclass treats bytestring as a kind of sequence, which it is, @@ -569,20 +531,12 @@ def from_typing_type(thing): mapping.pop(bytes, None) if sys.version_info[:2] <= (3, 13): mapping.pop(collections.abc.ByteString, None) - mapping.pop(typing.ByteString, None) elif ( (not mapping) and isinstance(thing, typing.ForwardRef) and thing.__forward_arg__ in vars(builtins) ): return st.from_type(getattr(builtins, thing.__forward_arg__)) - # Before Python 3.9, we sometimes have e.g. Sequence from both the typing - # module, and collections.abc module. Discard any type which is not it's own - # origin, where the origin is also in the mapping. - for t in sorted(mapping, key=type_sorting_key): - origin = get_origin(t) - if origin is not t and origin in mapping: - mapping.pop(t) # Sort strategies according to our type-sorting heuristic for stable output strategies = [ s @@ -637,7 +591,7 @@ utc_offsets = st.builds( # exposed for it, and NotImplemented itself is typed as Any so that it can be # returned without being listed in a function signature: # https://github.com/python/mypy/issues/6710#issuecomment-485580032 -_global_type_lookup: typing.Dict[ +_global_type_lookup: dict[ type, typing.Union[st.SearchStrategy, typing.Callable[[type], st.SearchStrategy]] ] = { type(None): st.none(), @@ -737,21 +691,19 @@ _global_type_lookup: typing.Dict[ re.Match: st.text().map(lambda c: re.match(".", c, flags=re.DOTALL)).filter(bool), re.Pattern: st.builds(re.compile, st.sampled_from(["", b""])), random.Random: st.randoms(), + zoneinfo.ZoneInfo: st.timezones(), # Pull requests with more types welcome! } -if zoneinfo is not None: # pragma: no branch - _global_type_lookup[zoneinfo.ZoneInfo] = st.timezones() if PYPY: _global_type_lookup[builtins.sequenceiterator] = st.builds(iter, st.tuples()) # type: ignore -_global_type_lookup[type] = st.sampled_from( - [type(None), *sorted(_global_type_lookup, key=str)] +_fallback_type_strategy = st.sampled_from( + sorted(_global_type_lookup, key=type_sorting_key) ) -if sys.version_info[:2] >= (3, 9): - # subclass of MutableMapping, and in Python 3.9 we resolve to a union - # which includes this... but we don't actually ever want to build one. - _global_type_lookup[os._Environ] = st.just(os.environ) +# subclass of MutableMapping, and so we resolve to a union which +# includes this... but we don't actually ever want to build one. +_global_type_lookup[os._Environ] = st.just(os.environ) if sys.version_info[:2] <= (3, 13): # Note: while ByteString notionally also represents the bytearray and @@ -823,15 +775,15 @@ _global_type_lookup.update( # installed. To avoid the performance hit of importing anything here, we defer # it until the method is called the first time, at which point we replace the # entry in the lookup table with the direct call. -def _from_numpy_type(thing: typing.Type) -> typing.Optional[st.SearchStrategy]: +def _from_numpy_type(thing: type) -> typing.Optional[st.SearchStrategy]: from hypothesis.extra.numpy import _from_type _global_extra_lookup["numpy"] = _from_type return _from_type(thing) -_global_extra_lookup: typing.Dict[ - str, typing.Callable[[typing.Type], typing.Optional[st.SearchStrategy]] +_global_extra_lookup: dict[ + str, typing.Callable[[type], typing.Optional[st.SearchStrategy]] ] = { "numpy": _from_numpy_type, } @@ -840,7 +792,9 @@ _global_extra_lookup: typing.Dict[ def register(type_, fallback=None, *, module=typing): if isinstance(type_, str): # Use the name of generic types which are not available on all - # versions, and the function just won't be added to the registry + # versions, and the function just won't be added to the registry; + # also works when module=None because typing_extensions isn't + # installed (nocover because it _is_ in our coverage tests). type_ = getattr(module, type_, None) if type_ is None: # pragma: no cover return lambda f: f @@ -853,37 +807,34 @@ def register(type_, fallback=None, *, module=typing): @functools.wraps(func) def really_inner(thing): - # This branch is for Python <= 3.8, when __args__ was not always tracked if getattr(thing, "__args__", None) is None: - return fallback # pragma: no cover + return fallback return func(thing) - if sys.version_info[:2] >= (3, 9): - try: - type_ = get_origin(type_) - except Exception: - pass _global_type_lookup[type_] = really_inner + _global_type_lookup[get_origin(type_) or type_] = really_inner return really_inner return inner -@register(typing.Type) +@register(type) +@register("Type") @register("Type", module=typing_extensions) def resolve_Type(thing): if getattr(thing, "__args__", None) is None: - # This branch is for Python <= 3.8, when __args__ was not always tracked - return st.just(type) # pragma: no cover + return st.just(type) + elif get_args(thing) == (): # pragma: no cover + return _fallback_type_strategy args = (thing.__args__[0],) if is_a_union(args[0]): args = args[0].__args__ # Duplicate check from from_type here - only paying when needed. args = list(args) for i, a in enumerate(args): - if type(a) == typing.ForwardRef: + if type(a) in (typing.ForwardRef, str): try: - args[i] = getattr(builtins, a.__forward_arg__) + args[i] = getattr(builtins, getattr(a, "__forward_arg__", a)) except AttributeError: raise ResolutionFailed( f"Cannot find the type referenced by {thing} - try using " @@ -892,12 +843,12 @@ def resolve_Type(thing): return st.sampled_from(sorted(args, key=type_sorting_key)) -@register(typing.List, st.builds(list)) +@register("List", st.builds(list)) def resolve_List(thing): return st.lists(st.from_type(thing.__args__[0])) -@register(typing.Tuple, st.builds(tuple)) +@register("Tuple", st.builds(tuple)) def resolve_Tuple(thing): elem_types = getattr(thing, "__args__", None) or () if len(elem_types) == 2 and elem_types[-1] is Ellipsis: @@ -931,27 +882,28 @@ def _from_hashable_type(type_): return st.from_type(type_).filter(_can_hash) -@register(typing.Set, st.builds(set)) +@register("Set", st.builds(set)) @register(typing.MutableSet, st.builds(set)) def resolve_Set(thing): return st.sets(_from_hashable_type(thing.__args__[0])) -@register(typing.FrozenSet, st.builds(frozenset)) +@register("FrozenSet", st.builds(frozenset)) def resolve_FrozenSet(thing): return st.frozensets(_from_hashable_type(thing.__args__[0])) -@register(typing.Dict, st.builds(dict)) +@register("Dict", st.builds(dict)) def resolve_Dict(thing): # If thing is a Collection instance, we need to fill in the values - keys_vals = thing.__args__ * 2 + keys, vals, *_ = thing.__args__ * 2 return st.dictionaries( - _from_hashable_type(keys_vals[0]), st.from_type(keys_vals[1]) + _from_hashable_type(keys), + st.none() if vals is None else st.from_type(vals), ) -@register(typing.DefaultDict, st.builds(collections.defaultdict)) +@register("DefaultDict", st.builds(collections.defaultdict)) @register("DefaultDict", st.builds(collections.defaultdict), module=typing_extensions) def resolve_DefaultDict(thing): return resolve_Dict(thing).map(lambda d: collections.defaultdict(None, d)) @@ -1007,15 +959,15 @@ def resolve_OrderedDict(thing): @register(typing.Pattern, st.builds(re.compile, st.sampled_from(["", b""]))) def resolve_Pattern(thing): if isinstance(thing.__args__[0], typing.TypeVar): # pragma: no cover - # TODO: this was covered on Python 3.8, but isn't on 3.10 - we should + # FIXME: this was covered on Python 3.8, but isn't on 3.10 - we should # work out why not and write some extra tests to help avoid regressions. return st.builds(re.compile, st.sampled_from(["", b""])) return st.just(re.compile(thing.__args__[0]())) -@register( # pragma: no branch # coverage does not see lambda->exit branch +@register( typing.Match, - st.text().map(lambda c: re.match(".", c, flags=re.DOTALL)).filter(bool), + st.text().map(partial(re.match, ".", flags=re.DOTALL)).filter(bool), ) def resolve_Match(thing): if thing.__args__[0] == bytes: diff --git a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/utils.py b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/utils.py index d60eabea9b5..8fb6ff7e72a 100644 --- a/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/utils.py +++ b/contrib/python/hypothesis/py3/hypothesis/strategies/_internal/utils.py @@ -11,7 +11,7 @@ import sys import threading from inspect import signature -from typing import TYPE_CHECKING, Callable, Dict +from typing import TYPE_CHECKING, Callable import attr @@ -25,7 +25,7 @@ from hypothesis.vendor.pretty import pretty if TYPE_CHECKING: from hypothesis.strategies._internal.strategies import SearchStrategy, T -_strategies: Dict[str, Callable[..., "SearchStrategy"]] = {} +_strategies: dict[str, Callable[..., "SearchStrategy"]] = {} class FloatKey: diff --git a/contrib/python/hypothesis/py3/hypothesis/utils/dynamicvariables.py b/contrib/python/hypothesis/py3/hypothesis/utils/dynamicvariables.py index cb823628b2b..11b5b899583 100644 --- a/contrib/python/hypothesis/py3/hypothesis/utils/dynamicvariables.py +++ b/contrib/python/hypothesis/py3/hypothesis/utils/dynamicvariables.py @@ -9,8 +9,9 @@ # obtain one at https://mozilla.org/MPL/2.0/. import threading +from collections.abc import Generator from contextlib import contextmanager -from typing import Generator, Generic, TypeVar +from typing import Generic, TypeVar T = TypeVar("T") diff --git a/contrib/python/hypothesis/py3/hypothesis/vendor/pretty.py b/contrib/python/hypothesis/py3/hypothesis/vendor/pretty.py index 9c092de367b..2fad6e1ff09 100644 --- a/contrib/python/hypothesis/py3/hypothesis/vendor/pretty.py +++ b/contrib/python/hypothesis/py3/hypothesis/vendor/pretty.py @@ -214,6 +214,24 @@ class RepresentationPrinter: meth = cls._repr_pretty_ if callable(meth): return meth(obj, self, cycle) + if hasattr(cls, "__attrs_attrs__"): + return pprint_fields( + obj, + self, + cycle, + [at.name for at in cls.__attrs_attrs__ if at.init], + ) + if hasattr(cls, "__dataclass_fields__"): + return pprint_fields( + obj, + self, + cycle, + [ + k + for k, v in cls.__dataclass_fields__.items() + if v.init + ], + ) # Now check for object-specific printers which show how this # object was constructed (a Hypothesis special feature). printers = self.known_object_printers[IDKey(obj)] @@ -693,10 +711,6 @@ def _type_pprint(obj, p, cycle): mod = _safe_getattr(obj, "__module__", None) try: name = obj.__qualname__ - if not isinstance(name, str): # pragma: no cover - # This can happen if the type implements __qualname__ as a property - # or other descriptor in Python 2. - raise Exception("Try __name__") except Exception: # pragma: no cover name = obj.__name__ if not isinstance(name, str): @@ -718,6 +732,20 @@ def _repr_pprint(obj, p, cycle): p.text(output_line) +def pprint_fields(obj, p, cycle, fields): + name = obj.__class__.__name__ + if cycle: + return p.text(f"{name}(...)") + with p.group(1, name + "(", ")"): + for idx, field in enumerate(fields): + if idx: + p.text(",") + p.breakable() + p.text(field) + p.text("=") + p.pretty(getattr(obj, field)) + + def _function_pprint(obj, p, cycle): """Base pprint for all functions and builtin functions.""" from hypothesis.internal.reflection import get_pretty_function_description diff --git a/contrib/python/hypothesis/py3/hypothesis/version.py b/contrib/python/hypothesis/py3/hypothesis/version.py index 11ed8649776..0624381d901 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, 112, 5) +__version_info__ = (6, 115, 1) __version__ = ".".join(map(str, __version_info__)) diff --git a/contrib/python/hypothesis/py3/ya.make b/contrib/python/hypothesis/py3/ya.make index 13a8342b392..8e7bcbe8124 100644 --- a/contrib/python/hypothesis/py3/ya.make +++ b/contrib/python/hypothesis/py3/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(6.112.5) +VERSION(6.115.1) LICENSE(MPL-2.0) |
