Sorting per-resource
-----------------------------

for any given set of items:

- collect items per session-scoped parametrized funcarg
- re-order until items no parametrizations are mixed

  examples:

        test()
        test1(s1)
        test1(s2)     
        test2()
        test3(s1)
        test3(s2)

  gets sorted to:

        test()
        test2()
        test1(s1)
        test3(s1)
        test1(s2)
        test3(s2)
 

the new @setup functions 
--------------------------------------

Consider a given @setup-marked function::

    @pytest.mark.setup(maxscope=SCOPE)
    def mysetup(request, arg1, arg2, ...)
        ...
        request.addfinalizer(fin)
        ...

then FUNCARGSET denotes the set of (arg1, arg2, ...) funcargs and
all of its dependent funcargs.  The mysetup function will execute
for any matching test item once per scope.  

The scope is determined as the minimum scope of all scopes of the args
in FUNCARGSET and the given "maxscope". 

If mysetup has been called and no finalizers have been called it is
called "active".

Furthermore the following rules apply:

- if an arg value in FUNCARGSET is about to be torn down, the 
  mysetup-registered finalizers will execute as well.

- There will never be two active mysetup invocations.

Example 1, session scope::

    @pytest.mark.funcarg(scope="session", params=[1,2])
    def db(request):
        request.addfinalizer(db_finalize)

    @pytest.mark.setup
    def mysetup(request, db):
        request.addfinalizer(mysetup_finalize)
        ...

And a given test module:

    def test_something():
        ...
    def test_otherthing():
        pass

Here is what happens::

    db(request) executes with request.param == 1
        mysetup(request, db) executes
            test_something() executes
            test_otherthing() executes
            mysetup_finalize() executes
    db_finalize() executes
    db(request) executes with request.param == 2
        mysetup(request, db) executes
            test_something() executes
            test_otherthing() executes
        mysetup_finalize() executes
    db_finalize() executes

Example 2, session/function scope::

    @pytest.mark.funcarg(scope="session", params=[1,2])
    def db(request):
        request.addfinalizer(db_finalize)

    @pytest.mark.setup(scope="function")
    def mysetup(request, db):
        ...
        request.addfinalizer(mysetup_finalize)
        ...

And a given test module:

    def test_something():
        ...
    def test_otherthing():
        pass

Here is what happens::

    db(request) executes with request.param == 1
        mysetup(request, db) executes
            test_something() executes
        mysetup_finalize() executes
        mysetup(request, db) executes
            test_otherthing() executes
        mysetup_finalize() executes
    db_finalize() executes
    db(request) executes with request.param == 2
        mysetup(request, db) executes
            test_something() executes
        mysetup_finalize() executes
        mysetup(request, db) executes
            test_otherthing() executes
        mysetup_finalize() executes
    db_finalize() executes


Example 3 - funcargs session-mix
----------------------------------------

Similar with funcargs, an example::

    @pytest.mark.funcarg(scope="session", params=[1,2])
    def db(request):
        request.addfinalizer(db_finalize)

    @pytest.mark.funcarg(scope="function")
    def table(request, db):
        ...
        request.addfinalizer(table_finalize)
        ...

And a given test module:

    def test_something(table):
        ...
    def test_otherthing(table):
        pass
    def test_thirdthing():
        pass

Here is what happens::

    db(request) executes with param == 1
        table(request, db)
            test_something(table)
        table_finalize()
        table(request, db)
            test_otherthing(table)
        table_finalize()
    db_finalize
    db(request) executes with param == 2
        table(request, db)
            test_something(table)
        table_finalize()
        table(request, db)
            test_otherthing(table)
        table_finalize()
    db_finalize
    test_thirdthing()
    
Data structures
--------------------

pytest internally maintains a dict of active funcargs with cache, param,
finalizer, (scopeitem?) information:

    active_funcargs = dict()

if a parametrized "db" is activated:
    
    active_funcargs["db"] = FuncargInfo(dbvalue, paramindex, 
                                        FuncargFinalize(...), scopeitem)

if a test is torn down and the next test requires a differently 
parametrized "db":

    for argname in item.callspec.params:
        if argname in active_funcargs:
            funcarginfo = active_funcargs[argname]
            if funcarginfo.param != item.callspec.params[argname]:
                funcarginfo.callfinalizer()
                del node2funcarg[funcarginfo.scopeitem]
                del active_funcargs[argname]
    nodes_to_be_torn_down = ...
    for node in nodes_to_be_torn_down:
        if node in node2funcarg:
            argname = node2funcarg[node]
            active_funcargs[argname].callfinalizer()
            del node2funcarg[node]
            del active_funcargs[argname]

if a test is setup requiring a "db" funcarg:

    if "db" in active_funcargs:
        return active_funcargs["db"][0]
    funcarginfo = setup_funcarg()
    active_funcargs["db"] = funcarginfo
    node2funcarg[funcarginfo.scopeitem] = "db"

Implementation plan for resources
------------------------------------------

1. Revert FuncargRequest to the old form, unmerge item/request
   (done)
2. make funcarg factories be discovered at collection time
3. Introduce funcarg marker
4. Introduce funcarg scope parameter
5. Introduce funcarg parametrize parameter
6. make setup functions be discovered at collection time
7. (Introduce a pytest_fixture_protocol/setup_funcargs hook)

methods and data structures
--------------------------------

A FuncarcManager holds all information about funcarg definitions
including parametrization and scope definitions.  It implements
a pytest_generate_tests hook which performs parametrization as appropriate.

as a simple example, let's consider a tree where a test function requires
a "abc" funcarg and its factory defines it as parametrized and scoped
for Modules.  When collections hits the function item, it creates
the metafunc object, and calls funcargdb.pytest_generate_tests(metafunc)
which looks up available funcarg factories and their scope and parametrization.
This information is equivalent to what can be provided today directly
at the function site and it should thus be relatively straight forward
to implement the additional way of defining parametrization/scoping.

conftest loading:
    each funcarg-factory will populate the session.funcargmanager

When a test item is collected, it grows a dictionary 
(funcargname2factorycalllist).  A factory lookup is performed 
for each required funcarg.  The resulting factory call is stored 
with the item.  If a function is parametrized multiple items are 
created with respective factory calls. Else if a factory is parametrized
multiple items and calls to the factory function are created as well.

At setup time, an item populates a funcargs mapping, mapping names
to values.  If a value is funcarg factories are queried for a given item
test functions and setup functions are put in a class
which looks up required funcarg factories.