> I still think the most sane approach is to let pytest show
> the repr of failing objects [0].  This would allow you to
> write objects with a .__bool__() and a .__repr__() where
> pytest would show the repr if it's false.

Don't get me wrong--I fully agree that the approach you're describing. And
I'm not advocating for that AST example at all (it's just all I could
cobble together because I didn't see an appropriate place to hack on the
repr handling).

> And if you really want to have the dual behaviour, make
> it explicit...

The dual behavior was an implementation detail of the hacked experiment I
was playing with. My interest was in having something like
pytest_assertrepr_compare() but for single-values rather than just
comparisons.

> [0] I don't think it currently does this, so this would
> be a feature request AFAIK.

Here's an example of Pytest's current behavior using a falsey value with a
repr:

    import pytest

    # Test helpers.
    def myfunc(x):
        if x == 42:
            return True
        msg = 'custom report\nmulti-line output\nmyfunc({0}) failed'
        return FalsyValue(msg.format(x))

    class FalsyValue(object):
        def __init__(self, repr_string):
            self.repr_string = repr_string

        def __bool__(self):
            return False

        def __nonzero__(self):  # <- for py 2
            return False

        def __eq__(self, other):
            return other == False

        def __repr__(self):
            return self.repr_string

    # Test cases.
    def test_passing():
        assert myfunc(42)

    def test_failing():
        assert myfunc(41)


Running this gives the following failure message:

    ============================ FAILURES ============================
    __________________________ test_failing __________________________

        def test_failing():
    >       assert myfunc(41)
    E       assert custom report\nmulti-line output\nmyfunc(41) failed
    E        +  where custom report\nmulti-line output\nmyfunc(41) fai
    led = myfunc(41)

    test_falsey_object.py:31: AssertionError
    =============== 1 failed, 1 passed in 0.22 seconds ===============

So this does sort-of work although the repr is duplicated and newlines are
being escaped.

On thing that's important to mention: If there's a feature request based on
anything in this discussion thread, it should not be made for my case.
After giving it more thought, I think I'll need to raise my own errors
directly so I'm not sure I would be able to use the feature.


On Thu, Mar 22, 2018 at 5:46 PM, Floris Bruynooghe <f...@devork.be> wrote:

> On Thu, Mar 22 2018, Shawn Brown wrote:
>
> > Would a return value helper--as you are thinking about it--be able to
> > handle cases like test_3passing()?
> >
> >     def test_3passing():
> >         with pytest.raises(AssertionError) as excinfo:
> >             assert myfunc(41)
> >         assert 'custom report' in str(excinfo.value)
>
> I agree with Ronny here and do think you're trying to design a very
> weird and unnatural API for Python.  The function should either return
> an object or raise an exception, you can't have both.  Well, you can as
> you've show, but this is very brittle and your users will be thoroughly
> confused about what is going on.  So you shouldn't have both.
>
> Having followed this discussion so far I still think the most sane
> approach is to let pytest show the repr of failing objects [0].  This would
> allow you to write objects with a .__bool__() and a .__repr__() where
> pytest would show the repr if it's false.  And if you really want to
> have the dual behaviour, make it explicit to the user with a signature
> like myfunc(value, raising=False) so they can invoke the raising
> behaviour when desired.
>
>
> [0] I don't think it currently does this, so this would be a feature
>     request AFAIK.
>
_______________________________________________
pytest-dev mailing list
pytest-dev@python.org
https://mail.python.org/mailman/listinfo/pytest-dev

Reply via email to