[pytest-dev] fixtures as context managers

holger krekel holger at merlinux.eu
Mon May 27 09:36:04 CEST 2013


On Sun, May 26, 2013 at 15:37 +0000, Jason R. Coombs wrote:
> No. I haven't seen that syntax much if at all. I suggest it because it appeals to my aesthetic.

that's fine :)

> I'm not sure the distinction between class and decorator is so clear. After all, a decorator is just a callable with a particular signature. I haven't considered the implementation, but I imagine pytest.fixture could in fact be a class if you wanted it to be.

it could but decorators are typically functions.

> Even if fixture is only a factory function, it's conceivable that 	the alternate function could be appended as an attribute:
> 
> def fixture(...):
>   # primary behavior
> 
> def _yielding_fixture(*args, **kwargs):
>   # wrap or alter behavior of fixture(*args, **kwargs)
> 
> fixture.yielding = _yielding_fixture

sure, that's possible.

> I agree the implementation is a little clumsy, but I would be inclined to accept a little bit of implementation cruft for a nicer exposed API.

I claim it would be a bit of an unexpected API.  Maybe someone else than us
two can comment on it :)

> That said, I'm not trying to convince, but only to share. If you don't love the idea, I won't be offended if you don't incorporate it.

thanks for the relaxedness.

> Sent from Earth

:)
holger


