From e73cfa3ffbfeeac650fa59db621f1d31fe7dc00c Mon Sep 17 00:00:00 2001 From: Patrick Date: Mon, 2 Nov 2026 00:23:38 +0700 Subject: [PATCH 1/2] fix(approx): unbox numpy scalars in sequence comparison Numpy integer scalars on the actual side wrapped around on subtraction (0 - uint8(1) -> 255), corrupting both the equality check and the failure report. Unbox them in ApproxSequenceLike._yield_comparisons and _repr_compare, mirroring ApproxNumpy. Closes pytest-dev/pytest#15314 --- changelog/15314.bugfix.rst | 1 + src/_pytest/approx.py | 5 ++++- testing/python/approx.py | 10 ++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 changelog/15314.bugfix.rst diff --git a/changelog/15314.bugfix.rst b/changelog/15314.bugfix.rst new file mode 100644 index 00000000000..f3ce8b36040 --- /dev/null +++ b/changelog/15314.bugfix.rst @@ -0,0 +1 @@ +Fixed :func:`pytest.approx` reporting wrong differences (and wrong equality results) when comparing a list expected value against a NumPy integer array: NumPy scalars are now unboxed before subtraction, avoiding wraparound such as ``0 - np.uint8(1)`` evaluating to ``255``. diff --git a/src/_pytest/approx.py b/src/_pytest/approx.py index 10c88d42334..2f3c1c8d996 100644 --- a/src/_pytest/approx.py +++ b/src/_pytest/approx.py @@ -431,6 +431,9 @@ def _repr_compare(self, other_side: Sequence[float]) -> list[str]: for i, (approx_value, other_value) in enumerate( zip(approx_side_as_map, other_side, strict=True) ): + # Unbox numpy scalars first: e.g. `0 - np.uint8(1)` wraps around + # to 255 instead of -1 (see #15314). + other_value = _unbox_numpy_scalar(other_value) if approx_value != other_value: try: abs_diff = abs(approx_value.expected - other_value) @@ -477,7 +480,7 @@ def __eq__(self, actual) -> bool: return super().__eq__(actual) def _yield_comparisons(self, actual): - return zip(actual, self.expected, strict=True) + return zip((_unbox_numpy_scalar(a) for a in actual), self.expected, strict=True) class ApproxScalar(Approx[ExpectedT]): diff --git a/testing/python/approx.py b/testing/python/approx.py index 35adcfea140..1f62df592cb 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -259,6 +259,16 @@ def test_error_messages_numpy_dtypes(self, assert_approx_raises_regex): ], ) + def test_numpy_integer_actual_against_list_expected(self): + # Numpy integer scalars must be unboxed before comparison, otherwise + # e.g. `0 - np.uint8(1)` wraps around to 255 (see #15314). + np = pytest.importorskip("numpy") + + assert np.array([1], dtype=np.uint8) == pytest.approx([0], abs=5) + assert np.array([0], dtype=np.uint8) == pytest.approx([1], abs=5) + assert np.array([255], dtype=np.uint8) != pytest.approx([0]) + assert np.array([-128], dtype=np.int8) != pytest.approx([127]) + def test_error_messages_invalid_args(self, assert_approx_raises_regex): np = pytest.importorskip("numpy") with pytest.raises(AssertionError) as e: From d6b5cb7c1c1310b5d962498b7d8cfd9b78607e92 Mon Sep 17 00:00:00 2001 From: Patrick Date: Mon, 2 Nov 2026 00:24:00 +0700 Subject: [PATCH 2/2] docs(authors): add rinopatrick --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 136ffd6c5ee..aa9ce45e90a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -426,6 +426,7 @@ Raquel Alegre Ravi Chandra Reagan Lee Reilly Brogan +rinopatrick Rob Arrow Robert Holt Roberto Aldera