diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 04f5f2216d..12c8c20f3c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -44,9 +44,9 @@ jobs: - check-py311-cover - check-py311-nocover - check-py311-niche - # - check-py312-cover - # - check-py312-nocover - # - check-py312-niche + - check-py312-cover + - check-py312-nocover + - check-py312-niche # - check-py313-cover - check-quality ## Skip all the (inactive/old) Rust and Ruby tests pending fixes diff --git a/hypothesis-python/RELEASE.rst b/hypothesis-python/RELEASE.rst new file mode 100644 index 0000000000..8744f26aa8 --- /dev/null +++ b/hypothesis-python/RELEASE.rst @@ -0,0 +1,4 @@ +RELEASE_TYPE: patch + +We now test against Python 3.12 beta in CI, and this patch +fixes some new deprecations. diff --git a/hypothesis-python/src/hypothesis/strategies/_internal/core.py b/hypothesis-python/src/hypothesis/strategies/_internal/core.py index 9764cde3c0..73ac4f9059 100644 --- a/hypothesis-python/src/hypothesis/strategies/_internal/core.py +++ b/hypothesis-python/src/hypothesis/strategies/_internal/core.py @@ -1123,6 +1123,11 @@ def from_type_guarded(thing): if types.is_a_union(thing): args = sorted(thing.__args__, key=types.type_sorting_key) return one_of([_from_type(t, recurse_guard) for t in args]) + # We also have a special case for TypeVars. + # They are represented as instances like `~T` when they come here. + # We need to work with their type instead. + if isinstance(thing, TypeVar) and type(thing) in types._global_type_lookup: + return as_strategy(types._global_type_lookup[type(thing)], thing) if not types.is_a_type(thing): if isinstance(thing, str): # See https://github.com/HypothesisWorks/hypothesis/issues/3016 @@ -1190,11 +1195,6 @@ def from_type_guarded(thing): mapping={k: v for k, v in anns.items() if k not in optional}, optional={k: v for k, v in anns.items() if k in optional}, ) - # We also have a special case for TypeVars. - # They are represented as instances like `~T` when they come here. - # We need to work with their type instead. - if isinstance(thing, TypeVar) and type(thing) in types._global_type_lookup: - return as_strategy(types._global_type_lookup[type(thing)], thing) # If there's no explicitly registered strategy, maybe a subtype of thing # is registered - if so, we can resolve it to the subclass strategy. diff --git a/hypothesis-python/src/hypothesis/strategies/_internal/types.py b/hypothesis-python/src/hypothesis/strategies/_internal/types.py index ba134ffa58..c2a8637f4a 100644 --- a/hypothesis-python/src/hypothesis/strategies/_internal/types.py +++ b/hypothesis-python/src/hypothesis/strategies/_internal/types.py @@ -429,7 +429,7 @@ def from_typing_type(thing): # Except for sequences of integers, or unions which include integer! # See https://github.com/HypothesisWorks/hypothesis/issues/2257 # - # This block drops ByteString from the types that can be generated + # This block drops bytes from the types that can be generated # if there is more than one allowed type, and the element type is # not either `int` or a Union with `int` as one of its elements. elem_type = (getattr(thing, "__args__", None) or ["not int"])[0] @@ -450,7 +450,7 @@ def from_typing_type(thing): 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. ByteString from both the typing + # 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): diff --git a/hypothesis-python/tests/cover/test_lookup.py b/hypothesis-python/tests/cover/test_lookup.py index 409c96baaf..7c157f2b55 100644 --- a/hypothesis-python/tests/cover/test_lookup.py +++ b/hypothesis-python/tests/cover/test_lookup.py @@ -46,7 +46,9 @@ t for t in types._global_type_lookup # We ignore TypeVar, because it is not a Generic type: - if isinstance(t, types.typing_root_type) and t != typing.TypeVar + if isinstance(t, types.typing_root_type) + and t != typing.TypeVar + and (sys.version_info[:2] <= (3, 11) or t != typing.ByteString) ), key=str, ) @@ -110,7 +112,11 @@ def test_typing_Type_Union(ex): @given(data=st.data()) def test_rare_types(data, typ): ex = data.draw(from_type(typ)) - assert isinstance(ex, typ) + with warnings.catch_warnings(): + if sys.version_info[:2] >= (3, 12): + warnings.simplefilter("ignore", DeprecationWarning) + # ByteString is deprecated in Python 3.12 + assert isinstance(ex, typ) class Elem: @@ -752,7 +758,7 @@ def test_inference_on_generic_collections_abc_aliases(typ, data): def test_bytestring_not_treated_as_generic_sequence(val): # Check that we don't fall into the specific problem from # https://github.com/HypothesisWorks/hypothesis/issues/2257 - assert not isinstance(val, typing.ByteString) + assert not isinstance(val, bytes) # Check it hasn't happened again from some other non-generic sequence type. for x in val: assert isinstance(x, set) @@ -764,7 +770,7 @@ def test_bytestring_not_treated_as_generic_sequence(val): def test_bytestring_is_valid_sequence_of_int_and_parent_classes(type_): find_any( st.from_type(typing.Sequence[type_]), - lambda val: isinstance(val, typing.ByteString), + lambda val: isinstance(val, bytes), ) diff --git a/hypothesis-python/tests/cover/test_type_lookup.py b/hypothesis-python/tests/cover/test_type_lookup.py index e5fb1c594e..5babf51103 100644 --- a/hypothesis-python/tests/cover/test_type_lookup.py +++ b/hypothesis-python/tests/cover/test_type_lookup.py @@ -211,7 +211,11 @@ def test_uninspectable_from_type(): def _check_instances(t): # See https://github.com/samuelcolvin/pydantic/discussions/2508 - return t.__module__ != "typing" and not t.__module__.startswith("pydantic") + return ( + t.__module__ != "typing" + and t.__name__ != "ByteString" + and not t.__module__.startswith("pydantic") + ) @pytest.mark.parametrize( diff --git a/hypothesis-python/tests/cover/test_type_lookup_forward_ref.py b/hypothesis-python/tests/cover/test_type_lookup_forward_ref.py index 488023b6c7..7e4ccfcd36 100644 --- a/hypothesis-python/tests/cover/test_type_lookup_forward_ref.py +++ b/hypothesis-python/tests/cover/test_type_lookup_forward_ref.py @@ -134,7 +134,7 @@ def test_bound_type_cheking_only_forward_ref(): st.builds(typechecking_only_fun).example() -def test_bound_type_cheking_only_forward_ref_wrong_type(): +def test_bound_type_checking_only_forward_ref_wrong_type(): """We should check ``ForwardRef`` parameter name correctly.""" with utils.temp_registered(ForwardRef("WrongType"), st.just(1)): with pytest.raises(ResolutionFailed): diff --git a/hypothesis-python/tests/datetime/test_dateutil_timezones.py b/hypothesis-python/tests/datetime/test_dateutil_timezones.py index 0f6181831f..e3afd61fbd 100644 --- a/hypothesis-python/tests/datetime/test_dateutil_timezones.py +++ b/hypothesis-python/tests/datetime/test_dateutil_timezones.py @@ -9,19 +9,27 @@ # obtain one at https://mozilla.org/MPL/2.0/. import datetime as dt +import sys +import warnings import pytest -from dateutil import tz, zoneinfo from hypothesis import assume, given from hypothesis.errors import FailedHealthCheck, InvalidArgument -from hypothesis.extra.dateutil import timezones from hypothesis.strategies import data, datetimes, just, sampled_from, times from hypothesis.strategies._internal.datetime import datetime_does_not_exist from tests.common.debug import assert_all_examples, find_any, minimal from tests.common.utils import fails_with +with warnings.catch_warnings(): + if sys.version_info[:2] >= (3, 12): + # Prior to https://github.com/dateutil/dateutil/pull/1285/ + warnings.simplefilter("ignore", DeprecationWarning) + from dateutil import tz, zoneinfo + +from hypothesis.extra.dateutil import timezones + def test_utc_is_minimal(): assert tz.UTC is minimal(timezones()) diff --git a/hypothesis-python/tests/datetime/test_pytz_timezones.py b/hypothesis-python/tests/datetime/test_pytz_timezones.py index 1023401d10..e2c9658216 100644 --- a/hypothesis-python/tests/datetime/test_pytz_timezones.py +++ b/hypothesis-python/tests/datetime/test_pytz_timezones.py @@ -9,14 +9,13 @@ # obtain one at https://mozilla.org/MPL/2.0/. import datetime as dt +import sys +import warnings import pytest -import pytz -from dateutil.tz import datetime_exists from hypothesis import assume, given from hypothesis.errors import InvalidArgument -from hypothesis.extra.pytz import timezones from hypothesis.strategies import data, datetimes, just, sampled_from, times from hypothesis.strategies._internal.datetime import datetime_does_not_exist @@ -27,6 +26,16 @@ minimal, ) +with warnings.catch_warnings(): + if sys.version_info[:2] >= (3, 12): + # See https://github.com/stub42/pytz/issues/105 and + # https://github.com/dateutil/dateutil/pull/1285/ + warnings.simplefilter("ignore", DeprecationWarning) + import pytz + from dateutil.tz import datetime_exists + +from hypothesis.extra.pytz import timezones + def test_utc_is_minimal(): assert pytz.UTC is minimal(timezones()) diff --git a/hypothesis-python/tests/nocover/test_from_type_recipe.py b/hypothesis-python/tests/nocover/test_from_type_recipe.py index 4d4464f8a6..d640ee6816 100644 --- a/hypothesis-python/tests/nocover/test_from_type_recipe.py +++ b/hypothesis-python/tests/nocover/test_from_type_recipe.py @@ -11,7 +11,14 @@ from hypothesis import given, strategies as st from hypothesis.strategies._internal.types import _global_type_lookup -TYPES = sorted((x for x in _global_type_lookup if x.__module__ != "typing"), key=str) +TYPES = sorted( + ( + x + for x in _global_type_lookup + if x.__module__ != "typing" and x.__name__ != "ByteString" + ), + key=str, +) def everything_except(excluded_types):