[py-dev] RFC: draft new resource management API (v1)
holger krekel
holger at merlinux.eu
Wed Jun 27 18:15:54 CEST 2012
On Wed, Jun 27, 2012 at 08:43 -0600, Carl Meyer wrote:
> I like it! In particular the parametrization support by passing a list
> is a quite intuitive extension of the API.
>
> "atnode" seems like an opaque arg name - what's wrong with "scope"? The
> latter name seems more intuitive to me. Would this arg have a default value?
"scope" makes sense - it's just that in the current API scope is a
"class", "module", ... string. Existing users might easily get a bit of
type clash - especially if you have a mixed funcarg/resource scenario.
Maybe "scopenode"?
The default scopenode would be the one on which you are calling
"register_factory". So in the first documented example call:
session.register_factory("db", createdb, scopenode=session)
the "scopenode" call would actually be superfluous. (Sidenote: the session
object is also a node - the root node from which all collection and item
nodes are descendants. Each node has a ".session" reference back to this
root node).
> In the long run, if funcarg-style is considered a useful shortcut and
> will not be deprecated, it would be nice if there were a bit more naming
> and API consistency between funcarg-style and new-style resource
> handling -- it would make them feel more aspects of one system rather
> than two different systems. I think this would really just require
> switching from pytest_funcarg__foo to pytest_resource__foo, renaming
> cached_setup to register_factory (and having it use the same API), and
> renaming getfuncargvalue to getresource. Of course I don't know whether
> this consistency is really worth the backwards-compatibility/deprecation
> hassles.
* getresource/getfuncargvalue: makes sense to me to go for advertising and
documenting getresource() instead of getfuncargvalue() and keeping
the latter as an alias with or without deprecation.
* addfinalizer would remain unmodified - it's just that the "request"
object passed to funcarg-factories adds finalizers with test function
invocation scope, whereas node.addfinalizer() does it for the respective
node scope (so e.g. called from a Class node it would register a per-class
finalizer)
* cached_setup: i hope that we do not need to offer this method anymore
other than for compatibility. It's internal caching-key is not easy
to explain and more than once users have stumbled about understanding it.
cached_setup is required as long as pytest_funcarg__ factories are called
_each_ time a resource is requested. (By contrast the new getresource()
only triggers a factory call once for the registered scope - thus
the factory implementation itself does not need to care for caching).
Note that register_factory is a different beast than cached_setup:
it does not create a value, just registers a factory. So i don't see
how we can unify this.
As to a possible resource-factory auto-discovery, i can imagine it to
work with introducing a marker::
# example content in a test module or in a conftest.py file
@pytest.mark.resourcefactory("db", scope=pytest.Class)
def myfactory(name, node):
# factory called once per each requesting class (methods
# on this class will share the returned value)
this declaration would trigger a register_factory("db", myfactory) call.
If we want to extend this to parametrization (multiple db factories)
we probably need something like this::
@pytest.mark.resourcefactory("db", scope=pytest.Class, multi=True)
def make_db_factories(name, node):
factoryfuncs = [compute list of factory funcs]
return factoryfuncs
This would be called at collection time and the scope and the number
of to-be-created values would be known in advance. It's basically
equivalent to a classnode.register_factory([list of factory funcs]) call.
(we could auto-magically recognize yield-generating functions but i'd
like to avoid it).
To go the full circle, the signature of factory functions could rather
accept a "request" object instead of (name, node). Actually today, a
request object has this internal state anyway. pytest_funcarg__ would thus
only look slighly special in that it skips the marker and has a fixed scope
of "pytest.Function".
Hope this thought train makes some sense :)
holger
More information about the Pytest-dev
mailing list