Skip to content

Commit 5cffef7

Browse files
authored
Fixed Bug Regarding Attribute Error in pytest.approx For Types Implicitly 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
1 parent a830a3e commit 5cffef7

File tree

4 files changed

+28
-5
lines changed

4 files changed

+28
-5
lines changed

AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ Pierre Sassoulas
321321
Pieter Mulder
322322
Piotr Banaszkiewicz
323323
Piotr Helm
324+
Poulami Sau
324325
Prakhar Gurunani
325326
Prashant Anand
326327
Prashant Sharma

changelog/12114.bugfix.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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.

src/_pytest/python_api.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def __repr__(self) -> str:
142142
)
143143
return f"approx({list_scalars!r})"
144144

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

@@ -163,10 +163,14 @@ def get_value_from_nested_list(
163163
self._approx_scalar, self.expected.tolist()
164164
)
165165

166-
if np_array_shape != other_side.shape:
166+
# convert other_side to numpy array to ensure shape attribute is available
167+
other_side_as_array = _as_numpy_array(other_side)
168+
assert other_side_as_array is not None
169+
170+
if np_array_shape != other_side_as_array.shape:
167171
return [
168172
"Impossible to compare arrays with different shapes.",
169-
f"Shapes: {np_array_shape} and {other_side.shape}",
173+
f"Shapes: {np_array_shape} and {other_side_as_array.shape}",
170174
]
171175

172176
number_of_elements = self.expected.size
@@ -175,7 +179,7 @@ def get_value_from_nested_list(
175179
different_ids = []
176180
for index in itertools.product(*(range(i) for i in np_array_shape)):
177181
approx_value = get_value_from_nested_list(approx_side_as_seq, index)
178-
other_value = get_value_from_nested_list(other_side, index)
182+
other_value = get_value_from_nested_list(other_side_as_array, index)
179183
if approx_value != other_value:
180184
abs_diff = abs(approx_value.expected - other_value)
181185
max_abs_diff = max(max_abs_diff, abs_diff)
@@ -188,7 +192,7 @@ def get_value_from_nested_list(
188192
message_data = [
189193
(
190194
str(index),
191-
str(get_value_from_nested_list(other_side, index)),
195+
str(get_value_from_nested_list(other_side_as_array, index)),
192196
str(get_value_from_nested_list(approx_side_as_seq, index)),
193197
)
194198
for index in different_ids

testing/python/approx.py

+17
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,23 @@ def test_numpy_array_wrong_shape(self):
763763
assert a12 != approx(a21)
764764
assert a21 != approx(a12)
765765

766+
def test_numpy_array_implicit_conversion(self):
767+
np = pytest.importorskip("numpy")
768+
769+
class ImplicitArray:
770+
"""Type which is implicitly convertible to a numpy array."""
771+
772+
def __init__(self, vals):
773+
self.vals = vals
774+
775+
def __array__(self, dtype=None, copy=None):
776+
return np.array(self.vals)
777+
778+
vec1 = ImplicitArray([1.0, 2.0, 3.0])
779+
vec2 = ImplicitArray([1.0, 2.0, 4.0])
780+
# see issue #12114 for test case
781+
assert vec1 != approx(vec2)
782+
766783
def test_numpy_array_protocol(self):
767784
"""
768785
array-like objects such as tensorflow's DeviceArray are handled like ndarray.

0 commit comments

Comments
 (0)