From 37367f935de706d534358ceb5c4f3dd5953408f6 Mon Sep 17 00:00:00 2001 From: Bahram Farahmand Date: Mon, 31 Mar 2025 16:56:57 +0300 Subject: [PATCH 1/9] Fix for issue #13047 --- AUTHORS | 1 + changelog/13047.bugfix.rst | 1 + src/_pytest/python_api.py | 20 ++++++++++++++++---- 3 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 changelog/13047.bugfix.rst diff --git a/AUTHORS b/AUTHORS index 95e6b13f11e..9004008bfa5 100644 --- a/AUTHORS +++ b/AUTHORS @@ -58,6 +58,7 @@ Ashley Whetter Aviral Verma Aviv Palivoda Babak Keyvani +Bahram Farahmand Barney Gale Ben Brown Ben Gartner diff --git a/changelog/13047.bugfix.rst b/changelog/13047.bugfix.rst new file mode 100644 index 00000000000..a9d03c9b8b8 --- /dev/null +++ b/changelog/13047.bugfix.rst @@ -0,0 +1 @@ +* Fixed an issue in equality checks where `bool` and `numpy.bool_` types were not handled consistently. This ensures proper handling of native and numpy boolean types. See #13047. diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index af078e25256..3f899a24ea8 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -421,14 +421,26 @@ def __repr__(self) -> str: def __eq__(self, actual) -> bool: """Return whether the given value is equal to the expected value within the pre-specified tolerance.""" + + def is_bool(val: Any) -> bool: + # Check if `val` is a native bool or numpy bool. + if isinstance(val, bool): + return True + try: + import numpy as np + + return isinstance(val, np.bool_) + except ImportError: + return False + asarray = _as_numpy_array(actual) if asarray is not None: # Call ``__eq__()`` manually to prevent infinite-recursion with # numpy<1.13. See #3748. return all(self.__eq__(a) for a in asarray.flat) - # Short-circuit exact equality, except for bool - if isinstance(self.expected, bool) and not isinstance(actual, bool): + # Short-circuit exact equality, except for bool and np.bool_ + if is_bool(self.expected) and not is_bool(actual): return False elif actual == self.expected: return True @@ -436,8 +448,8 @@ def __eq__(self, actual) -> bool: # If either type is non-numeric, fall back to strict equality. # NB: we need Complex, rather than just Number, to ensure that __abs__, # __sub__, and __float__ are defined. Also, consider bool to be - # nonnumeric, even though it has the required arithmetic. - if isinstance(self.expected, bool) or not ( + # non-numeric, even though it has the required arithmetic. + if is_bool(self.expected) or not ( isinstance(self.expected, (Complex, Decimal)) and isinstance(actual, (Complex, Decimal)) ): From f58bd366d5c0916e93b4f3f79484846a338b49cb Mon Sep 17 00:00:00 2001 From: Bahram Farahmand Date: Tue, 1 Apr 2025 15:04:33 +0300 Subject: [PATCH 2/9] Update changelog/13047.bugfix.rst Co-authored-by: Bruno Oliveira --- changelog/13047.bugfix.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/13047.bugfix.rst b/changelog/13047.bugfix.rst index a9d03c9b8b8..365801228a6 100644 --- a/changelog/13047.bugfix.rst +++ b/changelog/13047.bugfix.rst @@ -1 +1 @@ -* Fixed an issue in equality checks where `bool` and `numpy.bool_` types were not handled consistently. This ensures proper handling of native and numpy boolean types. See #13047. +* Fixed an issue in equality checks where `bool` and `numpy.bool_` types were not handled consistently. From feac0be3000426a357f7a23c5509a709cf2ea535 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 12:05:40 +0000 Subject: [PATCH 3/9] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- changelog/13047.bugfix.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/13047.bugfix.rst b/changelog/13047.bugfix.rst index 365801228a6..884cc834ccc 100644 --- a/changelog/13047.bugfix.rst +++ b/changelog/13047.bugfix.rst @@ -1 +1 @@ -* Fixed an issue in equality checks where `bool` and `numpy.bool_` types were not handled consistently. +* Fixed an issue in equality checks where `bool` and `numpy.bool_` types were not handled consistently. From c59fd5ab81afdefb4b84cb4f9f4c7af3a13baa01 Mon Sep 17 00:00:00 2001 From: Bahram Farahmand Date: Tue, 1 Apr 2025 18:49:56 +0300 Subject: [PATCH 4/9] Add tests for numpy booleans --- testing/python/approx.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/testing/python/approx.py b/testing/python/approx.py index 7eba4755c01..ad00cbb7b7b 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -641,11 +641,17 @@ def test_complex(self): assert approx(x, rel=5e-7, abs=0) != a def test_expecting_bool(self) -> None: + np = pytest.importorskip("numpy") assert True == approx(True) # noqa: E712 assert False == approx(False) # noqa: E712 assert True != approx(False) # noqa: E712 assert True != approx(False, abs=2) # noqa: E712 assert 1 != approx(True) + assert np.False_ != approx(True) + assert np.True_ != approx(False) + assert np.True_ == approx(True) + assert np.False_ == approx(False) + assert np.True_ != approx(False, abs=2) def test_list(self): actual = [1 + 1e-7, 2 + 1e-8] From 10dfe3793c7df1a5c5c5b82b78e09ba7c975b37b Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 1 Apr 2025 16:07:27 -0300 Subject: [PATCH 5/9] Update approx.py --- testing/python/approx.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/testing/python/approx.py b/testing/python/approx.py index ad00cbb7b7b..bf290ea3b07 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -641,12 +641,15 @@ def test_complex(self): assert approx(x, rel=5e-7, abs=0) != a def test_expecting_bool(self) -> None: - np = pytest.importorskip("numpy") assert True == approx(True) # noqa: E712 assert False == approx(False) # noqa: E712 assert True != approx(False) # noqa: E712 assert True != approx(False, abs=2) # noqa: E712 - assert 1 != approx(True) + assert 1 != approx(True) + + def test_expecting_bool_numpy(self) -> None: + """Check approx comparing with numpy.bool (#13047).""" + np = pytest.importorskip("numpy") assert np.False_ != approx(True) assert np.True_ != approx(False) assert np.True_ == approx(True) From 4d407c35ca3101333f017b18c7755b485f7f3f54 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 19:09:34 +0000 Subject: [PATCH 6/9] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- testing/python/approx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/python/approx.py b/testing/python/approx.py index bf290ea3b07..01b58782cdb 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -645,7 +645,7 @@ def test_expecting_bool(self) -> None: assert False == approx(False) # noqa: E712 assert True != approx(False) # noqa: E712 assert True != approx(False, abs=2) # noqa: E712 - assert 1 != approx(True) + assert 1 != approx(True) def test_expecting_bool_numpy(self) -> None: """Check approx comparing with numpy.bool (#13047).""" From 996c27bcb9cc59c5884ea4b82c06a62e23614ab9 Mon Sep 17 00:00:00 2001 From: Bahram Farahmand Date: Wed, 2 Apr 2025 00:57:43 +0300 Subject: [PATCH 7/9] Add more explanation to 13047.bugfix.rst --- changelog/13047.bugfix.rst | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/changelog/13047.bugfix.rst b/changelog/13047.bugfix.rst index 884cc834ccc..575f7282110 100644 --- a/changelog/13047.bugfix.rst +++ b/changelog/13047.bugfix.rst @@ -1 +1,23 @@ -* Fixed an issue in equality checks where `bool` and `numpy.bool_` types were not handled consistently. +Bug Fix :func:`pytest.approx` to restore consistent handling of equality checks between `bool` and `numpy.bool_` types. + * This behavior was accidentally changed in version 8.3.4 and has now been restored to match the behavior of version 8.3.3. + * Users relying on comparisons between `bool` and `numpy.bool_` can now expect consistent results as before. + + Example: + + **Behavior on pytest 8.3.4:** + + .. code-block:: console + + >>> import numpy as np + >>> from pytest import approx + >>> np.False_ == pytest.approx(False) + False + + **Behavior on pytest 8.3.3:** + + .. code-block:: console + + >>> import numpy as np + >>> from pytest import approx + >>> np.False_ == pytest.approx(False) + True From e14787ab9831b95ddd799e32380d0b1d9a2d0726 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 1 Apr 2025 19:36:58 -0300 Subject: [PATCH 8/9] Improve changelog --- changelog/13047.bugfix.rst | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/changelog/13047.bugfix.rst b/changelog/13047.bugfix.rst index 575f7282110..64ee28f252e 100644 --- a/changelog/13047.bugfix.rst +++ b/changelog/13047.bugfix.rst @@ -1,23 +1,17 @@ -Bug Fix :func:`pytest.approx` to restore consistent handling of equality checks between `bool` and `numpy.bool_` types. - * This behavior was accidentally changed in version 8.3.4 and has now been restored to match the behavior of version 8.3.3. - * Users relying on comparisons between `bool` and `numpy.bool_` can now expect consistent results as before. +Restore :func:`pytest.approx` handling of equality checks between `bool` and `numpy.bool_` types. - Example: +Comparing `bool` and `numpy.bool_` using :func:`pytest.approx` accidentally changed in version `8.3.4` and `8.3.5` to no longer match: - **Behavior on pytest 8.3.4:** +.. code-block:: python - .. code-block:: console + >>> import numpy as np + >>> from pytest import approx + >>> [np.True_, np.True_] == pytest.approx([True, True]) + False - >>> import numpy as np - >>> from pytest import approx - >>> np.False_ == pytest.approx(False) - False +This has now been fixed: - **Behavior on pytest 8.3.3:** +.. code-block:: python - .. code-block:: console - - >>> import numpy as np - >>> from pytest import approx - >>> np.False_ == pytest.approx(False) - True + >>> [np.True_, np.True_] == pytest.approx([True, True]) + True From e81028e9bc5807db1f7f8fbb1a6fcb972f5868fd Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 1 Apr 2025 19:44:58 -0300 Subject: [PATCH 9/9] Fix code-block in changelog --- changelog/13047.bugfix.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog/13047.bugfix.rst b/changelog/13047.bugfix.rst index 64ee28f252e..399e860505c 100644 --- a/changelog/13047.bugfix.rst +++ b/changelog/13047.bugfix.rst @@ -2,7 +2,7 @@ Restore :func:`pytest.approx` handling of equality checks between `bool` and `nu Comparing `bool` and `numpy.bool_` using :func:`pytest.approx` accidentally changed in version `8.3.4` and `8.3.5` to no longer match: -.. code-block:: python +.. code-block:: pycon >>> import numpy as np >>> from pytest import approx @@ -11,7 +11,7 @@ Comparing `bool` and `numpy.bool_` using :func:`pytest.approx` accidentally chan This has now been fixed: -.. code-block:: python +.. code-block:: pycon >>> [np.True_, np.True_] == pytest.approx([True, True]) True