diff --git a/backport-fix-provide-correct-type-bounds-on-sequence-matchers.patch b/backport-fix-provide-correct-type-bounds-on-sequence-matchers.patch new file mode 100644 index 0000000000000000000000000000000000000000..bcbff40e50fe38b57df1dd00b79f5b0a704525bf --- /dev/null +++ b/backport-fix-provide-correct-type-bounds-on-sequence-matchers.patch @@ -0,0 +1,169 @@ +From 3a96f7a79a342abb2328a72885eb0b21e1b20823 Mon Sep 17 00:00:00 2001 +From: Chris Rose +Date: Sat, 6 Aug 2022 07:48:17 -0700 +Subject: [PATCH] fix: provide correct type bounds on sequence matchers + +fixes #207 + +`Matcher[object]` is too tight of a bound on the output type; what we're +asserting is that a matcher for `Any` type is what `has_properties` will +do +--- + changelog.d/207.bugfix.rst | 1 + + setup.py | 2 ++ + src/hamcrest/__init__.py | 2 +- + src/hamcrest/core/assert_that.py | 14 +++++------ + src/hamcrest/library/object/hasproperty.py | 6 ++--- + .../type-hinting/library/collection/test_empty.yml | 2 +- + .../library/collection/test_generics.yml | 27 ++++++++++++++++++++++ + 7 files changed, 42 insertions(+), 12 deletions(-) + create mode 100644 changelog.d/207.bugfix.rst + create mode 100644 tests/type-hinting/library/collection/test_generics.yml + +diff --git a/changelog.d/207.bugfix.rst b/changelog.d/207.bugfix.rst +new file mode 100644 +index 0000000..ecc2dac +--- /dev/null ++++ b/changelog.d/207.bugfix.rst +@@ -0,0 +1 @@ ++``has_properties`` now returns ``Matcher[Any]`` type, which addresses type checking errors when nested as a matcher. +diff --git a/setup.py b/setup.py +index b3ea654..97057a9 100755 +--- a/setup.py ++++ b/setup.py +@@ -42,6 +42,8 @@ TESTS_BASIC = [ + # Can't use 0.940: https://github.com/python/mypy/issues/12339 + "mypy!=0.940; platform_python_implementation != 'PyPy'", + "types-mock", ++ "dataclasses; python_version<'3.7'", ++ "types-dataclasses; python_version<'3.7'", + ] + TESTS_NUMPY = ["numpy"] + DEV_TOOLS = [ +diff --git a/src/hamcrest/__init__.py b/src/hamcrest/__init__.py +index b307591..90573fe 100644 +--- a/src/hamcrest/__init__.py ++++ b/src/hamcrest/__init__.py +@@ -2,7 +2,7 @@ from hamcrest.core import * + from hamcrest.library import * + from hamcrest import core, library + +-__version__ = "2.0.3" ++__version__ = "2.0.4" + __author__ = "Chris Rose" + __copyright__ = "Copyright 2020 hamcrest.org" + __license__ = "BSD, see License.txt" +diff --git a/src/hamcrest/core/assert_that.py b/src/hamcrest/core/assert_that.py +index c8882bb..a31cf3a 100644 +--- a/src/hamcrest/core/assert_that.py ++++ b/src/hamcrest/core/assert_that.py +@@ -16,16 +16,16 @@ T = TypeVar("T") + + + @overload +-def assert_that(actual: T, matcher: Matcher[T], reason: str = "") -> None: ++def assert_that(actual_or_assertion: T, matcher: Matcher[T], reason: str = "") -> None: + ... + + + @overload +-def assert_that(assertion: bool, reason: str = "") -> None: ++def assert_that(actual_or_assertion: bool, reason: str = "") -> None: + ... + + +-def assert_that(actual, matcher=None, reason=""): ++def assert_that(actual_or_assertion, matcher=None, reason=""): + """Asserts that actual value satisfies matcher. (Can also assert plain + boolean condition.) + +@@ -55,11 +55,11 @@ def assert_that(actual, matcher=None, reason=""): + + """ + if isinstance(matcher, Matcher): +- _assert_match(actual=actual, matcher=matcher, reason=reason) ++ _assert_match(actual=actual_or_assertion, matcher=matcher, reason=reason) + else: +- if isinstance(actual, Matcher): +- warnings.warn("arg1 should be boolean, but was {}".format(type(actual))) +- _assert_bool(assertion=cast(bool, actual), reason=cast(str, matcher)) ++ if isinstance(actual_or_assertion, Matcher): ++ warnings.warn("arg1 should be boolean, but was {}".format(type(actual_or_assertion))) ++ _assert_bool(assertion=cast(bool, actual_or_assertion), reason=cast(str, matcher)) + + + def _assert_match(actual: T, matcher: Matcher[T], reason: str) -> None: +diff --git a/src/hamcrest/library/object/hasproperty.py b/src/hamcrest/library/object/hasproperty.py +index b27036d..ea2519f 100644 +--- a/src/hamcrest/library/object/hasproperty.py ++++ b/src/hamcrest/library/object/hasproperty.py +@@ -94,19 +94,19 @@ def has_property(name: str, match: Union[None, Matcher[V], V] = None) -> Matcher + + # Keyword argument form + @overload +-def has_properties(**keys_valuematchers: Union[Matcher[V], V]) -> Matcher[object]: ++def has_properties(**keys_valuematchers: Union[Matcher[V], V]) -> Matcher[Any]: + ... + + + # Name to matcher dict form + @overload +-def has_properties(keys_valuematchers: Mapping[str, Union[Matcher[V], V]]) -> Matcher[object]: ++def has_properties(keys_valuematchers: Mapping[str, Union[Matcher[V], V]]) -> Matcher[Any]: + ... + + + # Alternating name/matcher form + @overload +-def has_properties(*keys_valuematchers: Any) -> Matcher[object]: ++def has_properties(*keys_valuematchers: Any) -> Matcher[Any]: + ... + + +diff --git a/tests/type-hinting/library/collection/test_empty.yml b/tests/type-hinting/library/collection/test_empty.yml +index 5264802..cf815c7 100644 +--- a/tests/type-hinting/library/collection/test_empty.yml ++++ b/tests/type-hinting/library/collection/test_empty.yml +@@ -5,4 +5,4 @@ + from hamcrest import assert_that, is_, empty + + assert_that([], empty()) +- assert_that(99, empty()) # E: Cannot infer type argument 1 of "assert_that" +\ No newline at end of file ++ assert_that(99, empty()) # E: Cannot infer type argument 1 of "assert_that" +diff --git a/tests/type-hinting/library/collection/test_generics.yml b/tests/type-hinting/library/collection/test_generics.yml +new file mode 100644 +index 0000000..4666d5f +--- /dev/null ++++ b/tests/type-hinting/library/collection/test_generics.yml +@@ -0,0 +1,27 @@ ++- case: valid_has_items_has_properties ++ skip: platform.python_implementation() == "PyPy" ++ main: | ++ from dataclasses import dataclass ++ from hamcrest import assert_that, has_items, has_properties ++ ++ @dataclass ++ class Example: ++ name: str ++ ++ items = [Example("dave"), Example("wave")] ++ ++ a = assert_that(items, has_items(has_properties(name="dave"))) ++ ++- case: valid_has_item_has_properties ++ skip: platform.python_implementation() == "PyPy" ++ main: | ++ from dataclasses import dataclass ++ from hamcrest import assert_that, has_item, has_properties ++ ++ @dataclass ++ class Example: ++ name: str ++ ++ items = [Example("dave"), Example("wave")] ++ matcher = has_item(has_properties(name="dave")) ++ a = assert_that(items, matcher) +-- +2.9.3.windows.1 + diff --git a/python-hamcrest.spec b/python-hamcrest.spec index 2ef73c1ecc48c3b2941e4f6682946942516a1751..0757e8975a72161c90e8fabdd54f250750b17b0d 100644 --- a/python-hamcrest.spec +++ b/python-hamcrest.spec @@ -1,6 +1,6 @@ Name: python-hamcrest Version: 2.0.3 -Release: 4 +Release: 5 Summary: Hamcrest matchers for Python License: BSD-3-Clause URL: https://github.com/hamcrest/PyHamcrest @@ -8,6 +8,7 @@ Source0: https://github.com/hamcrest/PyHamcrest/archive/V2.0.3/%{name}- Patch0: numpy-1.20.0-alias-depr.patch Patch1: backport-nit-fix-a-minor-type-annotation-bug-in-isdict_contai.patch Patch2: Ordered-comparison-matchers-should-fail-for-incompar.patch +Patch3: backport-fix-provide-correct-type-bounds-on-sequence-matchers.patch BuildArch: noarch %description @@ -49,6 +50,9 @@ PYTHONPATH=%{buildroot}%{python3_sitelib} py.test-%{python3_version} -v %{python3_sitelib}/* %changelog +* Fri May 10 2024 wuzhaomin - 2.0.3-5 +- provide correct type bounds on sequence matchers + * Fri May 10 2024 lilu - 2.0.3-4 - Fix the error that ordered comparison matchers should fail for incomparable types