Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed Bug Regarding Attribute Error in pytest.approx For Types Implicitly Convertible to Numpy Arrays #12232

Merged
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,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 attribute error in pytest.approx for types implicitly convertible to numpy arrays by converting other_side to a numpy array so that np_array_shape != other_side.shape can be properly checked.
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
RonnyPfannschmidt marked this conversation as resolved.
Show resolved Hide resolved

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
17 changes: 17 additions & 0 deletions testing/python/approx.py
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,23 @@ def test_numpy_array_wrong_shape(self):
assert a12 != approx(a21)
assert a21 != approx(a12)

def test_numpy_array_implicit_conversion(self):
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
Loading