Skip to content

Commit d65d991

Browse files
approx: do not raise error in mixed sequence/collections
Previously `approx` would show an internal error when passed a collection with numbers and non-numbers mixed: ``` E (pytest_assertion plugin: representation of details failed: /tmp/test_a/.venv/lib/python3.12/site-packages/_pytest/python_api.py:343: TypeError: unsupported operand type(s) for -: 'str' and 'str'. E Probably an object has a faulty __repr__.) ``` The problem was that it was unconditionally computing the diff for every element. This change introduces a try/except catch around the code related to computing the diff, and ignores it instead of letting the error be raised. Fixes #13010 Co-authored-by: Bruno Oliveira <[email protected]>
1 parent a16e8ea commit d65d991

File tree

3 files changed

+42
-6
lines changed

3 files changed

+42
-6
lines changed

changelog/13010.improvement.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:func:`pytest.approx` now can compare collections that contain numbers and non-numbers mixed.

src/_pytest/python_api.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -340,14 +340,18 @@ def _repr_compare(self, other_side: Sequence[float]) -> list[str]:
340340
zip(approx_side_as_map, other_side)
341341
):
342342
if approx_value != other_value:
343-
abs_diff = abs(approx_value.expected - other_value)
344-
max_abs_diff = max(max_abs_diff, abs_diff)
345-
if other_value == 0.0:
346-
max_rel_diff = math.inf
343+
try:
344+
abs_diff = abs(approx_value.expected - other_value)
345+
max_abs_diff = max(max_abs_diff, abs_diff)
346+
# Ignore non-numbers for the diff calculations (#13012).
347+
except TypeError:
348+
pass
347349
else:
348-
max_rel_diff = max(max_rel_diff, abs_diff / abs(other_value))
350+
if other_value == 0.0:
351+
max_rel_diff = math.inf
352+
else:
353+
max_rel_diff = max(max_rel_diff, abs_diff / abs(other_value))
349354
different_ids.append(i)
350-
351355
message_data = [
352356
(str(i), str(other_side[i]), str(approx_side_as_map[i]))
353357
for i in different_ids

testing/python/approx.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,37 @@ def test_bool(self):
390390

391391
assert err.match(r"approx\(\) is not supported in a boolean context")
392392

393+
def test_mixed_sequence(self, assert_approx_raises_regex) -> None:
394+
"""Approx should work on sequences that also contain non-numbers (#13010)."""
395+
assert_approx_raises_regex(
396+
[1.1, 2, "word"],
397+
[1.0, 2, "different"],
398+
[
399+
"",
400+
r" comparison failed. Mismatched elements: 2 / 3:",
401+
rf" Max absolute difference: {SOME_FLOAT}",
402+
rf" Max relative difference: {SOME_FLOAT}",
403+
r" Index \| Obtained\s+\| Expected\s+",
404+
r"\s*0\s*\|\s*1\.1\s*\|\s*1\.0\s*±\s*1\.0e\-06\s*",
405+
r"\s*2\s*\|\s*word\s*\|\s*different\s*",
406+
],
407+
verbosity_level=2,
408+
)
409+
assert_approx_raises_regex(
410+
[1.1, 2, "word"],
411+
[1.0, 2, "word"],
412+
[
413+
"",
414+
r" comparison failed. Mismatched elements: 1 / 3:",
415+
rf" Max absolute difference: {SOME_FLOAT}",
416+
rf" Max relative difference: {SOME_FLOAT}",
417+
r" Index \| Obtained\s+\| Expected\s+",
418+
r"\s*0\s*\|\s*1\.1\s*\|\s*1\.0\s*±\s*1\.0e\-06\s*",
419+
],
420+
verbosity_level=2,
421+
)
422+
assert [1.1, 2, "word"] == pytest.approx([1.1, 2, "word"])
423+
393424
def test_operator_overloading(self):
394425
assert 1 == approx(1, rel=1e-6, abs=1e-12)
395426
assert not (1 != approx(1, rel=1e-6, abs=1e-12))

0 commit comments

Comments
 (0)