From f97598000d6b8e21ce0e9f2e8e8ddf7cb13949c4 Mon Sep 17 00:00:00 2001 From: duongynhi000005-oss Date: Thu, 5 Nov 2026 10:27:48 +0700 Subject: [PATCH] Fix approx() raising InvalidOperation on Decimal('NaN') actual value When the actual value is Decimal('NaN') and expected is finite, ApproxScalar.__eq__ previously evaluated `abs(self.expected - actual) <= self.tolerance`, which raises decimal.InvalidOperation under the default Decimal context. Return False early when `_is_nan(abs(actual))` and expected is finite, matching float('nan') behavior. Fixes #15335. Signed-off-by: duongynhi000005-oss --- changelog/15335.bugfix.rst | 1 + src/_pytest/approx.py | 3 +++ testing/python/approx.py | 10 ++++++++++ 3 files changed, 14 insertions(+) create mode 100644 changelog/15335.bugfix.rst diff --git a/changelog/15335.bugfix.rst b/changelog/15335.bugfix.rst new file mode 100644 index 00000000000..7b0ca83da2e --- /dev/null +++ b/changelog/15335.bugfix.rst @@ -0,0 +1 @@ +Fixed :func:`pytest.approx` raising :class:`decimal.InvalidOperation` when the actual value is a :class:`decimal.Decimal` NaN and the expected value is finite. diff --git a/src/_pytest/approx.py b/src/_pytest/approx.py index 10c88d42334..d053228cc49 100644 --- a/src/_pytest/approx.py +++ b/src/_pytest/approx.py @@ -589,6 +589,9 @@ def __eq__(self, actual) -> bool: if _is_nan(abs(self.expected)): return self.nan_ok and _is_nan(abs(actual)) + if _is_nan(abs(actual)): + return False + # Infinity shouldn't be approximately equal to anything but itself, but # if there's a relative tolerance, it will be infinite and infinity # will seem approximately equal to everything. The equal-to-itself diff --git a/testing/python/approx.py b/testing/python/approx.py index 35adcfea140..50a34879ea6 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -634,6 +634,16 @@ def test_decimal_outside_float_range(self): assert expected != approx(Decimal("Infinity")) assert Decimal("Infinity") != approx(expected) + def test_decimal_actual_nan(self): + """Comparing a Decimal('NaN') actual value against approx() compares unequal (#15335).""" + assert Decimal("NaN") != approx(Decimal("1.1")) + assert Decimal("NaN") != approx(Decimal("1.1"), nan_ok=True) + assert Decimal("NaN") != approx(1.1) + assert [Decimal("NaN")] != approx([Decimal("1.1")]) + assert {"val": Decimal("NaN")} != approx({"val": Decimal("1.1")}) + assert Decimal("NaN") == approx(Decimal("NaN"), nan_ok=True) + assert Decimal("NaN") != approx(Decimal("NaN"), nan_ok=False) + def test_fraction(self): within_1e6 = [ (1 + Fraction(1, 1000000), Fraction(1)),