> 
> On May 26, 2013, at 7:52, "holger krekel" <holger at merlinux.eu> wrote:
> 
> > Hi Jason,
> > 
> > (your post contains a bit many blank lines, btw, deleting them :)
> > 
> > On Sat, May 25, 2013 at 14:40 +0000, Jason R. Coombs wrote:
> >> May I suggest an alternate idea:
> >> 
> >> @pytest.fixture.context(.)
> >> 
> >> def func():
> >>  .
> >> 
> >> Or 
> >> 
> >> @pytest.fixture.yielding(.)
> >> 
> >> def func():
> >>  .
> >> 
> >> Depending on which mode is not supported by fixture directly.
> >> 
> >> Then, the decorator can take the same signature as the natural usage
> >> (@pytest.fixture), but alter the handling of a generator appropriately. It
> >> can be thought of as an alternate constructor for the same fixture factory.
> >> It provides a nice, namespaced name and doesn't threaten to pollute the
> >> pytest namespace with a proliferation of fixture variants.
> > 
> > Not cramming the pytest.* is a goal i share but i am not sure about
> > this suggestion.  The "idiomatic" way to introduce a decorator variant 
> > in Python is adding a keyword argument or a new name.  
> > 
> > Your suggested syntax reminds of what one does for classes, i.e. providing
> > classmethods for alternate constructors.  But i haven't seen it anywhere
> > for decorators, did you?
> > 
> > best,
> > holger
> > 
> >> 
> >> From: Pytest-dev [mailto:pytest-dev-bounces+jaraco=jaraco.com at python.org] On
> >> Behalf Of Bruno Oliveira
> >> Sent: Friday, 24 May, 2013 17:13
> >> To: holger krekel; Harro van der Klauw; Andreas Pelme; pytest-dev at python.org
> >> Subject: Re: [pytest-dev] fixtures as context managers
> >> 
> >> 
> >> 
> >> In light of the examples, IMHO, I agree that fixtures being explicit about
> >> using yield as context-managers is preferable.
> >> 
> >> 
> >> 
> >> I like @pytest.contextfixture, it is easy to look-up and understand since it
> >> mimics what we already have in contextlib.
> >> 
> >> 
> >> 
> >> 
> >> 
> >> 
> >> 
> >> On Fri, May 24, 2013 at 12:07 PM, holger krekel <holger at merlinux.eu
> >> <mailto:holger at merlinux.eu> > wrote:
> >> 
> >> On Fri, May 24, 2013 at 16:50 +0200, Harro van der Klauw wrote:
> >>> As long as it throws an error hinting that you might need yielding=True it
> >>> should be obvious on how to fix
> >>> the backwards incompatibility issue as soon as you run your tests.
> >> 
> >> We cannot easily throw an error with a hint.  Consider this example:
> >> 
> >>    import pytest
> >> 
> >> 
> >>    @pytest.fixture
> >>    def fix():
> >>        yield 1
> >>        yield 2
> >> 
> >>    def test_fix(fix):
> >>        for x in fix:
> >>            assert x < 3
> >> 
> >> This runs fine on pytest-2.3.5.  On trunk it gives this error:
> >> 
> >>    ...
> >> 
> >>    fix = 1
> >> 
> >>        def test_fix(fix):
> >>>      for x in fix:
> >>                assert x < 3
> >>    E           TypeError: 'int' object is not iterable
> >> 
> >> I've never written or seen somebody writing such a generator fixture,
> >> though.
> >> And what you would need to do is rewrite the fixture:
> >> 
> >>    @pytest.fixture
> >>    def fix():
> >>        def gen():
> >>            yield 1
> >>            yield 2
> >>        return gen()
> >> 
> >> Then again, when i first saw the contextlib.contextmanager decorator
> >> i found it not very intuitive.  Did anyone?  It looks like a hack.
> >>> From that angle i'd rather go for requiring "contextyield=True" or
> >> @pytest.contextfixture because that can be looked up in documentation
> >> and thus is easier to read for people not familiar with yields/contextlib.
> >> 
> >> best,
> >> holger
> >> 
> >> 
> >>> I don't see a big problem with this, updating of a requirement is
> >> something
> >>> that you should never do automatically.
> >>> 
> >>> Cheers,
> >>> Harro
> >>> 
> >>> 
> >>> 
> >>> On 24 May 2013 16:36, Andreas Pelme <andreas at pelme.se
> >> <mailto:andreas at pelme.se> > wrote:
> >>> 
> >>>> On Thursday 9 May 2013 at 15:56, holger krekel wrote:
> >>>>> This is probably used by very few people but to be on the safe side,
> >>>>> we probably should introduce a flag like this:
> >>>>> 
> >>>>> @pytest.fixture(ctx=True) # signal this is a context manager style
> >>>> fixture
> >>>>> def fix():
> >>>>> yield 1
> >>>>> 
> >>>>> What do you think? Any other suggestions for the flag name?
> >>>>> 
> >>>>> I'd rather not introduce something like @pytest.contextfixture
> >>>>> because it would be a duplication of the API (scope, params).
> >>>>> But i am open to be convinced otherwise.
> >>>> 
> >>>> I agree that another API like contextfixture should be avoided.
> >>>> 
> >>>> We had a short discussion on IRC about this, Holger had the idea of
> >> doing
> >>>> the opposite if ctx=True - a new default argument "yielding=False" which
> >>>> would make it possible to restore the old behavior by putting
> >> yielding=True
> >>>> on fixtures that would be affected by this.
> >>>> 
> >>>> A fixture that is a generator that currently looks like this
> >>>> 
> >>>> @pytest.fixture
> >>>> def fix():
> >>>>    yield 1
> >>>>    yield 2
> >>>> 
> >>>> Would then have to be changed to
> >>>> 
> >>>> @pytest.fixture(yielding=True)
> >>>> def fix():
> >>>>    yield 1
> >>>>    yield 2
> >>>> 
> >>>> This is backward incompatible, but given that it seems questionable if
> >>>> "generator fixtures" useful/is used, with a note in the release notes
> >> and
> >>>> documentation I think this could be a good compromise.
> >>>> 
> >>>> I will be happy to give this a try if it is decided this could be a good
> >>>> approach!
> >>>> 
> >>>> Cheers
> >>>> Andreas
> >>>> 
> >>>> 
> >>>> _______________________________________________
> >>>> Pytest-dev mailing list
> >>>> Pytest-dev at python.org <mailto:Pytest-dev at python.org> 
> >>>> http://mail.python.org/mailman/listinfo/pytest-dev
> >> 
> >>> _______________________________________________
> >>> Pytest-dev mailing list
> >>> Pytest-dev at python.org <mailto:Pytest-dev at python.org> 
> >>> http://mail.python.org/mailman/listinfo/pytest-dev
> >> 
> >> _______________________________________________
> >> Pytest-dev mailing list
> >> Pytest-dev at python.org <mailto:Pytest-dev at python.org> 
> >> http://mail.python.org/mailman/listinfo/pytest-dev
> > 
> > 
> > 




More information about the Pytest-dev mailing list