[py-dev] direct funcarg scoping/parametrization implementation (and resource-v3 draft)

holger krekel holger at merlinux.eu
Sat Jul 21 01:25:52 CEST 2012


Hi Carl,

On Fri, Jul 20, 2012 at 12:08 -0600, Carl Meyer wrote:
> Hi Holger,
> 
> I love the pytest.mark.funcarg decorator.

Good to hear!  I also like it :) And i think it makes sense to just
extend the funcargs system rather than to invent a parallel "resources" one.

> I think pytest.mark.setup is likely a good idea, too, but there are some
> questions I'm not clear on:
> 
> 1. How do I handle teardown for these setup functions? I would expect
> they'd take a request and I'd do request.addfinalizer(...), but in some
> of your examples they don't seem to take request, and in the one where
> it does, it says "In addition to normal funcargs you can also receive
> the “request” funcarg which represents a takes on each of the values in
> the params=[1,2,3] decorator argument" - which I'm having trouble
> parsing, and it isn't clear to me this request object would have
> addfinalizer().

Let me fix the paragraph, it should read something like:

    This would execute the ``modes`` function once for each parameter
    which will be put at ``request.param``.  This request object offers
    the ``addfinalizer(func)`` helper which allows to register a function
    which will be executed when test functions within the specified scope 
    finished execution.

The ``request`` is a funcarg and thus setup functions can choose to
receive it or not by stating it in their signature. It will always
be available.  Depending on the scope, ``request.node`` will be the
corresponding node i guess.

> 2. It's not entirely clear how the two types of scope Floris referred to
> earlier (scope based on location of the decorated function, and the
> scope keyword passed to the decorator) interact with each other. I
> presume that if I have a setup-decorated function in a conftest.py, it
> only applies to that directory and subdirectories.

right.

> If it's located in a
> module, I guess it only applies to tests in that module? 

Yes, this has been the rule of pytest_funcarg__ and pytest_generate_tests
definitions and thus it makes sense to use the same logic i think.

> What if it's
> located in a module and I give it scope="session" - what does that mean?
> Would that be functionally equivalent to scope="module" in that case,
> since it still only applies to that module?

Yes, exactly.

> Similarly, if I decorate a
> method of a class with the setup marker, does it only apply to test
> methods on that class?

Yes. In this case, session, module and class would all have the same
meaning or at least almost the same. Currently the effectively tighter
scope is not detected so a session-scoped finalizer will be executed
at the end of a session and not at the end of the class.

> 3. Is there a "class" value for the scope kwarg, in addition to
> "session", "module", and "function"? It would be nice to see a full list
> of the accepted values for that kwarg.

so far: session, module, class, function 
i am thinking about directory - it's actually slightly tricky to implement
so i may postpone until someone really wants it :)

However, what i am thinking about is allowing to specify a function
as scope.  In that case, something like function(config) would be called 
so that you could define scope according to command line options.
This is useful when you want to have slow but better isolated 
per-function scopes for resources in CI runs.
 
> That's all that comes to mind at the moment! Thanks for all your work on
> this.

Thanks for all your feedback, it's really helpful.  My next goal is to implement
the setup-functions and do some more examples.  I believe some pretty
cool things can be done with it, one example is doing per-function 
transactions on a session-scoped database::

    # content of conftest.py
    import pytest
    
    @pytest.mark.funcarg(scope="session", ...)
    def db(request):
       ...

    @pytest.mark.setup(scope="function")
    def dbtransact(request, db):
        if should_transact(request.node.obj ...):
            db.begin()
            request.addfinalizer(db.end)

Here, test functions themselves do not need to require "db" because
th setup function requires it.  This way you can manage global resources
without explicitely caring from test functions.

best,
holger

P.S: sometimes i wonder if a web framework couldn't use similar
     ideas as funcargs/scoping/declarations/etc. for implementing
     interactions between components ... But don't worry, i am not
     going to implement my own any time soon :)



More information about the Pytest-dev mailing list