From b613c79422b87690c5c969cbb3efeb34ed8fcf91 Mon Sep 17 00:00:00 2001 From: Ashutosh Kumar Singh Date: Tue, 19 May 2026 22:24:06 +0530 Subject: [PATCH 1/3] fix: approx() tolerance not applied to sequences/mappings inside dicts When approx() is called on a dict whose values are themselves lists or dicts, _approx_scalar() was returning ApproxScalar for those values, which does exact equality. The top-level approx() dispatcher correctly routes sequences to ApproxSequenceLike and mappings to ApproxMapping, but _approx_scalar() -- called per-element by ApproxMapping.__eq__ -- missed those cases. Adding the same isinstance checks in _approx_scalar means nested structures get recursive approximate comparison instead of falling back to ==. Fixes #8703 Signed-off-by: Ashutosh Kumar Singh --- src/_pytest/python_api.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index f22a0f5016d..f35cda4a43a 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -98,6 +98,10 @@ def _approx_scalar(self, x) -> ApproxBase: return ApproxDecimal(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok) if isinstance(x, (datetime, timedelta)): return ApproxTimedelta(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok) + if isinstance(x, Mapping): + return ApproxMapping(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok) + if _is_sequence_like(x): + return ApproxSequenceLike(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok) return ApproxScalar(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok) def _yield_comparisons(self, actual): From 666e91c6f9783edb8bba9f5ac522707a8030edaf Mon Sep 17 00:00:00 2001 From: Ashutosh Kumar Singh Date: Sat, 23 May 2026 23:46:12 +0530 Subject: [PATCH 2/3] Add tests and changelog for approx nested sequence fix Signed-off-by: Ashutosh Kumar Singh --- changelog/8703.bugfix.rst | 1 + testing/python/approx.py | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 changelog/8703.bugfix.rst diff --git a/changelog/8703.bugfix.rst b/changelog/8703.bugfix.rst new file mode 100644 index 00000000000..dd48e95aa83 --- /dev/null +++ b/changelog/8703.bugfix.rst @@ -0,0 +1 @@ +Fixed ``pytest.approx()`` silently falling back to exact equality when a dict value is itself a list or dict. Nested sequences and mappings are now compared with the same tolerance as top-level ones. diff --git a/testing/python/approx.py b/testing/python/approx.py index 88d46cbb755..31b868bcda4 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -726,6 +726,10 @@ def test_dict_nonnumeric(self): assert {"a": 1.0, "b": 1} != pytest.approx({"a": 1.0, "b": None}) assert {"a": 1.0, "b": True} != pytest.approx({"a": 1.0, "b": False}, abs=2) + def test_dict_with_list_values(self): + assert {"a": [1.0, 2.0 + 1e-7]} == approx({"a": [1.0, 2.0]}, abs=1e-5) + assert {"a": [1.0, 2.0 + 1e-4]} != approx({"a": [1.0, 2.0]}, abs=1e-5) + def test_dict_vs_other(self): assert 1 != approx({"a": 0}) From 4c44eea837627fde1cf8b4a9eee429a146f804fa Mon Sep 17 00:00:00 2001 From: Ashutosh Kumar Singh Date: Sun, 24 May 2026 00:10:05 +0530 Subject: [PATCH 3/3] Add test for list containing dict elements Covers the Mapping branch in _approx_scalar() to reach 100% diff coverage. Signed-off-by: Ashutosh Kumar Singh --- testing/python/approx.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/testing/python/approx.py b/testing/python/approx.py index 31b868bcda4..2108fb9522f 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -679,6 +679,10 @@ def test_list_wrong_len(self): assert [1, 2] != approx([1]) assert [1, 2] != approx([1, 2, 3]) + def test_list_with_dict_elements(self): + assert [{"a": 1.0 + 1e-7}] == approx([{"a": 1.0}], abs=1e-5) + assert [{"a": 1.0 + 1e-4}] != approx([{"a": 1.0}], abs=1e-5) + def test_tuple(self): actual = (1 + 1e-7, 2 + 1e-8) expected = (1, 2)