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

Failure when trying to call __repr__ because __iter__ is called? #7966

Closed
Holt59 opened this issue Oct 29, 2020 · 11 comments · Fixed by #11308
Closed

Failure when trying to call __repr__ because __iter__ is called? #7966

Holt59 opened this issue Oct 29, 2020 · 11 comments · Fixed by #11308
Labels
topic: rewrite related to the assertion rewrite mechanism type: bug problem that needs to be addressed

Comments

@Holt59
Copy link

Holt59 commented Oct 29, 2020

1. Problem

The following code

class A:
    def __iter__(self):
        raise ValueError()

    def __eq__(self, o: object) -> bool:
        return self is o


def test_a():
    assert A() == A()

...will throw:

    def test_a():
>       assert A() == A()
E       assert <tests.test_p...0019C132F1490> == <tests.test_p...0019C132F12B0>
E         (pytest_assertion plugin: representation of details failed: XXX\test_pp.py:3: ValueError.
E          Probably an object has a faulty __repr__.)

...because __iter__ is called in order to repr() the object?

Is there a way to avoid __iter__ being called when a test fails?

2. Setup

Python 3.8.2 | pytest 6.1.1

@RonnyPfannschmidt RonnyPfannschmidt added topic: rewrite related to the assertion rewrite mechanism type: bug problem that needs to be addressed labels Oct 29, 2020
@RonnyPfannschmidt
Copy link
Member

part of the assertion representation logic does try various things to tell more about the object

the isiterable helper unfortunately calls __iter__ instead of just verifying its existence

it only checks for type-error, which doesn't account for broken implementations of iter

@RonnyPfannschmidt
Copy link
Member

we should decide whether we want to uphold the invariant that iter calls should only raise type errors, or whether we need to do something better

in any case the error needs to be better

@Holt59
Copy link
Author

Holt59 commented Oct 29, 2020

Thanks, I actually did not think that there was some conventions about the type of exceptions being raised by special methods...

I'm working on a custom interpreter for a different language, so I simply had a different MyTypeError. Making a kind of hybrid error inheriting both type errors actually fix the issue.

@RonnyPfannschmidt
Copy link
Member

CC @nicoddemus after reviewing i beleive we shoudl add 2 additional failure modes

a) types that wrap data but dont proviede __iter__ should diff a mu
ltiline repr
b) types that fail __iter__ should be considered differently


In [1]: 
   ...: 
   ...: class A:
   ...:     def __iter__(self):
   ...:         raise ValueError()
   ...: 
   ...:     def __eq__(self, o: object) -> bool:
   ...:         return self is o
   ...: 

In [2]: 

In [2]: a = A()

In [9]: pprint.pformat(a)
Out[9]: '<__main__.A object at 0x7fab6fda9d30>'

In [10]: from _pytest.assertion.util import _pformat_dispatch

In [11]: _pformat_dispatch(a)
Out[11]: '<__main__.A object at 0x7fab6fda9d30>'

In [12]: 

i believe a better error should be triggered

@nicoddemus
Copy link
Member

I agree with b), however I don't understand what you mean with a)... why use a multiline repr for types that don't have a __iter__ method?

@RonnyPfannschmidt
Copy link
Member

@nicoddemus i have some helper pbjects around that just wrap around other sutff

also openapi types dont necessarily iterate but have a nested multiline repr

@nicoddemus
Copy link
Member

Hmm wait, I might be missing something, what do you mean by "multiline repr"? 🤔

@RonnyPfannschmidt
Copy link
Member

@nicoddemus any type where repr is either multiline on its own or by using something some pprint.pformat

@nicoddemus
Copy link
Member

You mean calling __repr__().split() and see if there are more than one line, and treat that accordingly? I see.

@RonnyPfannschmidt
Copy link
Member

Just use diff on it

@AIGeneratedUsername
Copy link

AIGeneratedUsername commented Dec 14, 2021

Hi, guys.

When a test fails, Pytest fails to print repr() for objects in a list. Example output:

============================= test session starts ==============================
collecting ... collected 1 item
test_sample_users FAILED [100%]

sample_users = [<[KeyError('is_adult') raised in repr()] SampleUser object at 0x7fa9fad23a00>, <[KeyError('is_adult') raised in repr()] SampleUser object at 0x7fa9fad23be0>]

# the test traceback is not included, it does not matter

My SampleUser is a 'pydantic' class, and SampleUser.is_adult is a class property. I do not know why Pytest fails to call repr(), because I can repr() manually in my code and all works fine.

Versions: Python 3.10.1; pytest latest; pydantic master.

QUESTION: Is it related to this issue, or I need to create a new one with more details?

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

Successfully merging a pull request may close this issue.

4 participants