Skip to content

Commit 08a39bf

Browse files
Merge pull request #11818 from RonnyPfannschmidt/ronny/issue-11797-approx-sequence-like
fix #11797: be more lenient on SequenceLike approx
2 parents c46a3a9 + 76f3f3d commit 08a39bf

File tree

3 files changed

+52
-5
lines changed

3 files changed

+52
-5
lines changed

changelog/11797.bugfix.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:func:`pytest.approx` now correctly handles :class:`Sequence <collections.abc.Sequence>`-like objects.

src/_pytest/python_api.py

+11-5
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ def _recursive_sequence_map(f, x):
129129
if isinstance(x, (list, tuple)):
130130
seq_type = type(x)
131131
return seq_type(_recursive_sequence_map(f, xi) for xi in x)
132+
elif _is_sequence_like(x):
133+
return [_recursive_sequence_map(f, xi) for xi in x]
132134
else:
133135
return f(x)
134136

@@ -721,11 +723,7 @@ def approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase:
721723
elif _is_numpy_array(expected):
722724
expected = _as_numpy_array(expected)
723725
cls = ApproxNumpy
724-
elif (
725-
hasattr(expected, "__getitem__")
726-
and isinstance(expected, Sized)
727-
and not isinstance(expected, (str, bytes))
728-
):
726+
elif _is_sequence_like(expected):
729727
cls = ApproxSequenceLike
730728
elif isinstance(expected, Collection) and not isinstance(expected, (str, bytes)):
731729
msg = f"pytest.approx() only supports ordered sequences, but got: {expected!r}"
@@ -736,6 +734,14 @@ def approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase:
736734
return cls(expected, rel, abs, nan_ok)
737735

738736

737+
def _is_sequence_like(expected: object) -> bool:
738+
return (
739+
hasattr(expected, "__getitem__")
740+
and isinstance(expected, Sized)
741+
and not isinstance(expected, (str, bytes))
742+
)
743+
744+
739745
def _is_numpy_array(obj: object) -> bool:
740746
"""
741747
Return true if the given object is implicitly convertible to ndarray,

testing/python/approx.py

+40
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,43 @@ def test_allow_ordered_sequences_only(self) -> None:
954954
with pytest.raises(TypeError, match="only supports ordered sequences"):
955955
assert {1, 2, 3} == approx({1, 2, 3})
956956

957+
def test_strange_sequence(self):
958+
"""https://github.com/pytest-dev/pytest/issues/11797"""
959+
a = MyVec3(1, 2, 3)
960+
b = MyVec3(0, 1, 2)
961+
962+
# this would trigger the error inside the test
963+
pytest.approx(a, abs=0.5)._repr_compare(b)
964+
965+
assert b == pytest.approx(a, abs=2)
966+
assert b != pytest.approx(a, abs=0.5)
967+
968+
969+
class MyVec3: # incomplete
970+
"""sequence like"""
971+
972+
_x: int
973+
_y: int
974+
_z: int
975+
976+
def __init__(self, x: int, y: int, z: int):
977+
self._x, self._y, self._z = x, y, z
978+
979+
def __repr__(self) -> str:
980+
return f"<MyVec3 {self._x} {self._y} {self._z}>"
981+
982+
def __len__(self) -> int:
983+
return 3
984+
985+
def __getitem__(self, key: int) -> int:
986+
if key == 0:
987+
return self._x
988+
if key == 1:
989+
return self._y
990+
if key == 2:
991+
return self._z
992+
raise IndexError(key)
993+
957994

958995
class TestRecursiveSequenceMap:
959996
def test_map_over_scalar(self):
@@ -981,3 +1018,6 @@ def test_map_over_mixed_sequence(self):
9811018
(5, 8),
9821019
[(7)],
9831020
]
1021+
1022+
def test_map_over_sequence_like(self):
1023+
assert _recursive_sequence_map(int, MyVec3(1, 2, 3)) == [1, 2, 3]

0 commit comments

Comments
 (0)