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

Attribute error in pytest.approx for types implicitly convertible to numpy arrays #12114

Closed
4 tasks done
ascended121 opened this issue Mar 12, 2024 · 4 comments
Closed
4 tasks done
Labels
topic: approx related to pytest.approx function type: bug problem that needs to be addressed

Comments

@ascended121
Copy link

  • a detailed description of the bug or problem you are having

When using pytest.approx with custom types which are implicitly convertible to numpy arrays, the following error is observed:

E       AssertionError: assert <test_matrix_...x7f9169ac4ca0> == approx([1.0 ±....0 ± 4.0e-06])
E         (pytest_assertion plugin: representation of details failed: ~/.venv/lib/python3.10/site-packages/_pytest/python_api.py:175: AttributeError: 'MyType' object has no attribute 'shape'.
E          Probably an object has a faulty __repr__.)

Note: The line number in the error message is a little off from the current head. That line is here.

Looking at the code, pytest.approx sensibly chooses the numpy implementation ApproxNumpy, because the expected value is implicitly convertible to a numpy array. However, the shape check assumes that other_side is a numpy array (rather than assuming its implicitly convertible to a numpy array, like the expected value is) and so assumes that it has a shape attribute (which is not part of how approx determines if something is a numpy array).

It seems like either:

  1. ApproxNumpy._repr_compare() needs to convert other_side to a numpy array (ie _as_numpy_array(other_side))
  2. _as_numpy_array needs to check if the object has a shape attribute, if the code is going to assume that's the case
  • output of pip list from the virtual environment you are using:
    Relevant packages:
    numpy: 1.26.1

  • pytest and operating system versions

pytest: 7.4.3
Ubuntu: 22.04

  • minimal example if possible
import numpy as np
import pytest

class MyType:
    """Type which is implicitly convertible to a numpy array."""
    def __init__(self, vals):
        self.vals = vals

    def __repr__(self):
        return f"{self.__class__.__name__}({self.vals})"

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

def test_approx_eq():
    vec1 = MyType([1.0, 2.0, 3.0])
    vec2 = MyType([1.0, 2.0, 4.0])
    assert vec1 == pytest.approx(vec2)
@Zac-HD Zac-HD added type: bug problem that needs to be addressed topic: approx related to pytest.approx function labels Mar 23, 2024
@poulami-sau
Copy link
Contributor

I can try working on this!

@poulami-sau
Copy link
Contributor

poulami-sau commented Apr 20, 2024

I'm not able to reproduce this error in the test cases for testing/python/approx.py, which is what I need to do to verify it in tox. Do you have any pointers?

This is one way I wrote the test case:

class ImplicitArray:
    """Type which is implicitly convertible to a numpy array."""
    def __init__(self, vals):
        self.vals = vals

    def __repr__(self):
        return f"{self.__class__.__name__}({self.vals})"

    def __array__(self, dtype=None, copy=None):
        print("called __array__ in ImplicitArray")
        np = pytest.importorskip("numpy")
        return np.array(self.vals)

class TestApprox:
    def test_numpy_array_implicit_conversion(self):
        vec1 = ImplicitArray([1.0, 2.0, 3.0])
        vec2 = ImplicitArray([1.0, 2.0, 4.0])
        assert vec1 == approx(vec2)

Here's another way that I wrote it:

class TestApprox:
    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 __repr__(self):
                return f"{self.__class__.__name__}({self.vals})"

            def __array__(self, dtype=None, copy=None):
                print("called __array__ in ImplicitArray")
                return np.array(self.vals)

        vec1 = ImplicitArray([1.0, 2.0, 3.0])
        vec2 = ImplicitArray([1.0, 2.0, 4.0])
        assert vec1 == approx(vec2)

But for both cases, they are passing when I know it shouldn't, and I haven't made any edits to ApproxNumpy yet.

@poulami-sau
Copy link
Contributor

Okay I think tox doesn't work for this as I cannot verify if numpy was actually installed or not with tox. However, it works with the alternate method for testing and I've fixed the bug. I can submit a pull request soon!

poulami-sau added a commit to poulami-sau/pytest that referenced this issue Apr 22, 2024
poulami-sau added a commit to poulami-sau/pytest that referenced this issue Apr 22, 2024
RonnyPfannschmidt pushed a commit that referenced this issue Apr 23, 2024
…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
@nicoddemus
Copy link
Member

Fixed in #12232

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic: approx related to pytest.approx function type: bug problem that needs to be addressed
Projects
None yet
Development

No branches or pull requests

4 participants