diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index 1e7c096d2b..e348ecb774 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -24,10 +24,10 @@ jobs: - uses: actions/checkout@v3 with: fetch-depth: 0 - - name: Set up Python 3.10 + - name: Set up Python 3.14 uses: actions/setup-python@v4 with: - python-version: "3.10.9" + python-version: "3.14.2" - name: Restore cache uses: actions/cache@v3 with: @@ -79,7 +79,7 @@ jobs: with: name: explicit-example-patches path: .hypothesis/patches/latest_hypofuzz_*.patch - + # Upload the database so it'll be persisted between runs. # Note that we can also pull it down to use locally via # https://hypothesis.readthedocs.io/en/latest/database.html#hypothesis.database.GitHubArtifactDatabase diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 178df30cf3..f1f12dfd32 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -33,7 +33,7 @@ jobs: - check-format fail-fast: false env: - PYTHON_VERSION: "3.10" + PYTHON_VERSION: "3.14" steps: - uses: actions/checkout@v3 with: @@ -88,22 +88,24 @@ jobs: - check-py313t-nocover - check-py313t-niche - check-quality - - check-pytest62 - - check-pytest62 + - check-pytest9 + - check-pytest84 + - check-pytest74 + - check-py311-pytest62 - check-django60 - check-django42 - - check-pandas22 - - check-pandas21 - - check-pandas20 - - check-pandas15 - - check-pandas14 - - check-pandas13 + - check-py313-pandas22 + - check-py312-pandas21 + - check-py311-pandas20 + - check-py311-pandas15 + - check-py310-pandas14 + - check-py310-pandas13 ## FIXME: actions update means Python builds without eg _bz2, which was required # - check-py310-pandas12 # - check-py310-pandas11 fail-fast: false env: - PYTHON_VERSION: "3.10" + PYTHON_VERSION: "3.14" steps: - uses: actions/checkout@v3 with: @@ -172,7 +174,7 @@ jobs: - check-numpy-nightly fail-fast: false env: - PYTHON_VERSION: "3.10" + PYTHON_VERSION: "3.14" steps: - uses: actions/checkout@v3 with: @@ -201,7 +203,7 @@ jobs: - macos-latest python-version: - "3.11" - - "3.13" + - "3.14" python-architecture: - null - "x86" @@ -213,11 +215,11 @@ jobs: - alt-rest exclude: - { os: macos-latest, python-architecture: "x86" } - - { python-version: "3.13", python-architecture: "x86" } + - { python-version: "3.14", python-architecture: "x86" } - { python-version: "3.11", task: nocover } - { python-version: "3.11", task: rest } - - { python-version: "3.13", task: alt-nocover } - - { python-version: "3.13", task: alt-rest } + - { python-version: "3.14", task: alt-nocover } + - { python-version: "3.14", task: alt-rest } fail-fast: false runs-on: ${{ matrix.os }} env: @@ -253,6 +255,7 @@ jobs: # The versions of pyodide-build and the Pyodide runtime may differ. PYODIDE_VERSION: 0.29.1 PYODIDE_BUILD_VERSION: 0.31.1 + # pyodide 0.29.0 (latest at time of writing) doesn't yet support 3.14 PYTHON_VERSION: 3.13.2 steps: - uses: actions/checkout@v3 @@ -316,7 +319,7 @@ jobs: token: ${{ secrets.GH_TOKEN }} - uses: ./.github/actions/install-base with: - python-version: "3.10" + python-version: "3.14" - name: Deploy package env: GH_TOKEN: ${{ secrets.GH_TOKEN }} diff --git a/.github/workflows/update-deps.yml b/.github/workflows/update-deps.yml index 685cdf63db..674f5f6b89 100644 --- a/.github/workflows/update-deps.yml +++ b/.github/workflows/update-deps.yml @@ -11,10 +11,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Set up Python 3.10 + - name: Set up Python 3.14 uses: actions/setup-python@v4 with: - python-version: '3.10' + python-version: '3.14' - name: Update pinned dependencies run: ./build.sh upgrade-requirements - name: Open pull request diff --git a/.readthedocs.yml b/.readthedocs.yml index 0ca2e8060c..68422c0ead 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -14,7 +14,7 @@ formats: build: os: ubuntu-22.04 tools: - python: "3.10" + python: "3.14" python: install: - requirements: requirements/tools.txt diff --git a/build.sh b/build.sh index c3043c58f7..9d2a129745 100755 --- a/build.sh +++ b/build.sh @@ -19,14 +19,16 @@ SCRIPTS="$ROOT/tooling/scripts" # shellcheck source=tooling/scripts/common.sh source "$SCRIPTS/common.sh" +PYTHON_VERSION="3.14.2" + if [ -n "${GITHUB_ACTIONS-}" ] || [ -n "${CODESPACES-}" ] || [ -n "${CLAUDECODE-}" ] ; then # We're on GitHub Actions, Codespaces, or Claude Code and already have a suitable Python PYTHON=$(command -v python3 || command -v python) else # Otherwise, we install it from scratch # NOTE: tooling keeps this version in sync with ci_version in tooling - "$SCRIPTS/ensure-python.sh" 3.10.19 - PYTHON=$(pythonloc 3.10.19)/bin/python + "$SCRIPTS/ensure-python.sh" "$PYTHON_VERSION" + PYTHON=$(pythonloc "$PYTHON_VERSION")/bin/python fi TOOL_REQUIREMENTS="$ROOT/requirements/tools.txt" diff --git a/hypothesis-python/RELEASE.rst b/hypothesis-python/RELEASE.rst new file mode 100644 index 0000000000..3d24d900f6 --- /dev/null +++ b/hypothesis-python/RELEASE.rst @@ -0,0 +1,3 @@ +RELEASE_TYPE: patch + +Update some internal type hints. diff --git a/hypothesis-python/src/hypothesis/database.py b/hypothesis-python/src/hypothesis/database.py index 68568675f3..4fbddd2902 100644 --- a/hypothesis-python/src/hypothesis/database.py +++ b/hypothesis-python/src/hypothesis/database.py @@ -76,7 +76,9 @@ def _usable_dir(path: StrPathT) -> bool: # Loop terminates because the root dir ('/' on unix) always exists. path = path.parent return path.is_dir() and os.access(path, os.R_OK | os.W_OK | os.X_OK) - except PermissionError: + except PermissionError: # pragma: no cover + # path.exists() returns False on 3.14+ instead of raising. See + # https://docs.python.org/3.14/library/pathlib.html#querying-file-type-and-status return False @@ -132,18 +134,36 @@ def __call__(self, *args: Any, **kwargs: Any) -> "ExampleDatabase": # This code only runs if Sphinx has already been imported; and it would live in our # docs/conf.py except that we would also like it to work for anyone documenting # downstream ExampleDatabase subclasses too. -if "sphinx" in sys.modules: +# +# We avoid type-checking this block due to this combination facts: +# * our check-types-api CI job runs under 3.14 +# * tools.txt therefore pins to a newer version of sphinx which uses 3.12+ `type` +# syntax +# * in test_mypy.py, mypy sees this block, sees sphinx is installed, tries parsing +# sphinx code, and errors +# +# Putting `and not TYPE_CHECKING` here is just a convenience for our testing setup +# (because we don't split mypy tests by running CI version, eg), not for runtime +# behavior. +if "sphinx" in sys.modules and not TYPE_CHECKING: # pragma: no cover try: import sphinx.ext.autodoc signature = "hypothesis.database._EDMeta.__call__" + + # _METACLASS_CALL_BLACKLIST moved in newer sphinx versions + try: + import sphinx.ext.autodoc._dynamic._signatures as _module + except ImportError: + _module = sphinx.ext.autodoc + # _METACLASS_CALL_BLACKLIST is a frozenset in later sphinx versions - if isinstance(sphinx.ext.autodoc._METACLASS_CALL_BLACKLIST, frozenset): - sphinx.ext.autodoc._METACLASS_CALL_BLACKLIST = ( - sphinx.ext.autodoc._METACLASS_CALL_BLACKLIST | {signature} - ) + if isinstance(_module._METACLASS_CALL_BLACKLIST, frozenset): + _module._METACLASS_CALL_BLACKLIST = _module._METACLASS_CALL_BLACKLIST | { + signature + } else: - sphinx.ext.autodoc._METACLASS_CALL_BLACKLIST.append(signature) + _module._METACLASS_CALL_BLACKLIST.append(signature) except Exception: pass diff --git a/hypothesis-python/src/hypothesis/entry_points.py b/hypothesis-python/src/hypothesis/entry_points.py index 4a68af7c43..98933265c2 100644 --- a/hypothesis-python/src/hypothesis/entry_points.py +++ b/hypothesis-python/src/hypothesis/entry_points.py @@ -17,23 +17,15 @@ import importlib.metadata import os -from collections.abc import Generator, Sequence -from importlib.metadata import EntryPoint - - -def get_entry_points() -> Generator[EntryPoint, None, None]: - try: - eps: Sequence[EntryPoint] = importlib.metadata.entry_points(group="hypothesis") - except TypeError: # pragma: no cover - # Load-time selection requires Python >= 3.10. See also - # https://importlib-metadata.readthedocs.io/en/latest/using.html - eps = importlib.metadata.entry_points().get("hypothesis", []) - yield from eps def run() -> None: - if not os.environ.get("HYPOTHESIS_NO_PLUGINS"): - for entry in get_entry_points(): # pragma: no cover - hook = entry.load() - if callable(hook): - hook() + if os.environ.get("HYPOTHESIS_NO_PLUGINS"): + return + + for entry in importlib.metadata.entry_points( + group="hypothesis" + ): # pragma: no cover + hook = entry.load() + if callable(hook): + hook() diff --git a/hypothesis-python/src/hypothesis/extra/_array_helpers.py b/hypothesis-python/src/hypothesis/extra/_array_helpers.py index c688bc5508..802f571f24 100644 --- a/hypothesis-python/src/hypothesis/extra/_array_helpers.py +++ b/hypothesis-python/src/hypothesis/extra/_array_helpers.py @@ -134,7 +134,8 @@ def valid_tuple_axes( min_size: int = 0, max_size: int | None = None, ) -> st.SearchStrategy[tuple[int, ...]]: - """All tuples will have a length >= ``min_size`` and <= ``max_size``. The default + """ + All tuples will have a length >= ``min_size`` and <= ``max_size``. The default value for ``max_size`` is ``ndim``. Examples from this strategy shrink towards an empty tuple, which render most @@ -153,7 +154,6 @@ def valid_tuple_axes( .. code-block:: python any_axis_strategy = none() | integers(-ndim, ndim - 1) | valid_tuple_axes(ndim) - """ check_type(int, ndim, "ndim") check_type(int, min_size, "min_size") @@ -364,7 +364,8 @@ def mutually_broadcastable_shapes( min_side: int = 1, max_side: int | None = None, ) -> st.SearchStrategy[BroadcastableShapes]: - """Return a strategy for a specified number of shapes N that are + """ + Return a strategy for a specified number of shapes N that are mutually-broadcastable with one another and with the provided base shape. * ``num_shapes`` is the number of mutually broadcast-compatible shapes to generate. @@ -397,7 +398,6 @@ def mutually_broadcastable_shapes( BroadcastableShapes(input_shapes=((), (), ()), result_shape=()) BroadcastableShapes(input_shapes=((3,), (), (3,)), result_shape=(3,)) BroadcastableShapes(input_shapes=((1, 2, 3), (3,), ()), result_shape=(1, 2, 3)) - """ arg_msg = "Pass either the `num_shapes` or the `signature` argument, but not both." if num_shapes is not not_set: diff --git a/hypothesis-python/src/hypothesis/internal/compat.py b/hypothesis-python/src/hypothesis/internal/compat.py index 0c82a53e30..23ccc42815 100644 --- a/hypothesis-python/src/hypothesis/internal/compat.py +++ b/hypothesis-python/src/hypothesis/internal/compat.py @@ -27,10 +27,10 @@ get_args, ) -try: - BaseExceptionGroup = BaseExceptionGroup - ExceptionGroup = ExceptionGroup # pragma: no cover -except NameError: +if sys.version_info >= (3, 11): + BaseExceptionGroup = BaseExceptionGroup # noqa: F821 + ExceptionGroup = ExceptionGroup # noqa: F821 +else: # pragma: no cover from exceptiongroup import ( BaseExceptionGroup as BaseExceptionGroup, ExceptionGroup as ExceptionGroup, @@ -47,7 +47,7 @@ # In order to use NotRequired, we need the version of TypedDict included in Python 3.11+. if sys.version_info[:2] >= (3, 11): from typing import NotRequired as NotRequired, TypedDict as TypedDict - else: + else: # pragma: no cover try: from typing_extensions import ( NotRequired as NotRequired, @@ -65,7 +65,7 @@ def __class_getitem__(cls, item): from typing import ( override as override, ) - except ImportError: + except ImportError: # pragma: no cover try: from typing_extensions import ( override as override, @@ -83,7 +83,7 @@ def __class_getitem__(cls, item): def add_note(exc, note): try: exc.add_note(note) - except AttributeError: + except AttributeError: # pragma: no cover if not hasattr(exc, "__notes__"): try: exc.__notes__ = [] @@ -251,6 +251,31 @@ def bad_django_TestCase(runner: Optional["ConjectureRunner"]) -> bool: # see issue #3812 if sys.version_info[:2] < (3, 12): + def _asdict_inner(obj, dict_factory): + if dataclasses._is_dataclass_instance(obj): + return dict_factory( + (f.name, _asdict_inner(getattr(obj, f.name), dict_factory)) + for f in dataclasses.fields(obj) + ) + elif isinstance(obj, tuple) and hasattr(obj, "_fields"): + return type(obj)(*[_asdict_inner(v, dict_factory) for v in obj]) + elif isinstance(obj, (list, tuple)): + return type(obj)(_asdict_inner(v, dict_factory) for v in obj) + elif isinstance(obj, dict): + if hasattr(type(obj), "default_factory"): + result = type(obj)(obj.default_factory) + for k, v in obj.items(): + result[_asdict_inner(k, dict_factory)] = _asdict_inner( + v, dict_factory + ) + return result + return type(obj)( + (_asdict_inner(k, dict_factory), _asdict_inner(v, dict_factory)) + for k, v in obj.items() + ) + else: + return copy.deepcopy(obj) + def dataclass_asdict(obj, *, dict_factory=dict): """ A vendored variant of dataclasses.asdict. Includes the bugfix for @@ -267,30 +292,6 @@ def dataclass_asdict(obj, *, dict_factory=dict): dataclass_asdict = dataclasses.asdict -def _asdict_inner(obj, dict_factory): - if dataclasses._is_dataclass_instance(obj): - return dict_factory( - (f.name, _asdict_inner(getattr(obj, f.name), dict_factory)) - for f in dataclasses.fields(obj) - ) - elif isinstance(obj, tuple) and hasattr(obj, "_fields"): - return type(obj)(*[_asdict_inner(v, dict_factory) for v in obj]) - elif isinstance(obj, (list, tuple)): - return type(obj)(_asdict_inner(v, dict_factory) for v in obj) - elif isinstance(obj, dict): - if hasattr(type(obj), "default_factory"): - result = type(obj)(obj.default_factory) - for k, v in obj.items(): - result[_asdict_inner(k, dict_factory)] = _asdict_inner(v, dict_factory) - return result - return type(obj)( - (_asdict_inner(k, dict_factory), _asdict_inner(v, dict_factory)) - for k, v in obj.items() - ) - else: - return copy.deepcopy(obj) - - if sys.version_info[:2] < (3, 13): # batched was added in 3.12, strict flag in 3.13 # copied from 3.13 docs reference implementation diff --git a/hypothesis-python/src/hypothesis/internal/conjecture/provider_conformance.py b/hypothesis-python/src/hypothesis/internal/conjecture/provider_conformance.py index 558bb68642..55ced851b5 100644 --- a/hypothesis-python/src/hypothesis/internal/conjecture/provider_conformance.py +++ b/hypothesis-python/src/hypothesis/internal/conjecture/provider_conformance.py @@ -44,7 +44,8 @@ def build_intervals(intervals: list[int]) -> list[tuple[int, int]]: if len(intervals) % 2: intervals = intervals[:-1] intervals.sort() - return list(batched(intervals, 2, strict=True)) + # help mypy infer tuple[int, ...] -> tuple[int, int] + return list(batched(intervals, 2, strict=True)) # type: ignore def interval_lists( diff --git a/hypothesis-python/src/hypothesis/internal/entropy.py b/hypothesis-python/src/hypothesis/internal/entropy.py index 0082eb2d88..63477860bf 100644 --- a/hypothesis-python/src/hypothesis/internal/entropy.py +++ b/hypothesis-python/src/hypothesis/internal/entropy.py @@ -244,7 +244,7 @@ def restore_all() -> None: def deterministic_PRNG(seed: int = 0) -> Generator[None, None, None]: """Context manager that handles random.seed without polluting global state. - See issue #1255 and PR #1295 for details and motivation - in short, + See issue #1266 and PR #1295 for details and motivation - in short, leaving the global pseudo-random number generator (PRNG) seeded is a very bad idea in principle, and breaks all kinds of independence assumptions in practice. diff --git a/hypothesis-python/src/hypothesis/internal/filtering.py b/hypothesis-python/src/hypothesis/internal/filtering.py index 155e67e74a..cf1f2f4f5d 100644 --- a/hypothesis-python/src/hypothesis/internal/filtering.py +++ b/hypothesis-python/src/hypothesis/internal/filtering.py @@ -26,6 +26,7 @@ import inspect import math import operator +import sys from collections.abc import Callable, Collection from decimal import Decimal from fractions import Fraction @@ -37,10 +38,9 @@ from hypothesis.internal.lambda_sources import lambda_description from hypothesis.internal.reflection import get_pretty_function_description -try: - # new in 3.14 - from functools import Placeholder # type: ignore -except ImportError: +if sys.version_info[:2] >= (3, 14): + from functools import Placeholder +else: # pragma: no cover Placeholder = object() Ex = TypeVar("Ex") diff --git a/hypothesis-python/src/hypothesis/internal/lambda_sources.py b/hypothesis-python/src/hypothesis/internal/lambda_sources.py index 596b4c8810..0803c3010f 100644 --- a/hypothesis-python/src/hypothesis/internal/lambda_sources.py +++ b/hypothesis-python/src/hypothesis/internal/lambda_sources.py @@ -301,7 +301,7 @@ def _lambda_code_matches_node(f, node): return _function_key(f) == _function_key(compiled) -def _check_unknown_perfectly_aligned_lambda(candidate): +def _check_unknown_perfectly_aligned_lambda(candidate): # pragma: no cover # This is a monkeypatch point for our self-tests, to make unknown # lambdas raise. pass diff --git a/hypothesis-python/src/hypothesis/internal/reflection.py b/hypothesis-python/src/hypothesis/internal/reflection.py index 87bf67a3e7..f462b10fcf 100644 --- a/hypothesis-python/src/hypothesis/internal/reflection.py +++ b/hypothesis-python/src/hypothesis/internal/reflection.py @@ -116,7 +116,7 @@ def function_digest(function: Any) -> bytes: return hasher.digest() -def check_signature(sig: Signature) -> None: +def check_signature(sig: Signature) -> None: # pragma: no cover # 3.10 only # Backport from Python 3.11; see https://github.com/python/cpython/pull/92065 for p in sig.parameters.values(): if iskeyword(p.name) and p.kind is not p.POSITIONAL_ONLY: diff --git a/hypothesis-python/src/hypothesis/strategies/_internal/core.py b/hypothesis-python/src/hypothesis/strategies/_internal/core.py index 1006fc792d..cfcb1f58bc 100644 --- a/hypothesis-python/src/hypothesis/strategies/_internal/core.py +++ b/hypothesis-python/src/hypothesis/strategies/_internal/core.py @@ -1067,6 +1067,7 @@ def do_draw(self, data: ConjectureData) -> Ex: with context.track_arg_label(k) as arg_label: kwargs[k] = data.draw(v) arg_labels |= arg_label + try: obj = self.target(*args, **kwargs) except TypeError as err: @@ -2499,6 +2500,7 @@ def register_type_strategy( ) if ( "pydantic.generics" in sys.modules + and isinstance(custom_type, type) and issubclass(custom_type, sys.modules["pydantic.generics"].GenericModel) and not re.search(r"[A-Za-z_]+\[.+\]", repr(custom_type)) and callable(strategy) diff --git a/hypothesis-python/src/hypothesis/strategies/_internal/types.py b/hypothesis-python/src/hypothesis/strategies/_internal/types.py index dae1691d15..84d73a2d20 100644 --- a/hypothesis-python/src/hypothesis/strategies/_internal/types.py +++ b/hypothesis-python/src/hypothesis/strategies/_internal/types.py @@ -104,7 +104,7 @@ except AttributeError: # pragma: no cover pass # Is missing for `python<3.10` try: - TypeGuardTypes += (typing.TypeIs,) # type: ignore + TypeGuardTypes += (typing.TypeIs,) except AttributeError: # pragma: no cover pass # Is missing for `python<3.13` try: @@ -115,7 +115,7 @@ RequiredTypes: tuple = () try: - RequiredTypes += (typing.Required,) # type: ignore + RequiredTypes += (typing.Required,) except AttributeError: # pragma: no cover pass # Is missing for `python<3.11` try: @@ -126,7 +126,7 @@ NotRequiredTypes: tuple = () try: - NotRequiredTypes += (typing.NotRequired,) # type: ignore + NotRequiredTypes += (typing.NotRequired,) except AttributeError: # pragma: no cover pass # Is missing for `python<3.11` try: @@ -137,7 +137,7 @@ ReadOnlyTypes: tuple = () try: - ReadOnlyTypes += (typing.ReadOnly,) # type: ignore + ReadOnlyTypes += (typing.ReadOnly,) except AttributeError: # pragma: no cover pass # Is missing for `python<3.13` try: @@ -148,7 +148,7 @@ LiteralStringTypes: tuple = () try: - LiteralStringTypes += (typing.LiteralString,) # type: ignore + LiteralStringTypes += (typing.LiteralString,) except AttributeError: # pragma: no cover pass # Is missing for `python<3.11` try: @@ -202,7 +202,7 @@ ): try: NON_RUNTIME_TYPES += (getattr(typing, name),) - except AttributeError: + except AttributeError: # pragma: no cover pass try: NON_RUNTIME_TYPES += (getattr(typing_extensions, name),) @@ -717,8 +717,13 @@ def _networks(bits): # 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 +if sys.version_info < (3, 12): + _RegistryKeyT: typing.TypeAlias = type +else: # pragma: no cover + _RegistryKeyT: typing.TypeAlias = type | typing.TypeAliasType + _global_type_lookup: dict[ - type, st.SearchStrategy | typing.Callable[[type], st.SearchStrategy] + _RegistryKeyT, st.SearchStrategy | typing.Callable[[type], st.SearchStrategy] ] = { type(None): st.none(), bool: st.booleans(), @@ -976,8 +981,11 @@ def resolve_Tuple(thing): elem_types = getattr(thing, "__args__", None) or () if len(elem_types) == 2 and elem_types[-1] is Ellipsis: return st.lists(st.from_type(elem_types[0])).map(tuple) - elif len(elem_types) == 1 and elem_types[0] == (): - return st.tuples() # Empty tuple; see issue #1583 + elif len(elem_types) == 1 and elem_types[0] == (): # pragma: no cover + # Empty tuple; see issue #1583. + # Only possible on 3.10. `from typing import Tuple; Tuple[()].__args__` + # is ((),) on 3.10, and () on 3.11+. + return st.tuples() return st.tuples(*map(st.from_type, elem_types)) diff --git a/hypothesis-python/tests/common/utils.py b/hypothesis-python/tests/common/utils.py index d84c1946d8..dcb3f73be9 100644 --- a/hypothesis-python/tests/common/utils.py +++ b/hypothesis-python/tests/common/utils.py @@ -258,14 +258,14 @@ class Why(enum.Enum): def xfail_on_crosshair(why: Why, /, *, strict=True, as_marks=False): # run `pytest -m xf_crosshair` to select these tests! - kw = { - "strict": strict and why != Why.undiscovered, - "reason": f"Expected failure due to: {why.value}", - "condition": settings().backend == "crosshair", - } + mark = pytest.mark.xfail( + strict=strict and why != Why.undiscovered, + reason=f"Expected failure due to: {why.value}", + condition=settings().backend == "crosshair", + ) if as_marks: # for use with pytest.param(..., marks=xfail_on_crosshair()) - return (pytest.mark.xf_crosshair, pytest.mark.xfail(**kw)) - return lambda fn: pytest.mark.xf_crosshair(pytest.mark.xfail(**kw)(fn)) + return (pytest.mark.xf_crosshair, mark) + return lambda fn: pytest.mark.xf_crosshair(mark(fn)) def skipif_threading(f): diff --git a/hypothesis-python/tests/conjecture/test_provider.py b/hypothesis-python/tests/conjecture/test_provider.py index 75f2bbec36..0349119b30 100644 --- a/hypothesis-python/tests/conjecture/test_provider.py +++ b/hypothesis-python/tests/conjecture/test_provider.py @@ -13,6 +13,7 @@ import math import sys import time +import warnings from contextlib import contextmanager, nullcontext from random import Random from threading import RLock @@ -790,9 +791,15 @@ def f(n): @pytest.mark.parametrize("provider", [HypothesisProvider, PrngProvider]) def test_provider_conformance(provider): - run_conformance_test( - provider, settings=settings(max_examples=20, stateful_step_count=20) - ) + with warnings.catch_warnings(): + # emitted by available_timezones() from st.timezone_keys() on 3.11+ + # with tzdata installed. see https://github.com/python/cpython/issues/137841. + # Once cpython fixes this, we can remove this. + if sys.version_info >= (3, 11): + warnings.simplefilter("ignore", EncodingWarning) + run_conformance_test( + provider, settings=settings(max_examples=20, stateful_step_count=20) + ) # see https://github.com/HypothesisWorks/hypothesis/issues/4462 and discussion diff --git a/hypothesis-python/tests/cover/test_cache_implementation.py b/hypothesis-python/tests/cover/test_cache_implementation.py index a664555dc7..bf9732037e 100644 --- a/hypothesis-python/tests/cover/test_cache_implementation.py +++ b/hypothesis-python/tests/cover/test_cache_implementation.py @@ -116,7 +116,7 @@ def test_behaves_like_a_dict_with_losses(implementation, writes, size): assert len(target) <= min(len(model), size) -@xfail_on_crosshair(Why.symbolic_outside_context) +@xfail_on_crosshair(Why.symbolic_outside_context, strict=False) @settings( suppress_health_check={HealthCheck.too_slow} | set(settings().suppress_health_check), diff --git a/hypothesis-python/tests/cover/test_core.py b/hypothesis-python/tests/cover/test_core.py index 2112134bc0..ac4cb0d89b 100644 --- a/hypothesis-python/tests/cover/test_core.py +++ b/hypothesis-python/tests/cover/test_core.py @@ -17,6 +17,8 @@ from hypothesis.database import InMemoryExampleDatabase from hypothesis.errors import InvalidArgument, NoSuchExample, Unsatisfiable +from tests.common.utils import Why, xfail_on_crosshair + def test_stops_after_max_examples_if_satisfying(): count = 0 @@ -162,6 +164,9 @@ def test_non_executed_tests_raise_skipped(test_fn): ], ) @given(st.data()) +@xfail_on_crosshair( + Why.other, strict=False +) # https://github.com/pschanely/hypothesis-crosshair/issues/48 def test_characters_codec(codec, max_codepoint, exclude_categories, categories, data): strategy = st.characters( codec=codec, diff --git a/hypothesis-python/tests/cover/test_lookup.py b/hypothesis-python/tests/cover/test_lookup.py index a00fdf04c6..83b37dd451 100644 --- a/hypothesis-python/tests/cover/test_lookup.py +++ b/hypothesis-python/tests/cover/test_lookup.py @@ -52,7 +52,7 @@ find_any, minimal, ) -from tests.common.utils import fails_with, temp_registered +from tests.common.utils import Why, fails_with, temp_registered, xfail_on_crosshair # we'll continue testing the typing variants until their removal from the stdlib # ruff: noqa: UP006, UP035, UP045, UP007 @@ -665,38 +665,44 @@ def test_recursive_type_with_defaults_minimizes_to_defaults(): assert minimal(from_type(MyList), lambda ex: True) == MyList() -class A: - def __init__(self, nxt: typing.Optional["B"]): +class MutualA: + def __init__(self, nxt: typing.Optional["MutualB"]): self.nxt = nxt def __repr__(self): return f"A({self.nxt})" -class B: - def __init__(self, nxt: typing.Optional["A"]): +class MutualB: + def __init__(self, nxt: typing.Optional["MutualA"]): self.nxt = nxt def __repr__(self): return f"B({self.nxt})" -@given(nxt=st.from_type(A)) +@given(nxt=st.from_type(MutualA)) +@xfail_on_crosshair( + Why.other +) # https://github.com/pschanely/hypothesis-crosshair/issues/49 def test_resolving_mutually_recursive_types(nxt): i = 0 while nxt: - assert isinstance(nxt, [A, B][i % 2]) + assert isinstance(nxt, [MutualA, MutualB][i % 2]) nxt = nxt.nxt i += 1 +@xfail_on_crosshair( + Why.other +) # https://github.com/pschanely/hypothesis-crosshair/issues/49 def test_resolving_mutually_recursive_types_with_limited_stack(): orig_recursionlimit = sys.getrecursionlimit() current_stack_depth = stack_depth_of_caller() sys.setrecursionlimit(current_stack_depth + 100) try: - @given(nxt=st.from_type(A)) + @given(nxt=st.from_type(MutualA)) def test(nxt): pass @@ -979,6 +985,9 @@ def test_timezone_lookup(type_): ) @settings(suppress_health_check=[HealthCheck.data_too_large]) @given(data=st.data()) +@xfail_on_crosshair( + Why.other +) # https://github.com/pschanely/hypothesis-crosshair/issues/49 def test_generic_collections_only_use_hashable_elements(typ, data): data.draw(from_type(typ)) @@ -1101,6 +1110,9 @@ def __init__(self, value: int) -> None: @given(st.data()) +@xfail_on_crosshair( + Why.other +) # https://github.com/pschanely/hypothesis-crosshair/issues/49 def test_constructor_is_more_important(data): """Constructor types should take precedence over all other annotations.""" data.draw(st.builds(AnnotatedConstructor)) diff --git a/hypothesis-python/tests/cover/test_regex.py b/hypothesis-python/tests/cover/test_regex.py index 3e26d97d2a..ade39911c6 100644 --- a/hypothesis-python/tests/cover/test_regex.py +++ b/hypothesis-python/tests/cover/test_regex.py @@ -33,6 +33,7 @@ check_can_generate_examples, find_any, ) +from tests.common.utils import Why, xfail_on_crosshair def is_ascii(s): @@ -409,6 +410,9 @@ def test_shared_union(): @given(st.data()) +@xfail_on_crosshair( + Why.other +) # https://github.com/pschanely/hypothesis-crosshair/issues/48 def test_issue_992_regression(data): strat = st.from_regex( re.compile( diff --git a/hypothesis-python/tests/nocover/test_drypython_returns.py b/hypothesis-python/tests/nocover/test_drypython_returns.py index e3767bfcd2..635a6e6b63 100644 --- a/hypothesis-python/tests/nocover/test_drypython_returns.py +++ b/hypothesis-python/tests/nocover/test_drypython_returns.py @@ -16,7 +16,7 @@ from hypothesis.errors import ResolutionFailed from tests.common.debug import check_can_generate_examples, find_any -from tests.common.utils import temp_registered +from tests.common.utils import Why, temp_registered, xfail_on_crosshair # Primitives: # =========== @@ -59,6 +59,9 @@ def target_func(mappable: "MappableN[_FirstType]") -> bool: @given(st.data()) +@xfail_on_crosshair( + Why.other, strict=False +) # https://github.com/pschanely/hypothesis-crosshair/issues/49 def test_my_mappable(source: st.DataObject) -> None: """ Checks that complex types with multiple inheritance levels and strings are fine. diff --git a/hypothesis-python/tests/nocover/test_regex.py b/hypothesis-python/tests/nocover/test_regex.py index b4f49165de..f30582ee8b 100644 --- a/hypothesis-python/tests/nocover/test_regex.py +++ b/hypothesis-python/tests/nocover/test_regex.py @@ -21,6 +21,8 @@ base_regex_strategy, ) +from tests.common.utils import Why, xfail_on_crosshair + @st.composite def charset(draw): @@ -73,6 +75,9 @@ def test_conservative_regex_are_correct_by_construction(data): assert pattern.search(result) is not None +@xfail_on_crosshair( + Why.other +) # https://github.com/pschanely/hypothesis-crosshair/issues/48 @given(st.data()) def test_fuzz_stuff(data): pattern = data.draw( @@ -102,6 +107,9 @@ def test_fuzz_stuff(data): assert regex.search(ex) +@xfail_on_crosshair( + Why.other +) # https://github.com/pschanely/hypothesis-crosshair/issues/48 @pytest.mark.skipif(sys.version_info[:2] < (3, 11), reason="new syntax") @given(st.data()) def test_regex_atomic_group(data): @@ -110,6 +118,9 @@ def test_regex_atomic_group(data): assert re.search(pattern, ex) +@xfail_on_crosshair( + Why.other +) # https://github.com/pschanely/hypothesis-crosshair/issues/48 @pytest.mark.skipif(sys.version_info[:2] < (3, 11), reason="new syntax") @given(st.data()) def test_regex_possessive(data): @@ -124,6 +135,9 @@ def test_regex_possessive(data): assert re.compile(I_WITH_DOT, flags=re.IGNORECASE).match(I_WITH_DOT.swapcase()) +@xfail_on_crosshair( + Why.other +) # https://github.com/pschanely/hypothesis-crosshair/issues/48 @given(st.data()) def test_case_insensitive_not_literal_never_constructs_multichar_match(data): # So our goal is to confirm that we can never accidentally create a non-matching diff --git a/hypothesis-python/tox.ini b/hypothesis-python/tox.ini index 8066e7f401..f0b84b5081 100644 --- a/hypothesis-python/tox.ini +++ b/hypothesis-python/tox.ini @@ -97,7 +97,7 @@ deps = commands = python -bb -X dev -m pytest tests/pandas -n auto -[testenv:pandas13] +[testenv:py310-pandas13] deps = -r../requirements/test.txt numpy~=1.26.4 @@ -105,7 +105,7 @@ deps = commands = python -bb -X dev -m pytest tests/pandas -n auto -[testenv:pandas14] +[testenv:py310-pandas14] deps = -r../requirements/test.txt numpy~=1.26.4 @@ -113,7 +113,7 @@ deps = commands = python -bb -X dev -m pytest tests/pandas -n auto -[testenv:pandas15] +[testenv:py311-pandas15] deps = -r../requirements/test.txt numpy~=1.26.4 @@ -121,7 +121,7 @@ deps = commands = python -bb -X dev -m pytest tests/pandas -n auto -[testenv:pandas20] +[testenv:py311-pandas20] deps = -r../requirements/test.txt numpy~=1.26.4 @@ -129,7 +129,7 @@ deps = commands = python -bb -X dev -m pytest tests/pandas -n auto -[testenv:pandas21] +[testenv:py312-pandas21] deps = -r../requirements/test.txt numpy~=1.26.4 @@ -139,7 +139,7 @@ setenv= commands = python -bb -X dev -m pytest tests/pandas -n auto -[testenv:pandas22] +[testenv:py313-pandas22] deps = -r../requirements/test.txt pandas~=2.2.2 @@ -223,11 +223,19 @@ commands_pre = commands = python -bb -X dev -m pytest tests/pytest tests/cover/test_testdecorators.py -[testenv:pytest8] +[testenv:pytest84] deps = -r../requirements/test.txt commands_pre = - pip install pytest==8.* pytest-xdist + pip install pytest==8.4.2 pytest-xdist +commands = + python -bb -X dev -m pytest tests/pytest tests/cover/test_testdecorators.py + +[testenv:pytest9] +deps = + -r../requirements/test.txt +commands_pre = + pip install pytest==9.* pytest-xdist commands = python -bb -X dev -m pytest tests/pytest tests/cover/test_testdecorators.py diff --git a/pyproject.toml b/pyproject.toml index 7c387fdce0..4e634a5a2c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -91,7 +91,7 @@ ignore = [ "hypothesis-python/tests/conjecture/test_data_tree.py" = ["B023"] [tool.mypy] -python_version = "3.10" +python_version = "3.14" platform = "linux" allow_redefinition = true diff --git a/requirements/coverage.txt b/requirements/coverage.txt index ec7403efd6..ac64e679e9 100644 --- a/requirements/coverage.txt +++ b/requirements/coverage.txt @@ -1,13 +1,11 @@ # -# This file is autogenerated by pip-compile with Python 3.10 +# This file is autogenerated by pip-compile with Python 3.14 # by the following command: # # ./build.sh upgrade-requirements # annotated-types==0.7.0 # via -r requirements/coverage.in -async-timeout==5.0.1 - # via redis black==25.12.0 # via -r requirements/coverage.in click==8.3.1 @@ -18,10 +16,6 @@ coverage[toml]==7.13.1 # via pytest-cov dpcontracts==0.6.0 # via -r requirements/coverage.in -exceptiongroup==1.3.1 ; python_version < "3.11" - # via - # hypothesis (hypothesis-python/pyproject.toml) - # pytest execnet==2.1.2 # via pytest-xdist fakeredis==2.33.0 @@ -34,7 +28,7 @@ libcst==1.8.6 # via -r requirements/coverage.in mypy-extensions==1.1.0 # via black -numpy==2.2.6 +numpy==2.4.1 # via # -r requirements/coverage.in # pandas @@ -89,17 +83,8 @@ sortedcontainers==2.4.0 # via # fakeredis # hypothesis (hypothesis-python/pyproject.toml) -tomli==2.3.0 - # via - # black - # coverage - # pytest typing-extensions==4.15.0 - # via - # -r requirements/coverage.in - # black - # exceptiongroup - # fakeredis + # via -r requirements/coverage.in tzdata==2025.3 # via pandas watchdog==6.0.0 diff --git a/requirements/crosshair.txt b/requirements/crosshair.txt index af5f5cbf56..baa890184c 100644 --- a/requirements/crosshair.txt +++ b/requirements/crosshair.txt @@ -1,5 +1,5 @@ # -# This file is autogenerated by pip-compile with Python 3.10 +# This file is autogenerated by pip-compile with Python 3.14 # by the following command: # # ./build.sh upgrade-requirements @@ -17,12 +17,6 @@ crosshair-tool==0.0.101 # via # -r requirements/crosshair.in # hypothesis-crosshair -exceptiongroup==1.3.1 ; python_version < "3.11" - # via - # cattrs - # hypothesis - # hypothesis (hypothesis-python/pyproject.toml) - # pytest execnet==2.1.2 # via pytest-xdist hypothesis==6.150.0 @@ -63,15 +57,12 @@ sortedcontainers==2.4.0 # via # hypothesis # hypothesis (hypothesis-python/pyproject.toml) -tomli==2.3.0 - # via pytest typeshed-client==2.8.2 # via crosshair-tool typing-extensions==4.15.0 # via # cattrs # crosshair-tool - # exceptiongroup # typeshed-client # typing-inspect typing-inspect==0.9.0 diff --git a/requirements/fuzzing.txt b/requirements/fuzzing.txt index 7d9a28a61c..f4838e5cda 100644 --- a/requirements/fuzzing.txt +++ b/requirements/fuzzing.txt @@ -1,5 +1,5 @@ # -# This file is autogenerated by pip-compile with Python 3.10 +# This file is autogenerated by pip-compile with Python 3.14 # by the following command: # # ./build.sh upgrade-requirements @@ -8,8 +8,6 @@ annotated-types==0.7.0 # via -r requirements/coverage.in anyio==4.12.1 # via starlette -async-timeout==5.0.1 - # via redis attrs==25.4.0 # via # outcome @@ -30,15 +28,6 @@ coverage[toml]==7.13.1 # pytest-cov dpcontracts==0.6.0 # via -r requirements/coverage.in -exceptiongroup==1.3.1 ; python_version < "3.11" - # via - # anyio - # hypercorn - # hypothesis - # hypothesis (hypothesis-python/pyproject.toml) - # pytest - # taskgroup - # trio execnet==2.1.2 # via pytest-xdist fakeredis==2.33.0 @@ -77,7 +66,7 @@ mdurl==0.1.2 # via markdown-it-py mypy-extensions==1.1.0 # via black -numpy==2.2.6 +numpy==2.4.1 # via # -r requirements/coverage.in # pandas @@ -149,26 +138,10 @@ sortedcontainers==2.4.0 # trio starlette==0.51.0 # via hypofuzz -taskgroup==0.2.2 - # via hypercorn -tomli==2.3.0 - # via - # black - # coverage - # hypercorn - # pytest trio==0.32.0 # via hypofuzz typing-extensions==4.15.0 - # via - # -r requirements/coverage.in - # anyio - # black - # exceptiongroup - # fakeredis - # hypercorn - # starlette - # taskgroup + # via -r requirements/coverage.in tzdata==2025.3 # via pandas watchdog==6.0.0 diff --git a/requirements/test.txt b/requirements/test.txt index 60462b2eaa..a6cda8a2fe 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -1,13 +1,9 @@ # -# This file is autogenerated by pip-compile with Python 3.10 +# This file is autogenerated by pip-compile with Python 3.14 # by the following command: # # ./build.sh upgrade-requirements # -exceptiongroup==1.3.1 ; python_version < "3.11" - # via - # hypothesis (hypothesis-python/pyproject.toml) - # pytest execnet==2.1.2 # via pytest-xdist iniconfig==2.3.0 @@ -30,7 +26,3 @@ pytest-xdist==3.8.0 # via -r requirements/test.in sortedcontainers==2.4.0 # via hypothesis (hypothesis-python/pyproject.toml) -tomli==2.3.0 - # via pytest -typing-extensions==4.15.0 - # via exceptiongroup diff --git a/requirements/tools.in b/requirements/tools.in index e9314b5989..4bf4e87e7c 100644 --- a/requirements/tools.in +++ b/requirements/tools.in @@ -28,7 +28,8 @@ types-pytz types-redis typing-extensions watchdog # for typing -attrs # for typing build sortedcontainers-stubs # for typing +attrs # for typing +tomli # for update_pyproject_toml -r test.in diff --git a/requirements/tools.txt b/requirements/tools.txt index f50e1af140..72d0f6bb3a 100644 --- a/requirements/tools.txt +++ b/requirements/tools.txt @@ -1,5 +1,5 @@ # -# This file is autogenerated by pip-compile with Python 3.10 +# This file is autogenerated by pip-compile with Python 3.14 # by the following command: # # ./build.sh upgrade-requirements @@ -22,8 +22,6 @@ autoflake==2.3.1 # via shed babel==2.17.0 # via sphinx -backports-tarfile==1.2.0 - # via jaraco-context beautifulsoup4==4.14.3 # via # furo @@ -63,16 +61,15 @@ coverage==7.13.1 # via -r requirements/tools.in cryptography==46.0.3 # via - # secretstorage # types-pyopenssl # types-redis decorator==5.2.1 # via ipython distlib==0.4.0 # via virtualenv -django==5.2.10 +django==6.0.1 # via -r requirements/tools.in -docutils==0.21.2 +docutils==0.22.4 # via # pelican # readme-renderer @@ -81,12 +78,6 @@ docutils==0.21.2 # sphinx-jsonschema dpcontracts==0.6.0 # via -r requirements/tools.in -exceptiongroup==1.3.1 ; python_version < "3.11" - # via - # anyio - # hypothesis (hypothesis-python/pyproject.toml) - # ipython - # pytest execnet==2.1.2 # via pytest-xdist executing==2.2.1 @@ -109,12 +100,12 @@ idna==3.11 # requests imagesize==1.4.1 # via sphinx -importlib-metadata==8.7.1 - # via keyring iniconfig==2.3.0 # via pytest -ipython==8.38.0 +ipython==9.9.0 # via -r requirements/tools.in +ipython-pygments-lexers==1.1.1 + # via ipython isort==7.0.0 # via shed jaraco-classes==3.4.0 @@ -125,10 +116,6 @@ jaraco-functools==4.4.0 # via keyring jedi==0.19.2 # via ipython -jeepney==0.9.0 - # via - # keyring - # secretstorage jinja2==3.1.6 # via # pelican @@ -169,7 +156,7 @@ nh3==0.3.2 # via readme-renderer nodeenv==1.10.0 # via pyright -numpy==2.2.6 +numpy==2.4.1 # via -r requirements/tools.in ordered-set==4.1.0 # via pelican @@ -220,6 +207,7 @@ pygments==2.18.0 # accessible-pygments # furo # ipython + # ipython-pygments-lexers # pelican # pytest # readme-renderer @@ -271,10 +259,10 @@ rich==14.2.0 # via # pelican # twine +roman-numerals==4.1.0 + # via sphinx ruff==0.14.11 # via -r requirements/tools.in -secretstorage==3.5.0 - # via keyring shed==2024.1.1 # via -r requirements/tools.in six==1.17.0 @@ -289,14 +277,14 @@ sortedcontainers-stubs==2.4.3 # via -r requirements/tools.in soupsieve==2.8.1 # via beautifulsoup4 -sphinx==8.1.3 +sphinx==9.1.0 # via # -r requirements/tools.in # furo # sphinx-autobuild # sphinx-basic-ng # sphinx-codeautolink -sphinx-autobuild==2024.10.3 +sphinx-autobuild==2025.8.25 # via -r requirements/tools.in sphinx-basic-ng==1.0.0b2 # via furo @@ -326,17 +314,8 @@ starlette==0.51.0 # via sphinx-autobuild tokenize-rt==6.2.0 # via pyupgrade -tomli==2.3.0 - # via - # autoflake - # black - # build - # mypy - # pip-tools - # pyproject-api - # pytest - # sphinx - # tox +tomli==2.4.0 + # via -r requirements/tools.in tox==4.34.1 # via -r requirements/tools.in traitlets==5.14.3 @@ -360,20 +339,10 @@ types-setuptools==80.9.0.20251223 typing-extensions==4.15.0 # via # -r requirements/tools.in - # anyio - # asgiref # beautifulsoup4 - # black - # cryptography - # exceptiongroup - # ipython # mypy # pyright # sortedcontainers-stubs - # starlette - # tox - # uvicorn - # virtualenv unidecode==1.4.0 # via pelican urllib3==2.6.3 @@ -396,8 +365,6 @@ websockets==16.0 # via sphinx-autobuild wheel==0.45.1 # via pip-tools -zipp==3.23.0 - # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: pip==25.3 diff --git a/tooling/README.rst b/tooling/README.md similarity index 67% rename from tooling/README.rst rename to tooling/README.md index 7d42af830f..b2c2276d7c 100644 --- a/tooling/README.rst +++ b/tooling/README.md @@ -1,6 +1,4 @@ -======================== -Hypothesis Build Tooling -======================== +# Hypothesis Build Tooling This is a piece of software for managing Hypothesis's build tasks, releases, etc. It's very Hypothesis specific, though it may become less so in the future. diff --git a/tooling/src/hypothesistooling/__main__.py b/tooling/src/hypothesistooling/__main__.py index 47fbc324bc..01de77dd5a 100644 --- a/tooling/src/hypothesistooling/__main__.py +++ b/tooling/src/hypothesistooling/__main__.py @@ -631,7 +631,7 @@ def run_tox(task, version, *args): "pypy3.10": "pypy3.10-7.3.19", "pypy3.11": "pypy3.11-7.3.20", } -ci_version = "3.10" # Keep this in sync with GH Actions main.yml and .readthedocs.yml +ci_version = "3.14" # Keep this in sync with GH Actions main.yml and .readthedocs.yml python_tests = task( if_changed=( @@ -689,8 +689,10 @@ def standard_tox_task(name, py=ci_version): ) -# standard_tox_task("py310-pytest46", py="3.10") -standard_tox_task("pytest62") +standard_tox_task("py311-pytest62", py="3.11") # hits "ast.Str is deprecated" in 3.12+ +standard_tox_task("pytest74") +standard_tox_task("pytest84") +standard_tox_task("pytest9") dj_version = max(ci_version, "3.12") for n in DJANGO_VERSIONS: @@ -698,10 +700,15 @@ def standard_tox_task(name, py=ci_version): # we also test no-contrib on the latest django version standard_tox_task("django-nocontrib", py=dj_version) -for n in [13, 14, 15, 20, 21, 22]: - standard_tox_task(f"pandas{n}") +# test each pandas version with the latest python version they support standard_tox_task("py310-pandas11", py="3.10") standard_tox_task("py310-pandas12", py="3.10") +standard_tox_task("py310-pandas13", py="3.10") +standard_tox_task("py310-pandas14", py="3.10") +standard_tox_task("py311-pandas15", py="3.11") +standard_tox_task("py311-pandas20", py="3.11") +standard_tox_task("py312-pandas21", py="3.12") +standard_tox_task("py313-pandas22", py="3.13") for kind in ("cover", "nocover", "niche", "custom"): standard_tox_task(f"crosshair-{kind}") diff --git a/whole_repo_tests/types/revealed_types.py b/whole_repo_tests/types/revealed_types.py index 2cc7c22398..fd211c511f 100644 --- a/whole_repo_tests/types/revealed_types.py +++ b/whole_repo_tests/types/revealed_types.py @@ -94,7 +94,7 @@ class DifferingRevealedTypes(NamedTuple): NUMPY_REVEALED_TYPES = [ ( 'arrays(dtype=np.dtype("int32"), shape=1)', - "ndarray[tuple[int, ...], dtype[signedinteger[_32Bit]]]", + "ndarray[tuple[Any, ...], dtype[signedinteger[_32Bit]]]", ), # ( # "arrays(dtype=np.dtype(int), shape=1)", @@ -199,15 +199,15 @@ class DifferingRevealedTypes(NamedTuple): ), ( "integer_array_indices(shape=(2, 3))", - "tuple[ndarray[tuple[int, ...], dtype[signedinteger[Any]]], ...]", + "tuple[ndarray[tuple[Any, ...], dtype[signedinteger[Any]]], ...]", ), ( 'integer_array_indices(shape=(2, 3), dtype=np.dtype("int32"))', - "tuple[ndarray[tuple[int, ...], dtype[signedinteger[_32Bit]]], ...]", + "tuple[ndarray[tuple[Any, ...], dtype[signedinteger[_32Bit]]], ...]", ), ( 'integer_array_indices(shape=(2, 3), dtype=np.dtype("uint8"))', - "tuple[ndarray[tuple[int, ...], dtype[unsignedinteger[_8Bit]]], ...]", + "tuple[ndarray[tuple[Any, ...], dtype[unsignedinteger[_8Bit]]], ...]", ), # basic_indices with allow_ellipsis=False (no EllipsisType differences) (