Inconsistent output on assertion errors with references

Create issue
Issue #9 wontfix
Dusty Phillips created an issue

I really appreciate how py.test converts local variables in assertions to their values, allowing me to quickly determine what the incorrect itemss are.

However, when asserting the values of classes or named tuples, the references are not followed and the replaced value is typically just the identifier of the class. The attached file contains some tests that describe this.

This is an example of the desired output when a test fails:

{{{ #!python

    n = Obj()
    y = n.y
    assert n.x == 5
  assert y == 5

E assert 7 == 5 }}}

the last line shows the local variable replaced with a 7.

Here is a faulty substitution: {{{ #!python

    n = Obj()
    assert n.x == 5
  assert n.y == 5

E assert <test_example.Obj object at 0x98a05ec>.y == 5 }}}

When I get this error I have to add an extra 'print n.y' line and run the test again. This isn't a big deal since the print statements are suppressed when tests pass, but a correct substitution would be slightly more useful.

Dusty

Comments (2)

  1. Holger Krekel repo owner

    Hey Dusty,

    discussed a bit with Armin (rigo). You are using class-attributes in your example, i think. With normal instance attributes assert reinterpretation will actually work as you wish. The reason why py.test does not show class-attributes is that this would also show things like "someclass.method(...)" or even show tons of intermediate values on "mod1.mod2.cls1.x". So the current code tries to strike a balance by only showing intermediates on instance variables. If you like you can play a bit with the code here:

    http://bitbucket.org/hpk42/py-trunk/src/tip/py/magic/exprinfo.py#cl-307

    by modifying the checks to suite your case. If you make a few cases and think you have found a good balance, i'd be happy to review and see to commit it to mainline. If you need help to get going, feel free to join #pylib on irc.freenode.net.

    For now, i close the issue - feel free to reopen it.

    sidenote: the indirect way of evaluating through the frame object is there to help pypy to plug its own pypy-over-cpython interpreter in. hope it's not too confusing.

    cheers holger

  2. Dusty Phillips reporter

    Hi Holger,

    I had a look at that code and managed to hack it so that it would work with named tuples, which is the specific problem I was having. By 'hack', I mean it was too ugly to publish with my name on it. ;-)

    Having looked at it, I understand now why a more general solution is difficult. I was hoping that even if you had a chain like this:

    assert myObject.myMethod().someattrib = 5

    it would be able to follow the whole chain and replace it with

    assert 7 = 5

    in the output. Looking through it, I don't see that the data necessary to do this is actually available at that point in the code, even with introspection?

    At any rate, there are two workarounds -- place print statements wherever failures occur and leave them there, or set the attribute to a local namespace variable before asserting, so this bit of magic isn't that important; I'm fine with leaving the issue closed.

  3. Log in to comment