Skip to content

Commit

Permalink
[8.1.x] Fixed Bug Regarding Attribute Error in pytest.approx For Type…
Browse files Browse the repository at this point in the history
…s Implicitly Convertible to Numpy Arrays (#12240)


---------

Co-authored-by: poulami-sau <[email protected]>
Co-authored-by: Bruno Oliveira <[email protected]>
  • Loading branch information
3 people authored Apr 25, 2024
1 parent a43b098 commit 0675383
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 5 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ Pierre Sassoulas
Pieter Mulder
Piotr Banaszkiewicz
Piotr Helm
Poulami Sau
Prakhar Gurunani
Prashant Anand
Prashant Sharma
Expand Down
1 change: 1 addition & 0 deletions changelog/12114.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed error in :func:`pytest.approx` when used with `numpy` arrays and comparing with other types.
14 changes: 9 additions & 5 deletions src/_pytest/python_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def __repr__(self) -> str:
)
return f"approx({list_scalars!r})"

def _repr_compare(self, other_side: "ndarray") -> List[str]:
def _repr_compare(self, other_side: Union["ndarray", List[Any]]) -> List[str]:
import itertools
import math

Expand All @@ -163,10 +163,14 @@ def get_value_from_nested_list(
self._approx_scalar, self.expected.tolist()
)

if np_array_shape != other_side.shape:
# Convert other_side to numpy array to ensure shape attribute is available.
other_side_as_array = _as_numpy_array(other_side)
assert other_side_as_array is not None

if np_array_shape != other_side_as_array.shape:
return [
"Impossible to compare arrays with different shapes.",
f"Shapes: {np_array_shape} and {other_side.shape}",
f"Shapes: {np_array_shape} and {other_side_as_array.shape}",
]

number_of_elements = self.expected.size
Expand All @@ -175,7 +179,7 @@ def get_value_from_nested_list(
different_ids = []
for index in itertools.product(*(range(i) for i in np_array_shape)):
approx_value = get_value_from_nested_list(approx_side_as_seq, index)
other_value = get_value_from_nested_list(other_side, index)
other_value = get_value_from_nested_list(other_side_as_array, index)
if approx_value != other_value:
abs_diff = abs(approx_value.expected - other_value)
max_abs_diff = max(max_abs_diff, abs_diff)
Expand All @@ -188,7 +192,7 @@ def get_value_from_nested_list(
message_data = [
(
str(index),
str(get_value_from_nested_list(other_side, index)),
str(get_value_from_nested_list(other_side_as_array, index)),
str(get_value_from_nested_list(approx_side_as_seq, index)),
)
for index in different_ids
Expand Down
18 changes: 18 additions & 0 deletions testing/python/approx.py
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,24 @@ def test_numpy_array_wrong_shape(self):
assert a12 != approx(a21)
assert a21 != approx(a12)

def test_numpy_array_implicit_conversion(self) -> None:
"""#12114."""
np = pytest.importorskip("numpy")

class ImplicitArray:
"""Type which is implicitly convertible to a numpy array."""

def __init__(self, vals):
self.vals = vals

def __array__(self, dtype=None, copy=None):
return np.array(self.vals)

vec1 = ImplicitArray([1.0, 2.0, 3.0])
vec2 = ImplicitArray([1.0, 2.0, 4.0])
# see issue #12114 for test case
assert vec1 != approx(vec2)

def test_numpy_array_protocol(self):
"""
array-like objects such as tensorflow's DeviceArray are handled like ndarray.
Expand Down

0 comments on commit 0675383

Please sign in to comment.