Skip to content

Commit 70d0c9d

Browse files
Fix approx when mixing numpy.bool and bool (#13338) (#13342)
Fix #13047 --------- Co-authored-by: Bruno Oliveira <[email protected]> (cherry picked from commit 0c45497) Co-authored-by: Bahram Farahmand <[email protected]>
1 parent 2b35bfa commit 70d0c9d

File tree

4 files changed

+43
-4
lines changed

4 files changed

+43
-4
lines changed

AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ Ashish Kurmi
5454
Aviral Verma
5555
Aviv Palivoda
5656
Babak Keyvani
57+
Bahram Farahmand
5758
Barney Gale
5859
Ben Brown
5960
Ben Gartner

changelog/13047.bugfix.rst

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
Restore :func:`pytest.approx` handling of equality checks between `bool` and `numpy.bool_` types.
2+
3+
Comparing `bool` and `numpy.bool_` using :func:`pytest.approx` accidentally changed in version `8.3.4` and `8.3.5` to no longer match:
4+
5+
.. code-block:: pycon
6+
7+
>>> import numpy as np
8+
>>> from pytest import approx
9+
>>> [np.True_, np.True_] == pytest.approx([True, True])
10+
False
11+
12+
This has now been fixed:
13+
14+
.. code-block:: pycon
15+
16+
>>> [np.True_, np.True_] == pytest.approx([True, True])
17+
True

src/_pytest/python_api.py

+16-4
Original file line numberDiff line numberDiff line change
@@ -426,23 +426,35 @@ def __repr__(self) -> str:
426426
def __eq__(self, actual) -> bool:
427427
"""Return whether the given value is equal to the expected value
428428
within the pre-specified tolerance."""
429+
430+
def is_bool(val: Any) -> bool:
431+
# Check if `val` is a native bool or numpy bool.
432+
if isinstance(val, bool):
433+
return True
434+
try:
435+
import numpy as np
436+
437+
return isinstance(val, np.bool_)
438+
except ImportError:
439+
return False
440+
429441
asarray = _as_numpy_array(actual)
430442
if asarray is not None:
431443
# Call ``__eq__()`` manually to prevent infinite-recursion with
432444
# numpy<1.13. See #3748.
433445
return all(self.__eq__(a) for a in asarray.flat)
434446

435-
# Short-circuit exact equality, except for bool
436-
if isinstance(self.expected, bool) and not isinstance(actual, bool):
447+
# Short-circuit exact equality, except for bool and np.bool_
448+
if is_bool(self.expected) and not is_bool(actual):
437449
return False
438450
elif actual == self.expected:
439451
return True
440452

441453
# If either type is non-numeric, fall back to strict equality.
442454
# NB: we need Complex, rather than just Number, to ensure that __abs__,
443455
# __sub__, and __float__ are defined. Also, consider bool to be
444-
# nonnumeric, even though it has the required arithmetic.
445-
if isinstance(self.expected, bool) or not (
456+
# non-numeric, even though it has the required arithmetic.
457+
if is_bool(self.expected) or not (
446458
isinstance(self.expected, (Complex, Decimal))
447459
and isinstance(actual, (Complex, Decimal))
448460
):

testing/python/approx.py

+9
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,15 @@ def test_expecting_bool(self) -> None:
610610
assert True != approx(False, abs=2) # noqa: E712
611611
assert 1 != approx(True)
612612

613+
def test_expecting_bool_numpy(self) -> None:
614+
"""Check approx comparing with numpy.bool (#13047)."""
615+
np = pytest.importorskip("numpy")
616+
assert np.False_ != approx(True)
617+
assert np.True_ != approx(False)
618+
assert np.True_ == approx(True)
619+
assert np.False_ == approx(False)
620+
assert np.True_ != approx(False, abs=2)
621+
613622
def test_list(self):
614623
actual = [1 + 1e-7, 2 + 1e-8]
615624
expected = [1, 2]

0 commit comments

Comments
 (0)