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
This commit is contained in:
poulami-sau 2024-04-23 04:45:33 -04:00 committed by GitHub
parent a830a3e98d
commit 5cffef7f07
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 28 additions and 5 deletions

View File

@ -321,6 +321,7 @@ Pierre Sassoulas
Pieter Mulder Pieter Mulder
Piotr Banaszkiewicz Piotr Banaszkiewicz
Piotr Helm Piotr Helm
Poulami Sau
Prakhar Gurunani Prakhar Gurunani
Prashant Anand Prashant Anand
Prashant Sharma Prashant Sharma

View File

@ -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.

View File

@ -142,7 +142,7 @@ class ApproxNumpy(ApproxBase):
) )
return f"approx({list_scalars!r})" 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 itertools
import math import math
@ -163,10 +163,14 @@ class ApproxNumpy(ApproxBase):
self._approx_scalar, self.expected.tolist() 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 [ return [
"Impossible to compare arrays with different shapes.", "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 number_of_elements = self.expected.size
@ -175,7 +179,7 @@ class ApproxNumpy(ApproxBase):
different_ids = [] different_ids = []
for index in itertools.product(*(range(i) for i in np_array_shape)): 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) 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: if approx_value != other_value:
abs_diff = abs(approx_value.expected - other_value) abs_diff = abs(approx_value.expected - other_value)
max_abs_diff = max(max_abs_diff, abs_diff) max_abs_diff = max(max_abs_diff, abs_diff)
@ -188,7 +192,7 @@ class ApproxNumpy(ApproxBase):
message_data = [ message_data = [
( (
str(index), 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)), str(get_value_from_nested_list(approx_side_as_seq, index)),
) )
for index in different_ids for index in different_ids

View File

@ -763,6 +763,23 @@ class TestApprox:
assert a12 != approx(a21) assert a12 != approx(a21)
assert a21 != approx(a12) 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): def test_numpy_array_protocol(self):
""" """
array-like objects such as tensorflow's DeviceArray are handled like ndarray. array-like objects such as tensorflow's DeviceArray are handled like ndarray.