Skip to content

Commit

Permalink
Fixed Bug Regarding Attribute Error in pytest.approx For Types Implic…
Browse files Browse the repository at this point in the history
…itly Convertible to Numpy Arrays (#12232)

* added test case in testing/python/approx.py based on test case provided by reporter in issue #12114
* test cases pass for pytest testing/python/approx.py
* expanded the type annotation to include objects which may cast to a array and renamed other_side to other_side_as_array and asserted that it is not none
  • Loading branch information
poulami-sau authored Apr 23, 2024
1 parent a830a3e commit 5cffef7
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 5 deletions.
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

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

0 comments on commit 5cffef7

Please sign in to comment.