From 46dc7eeacb66a92aedcbffc38a3e78592feb30a6 Mon Sep 17 00:00:00 2001 From: holger krekel Date: Thu, 2 Aug 2012 12:41:46 +0200 Subject: [PATCH] move pytest.mark.factory/setup to pytest.factory/setup, as per flub 's suggestion --- _pytest/__init__.py | 2 +- _pytest/python.py | 52 +++++++++++++++++---- doc/en/conf.py | 2 +- doc/en/funcarg_compare.txt | 2 +- doc/en/resources.txt | 24 +++++----- doc/en/setup.txt | 18 ++++---- setup.py | 2 +- testing/test_python.py | 94 +++++++++++++++++++------------------- 8 files changed, 116 insertions(+), 80 deletions(-) diff --git a/_pytest/__init__.py b/_pytest/__init__.py index 98b4fa5e0..8e0072c6d 100644 --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.0.dev7' +__version__ = '2.3.0.dev8' diff --git a/_pytest/python.py b/_pytest/python.py index eb89e713b..bdef136f2 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -6,11 +6,45 @@ import pytest from _pytest.main import getfslineno from _pytest.monkeypatch import monkeypatch from py._code.code import TerminalRepr -from _pytest.mark import MarkInfo import _pytest cutdir = py.path.local(_pytest.__file__).dirpath() +class FactoryMarker: + def __init__(self, scope, params): + self.scope = scope + self.params = params + def __call__(self, function): + function._pytestfactory = self + return function + +class SetupMarker: + def __init__(self, scope): + self.scope = scope + def __call__(self, function): + function._pytestsetup = self + return function + +# XXX a test fails when scope="function" how it should be, investigate +def factory(scope=None, params=None): + """ return a decorator to mark functions as resource factories. + + :arg scope: the scope for which this resource is shared, one of + "function", "class", "module", "session". Defaults to "function". + :arg params: an optional list of parameters which will cause multiple + invocations of tests depending on the resource. + """ + return FactoryMarker(scope, params) + +def setup(scope="function"): + """ return a decorator to mark functions as setup functions. + + :arg scope: the scope for which the setup function will be active, one + of "function", "class", "module", "session". + Defaults to "function". + """ + return SetupMarker(scope) + def cached_property(f): """returns a cached property that is calculated by function f. taken from http://code.activestate.com/recipes/576563-cached-property/""" @@ -78,6 +112,8 @@ def pytest_sessionstart(session): def pytest_namespace(): raises.Exception = pytest.fail.Exception return { + 'factory': factory, + 'setup': setup, 'raises' : raises, 'collect': { 'Module': Module, 'Class': Class, 'Instance': Instance, @@ -1251,12 +1287,12 @@ class FuncargManager: # or are "funcarg" marked if not callable(obj): continue - marker = getattr(obj, "factory", None) - if marker is not None and isinstance(marker, MarkInfo): + marker = getattr(obj, "_pytestfactory", None) + if marker is not None: assert not name.startswith(self._argprefix) argname = name - scope = marker.kwargs.get("scope") - params = marker.kwargs.get("params") + scope = marker.scope + params = marker.params new = True elif name.startswith(self._argprefix): argname = name[len(self._argprefix):] @@ -1265,9 +1301,9 @@ class FuncargManager: new = False else: # no funcargs. check if we have a setup function. - setup = getattr(obj, "setup", None) - if setup is not None and isinstance(setup, MarkInfo): - scope = setup.kwargs.get("scope") + setup = getattr(obj, "_pytestsetup", None) + if setup is not None: + scope = setup.scope sf = SetupCall(self, nodeid, obj, scope) self.setuplist.append(sf) continue diff --git a/doc/en/conf.py b/doc/en/conf.py index f53a1757f..fac7b07e4 100644 --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -17,7 +17,7 @@ # # The full version, including alpha/beta/rc tags. # The short X.Y version. -version = release = "2.3.0.dev6" +version = release = "2.3.0.dev8" import sys, os diff --git a/doc/en/funcarg_compare.txt b/doc/en/funcarg_compare.txt index e28248724..3f9284002 100644 --- a/doc/en/funcarg_compare.txt +++ b/doc/en/funcarg_compare.txt @@ -209,7 +209,7 @@ during test execution and parametrization happens at collection time. It follows that pytest_configure/session/runtest_setup are often not appropriate for implementing common fixture needs. Therefore, -pytest-2.X introduces a new "@pytest.mark.setup" marker which takes +pytest-2.X introduces a new :ref:`@pytest.setup` marker which takes an optional "scope" parameter. See :ref:`new_setup` for examples. diff --git a/doc/en/resources.txt b/doc/en/resources.txt index c122d6e2c..97f2805de 100644 --- a/doc/en/resources.txt +++ b/doc/en/resources.txt @@ -43,7 +43,7 @@ instead of the default per-function scope. Concretely, there are three means of resource and parametrization management: -* a `@pytest.mark.factory`_ marker to define resource factories, +* a `@pytest.factory`_ marker to define resource factories, their scoping and parametrization. Factories can themselves receive resources through their function arguments, easing the setup of interdependent resources. They can also use @@ -62,16 +62,16 @@ you can use without defining them yourself. Moreover, third-party plugins offer their own resources so that after installation you can simply use them in your test and setup functions. -.. _`@pytest.mark.factory`: +.. _`@pytest.factory`: -``@pytest.mark.factory``: Creating parametrized, scoped resources +``@pytest.factory``: Creating parametrized, scoped resources ----------------------------------------------------------------- .. regendoc:wipe .. versionadded:: 2.3 -The `@pytest.mark.factory`_ marker allows to +The `@pytest.factory`_ marker allows to * mark a function as a factory for resources, useable by test and setup functions * define parameters in order to run tests multiple times with different @@ -88,7 +88,7 @@ test session:: import pytest import smtplib - @pytest.mark.factory(scope="session") + @pytest.factory(scope="session") def smtp(testcontext): smtp = smtplib.SMTP("merlinux.eu") testcontext.addfinalizer(smtp.close) @@ -152,7 +152,7 @@ the ``smtp`` factory:: import pytest import smtplib - @pytest.mark.factory(scope="session", + @pytest.factory(scope="session", params=["merlinux.eu", "mail.python.org"]) def smtp(testcontext): smtp = smtplib.SMTP(testcontext.param) @@ -256,7 +256,7 @@ object by sticking the ``smtp`` resource into it:: def __init__(self, smtp): self.smtp = smtp - @pytest.mark.factory(scope="module") + @pytest.factory(scope="module") def app(smtp): return App(smtp) @@ -301,7 +301,7 @@ scoped on a per-module basis:: # content of test_module.py import pytest - @pytest.mark.factory(scope="module", params=["mod1", "mod2"]) + @pytest.factory(scope="module", params=["mod1", "mod2"]) def modarg(testcontext): param = testcontext.param print "create", param @@ -310,7 +310,7 @@ scoped on a per-module basis:: testcontext.addfinalizer(fin) return param - @pytest.mark.factory(scope="function", params=[1,2]) + @pytest.factory(scope="function", params=[1,2]) def otherarg(testcontext): return testcontext.param @@ -363,14 +363,14 @@ resource was executed before the ``mod2`` resource was setup. ``testcontext``: interacting with test context --------------------------------------------------- -The ``testcontext`` object may be received by `@pytest.mark.factory`_ or -`@pytest.mark.setup`_ marked functions. It contains information relating +The ``testcontext`` object may be received by `@pytest.factory`_ or +`@pytest.setup`_ marked functions. It contains information relating to the test context within which the function executes. Moreover, you can call ``testcontext.addfinalizer(myfinalizer)`` in order to trigger a call to ``myfinalizer`` after the last test in the test context has executed. If passed to a parametrized factory ``testcontext.param`` will contain a parameter (one value out of the ``params`` list specified with the -`@pytest.mark.factory`_ marker). +`@pytest.factory`_ marker). .. autoclass:: _pytest.python.TestContext() :members: diff --git a/doc/en/setup.txt b/doc/en/setup.txt index 197e3ceb8..71ff7bff0 100644 --- a/doc/en/setup.txt +++ b/doc/en/setup.txt @@ -41,7 +41,7 @@ to mark functions as setup functions which: All of these features are now demonstrated by little examples. .. _`new_setup`: -.. _`@pytest.mark.setup`: +.. _`@pytest.setup`: basic per-function setup ------------------------------- @@ -56,7 +56,7 @@ the test execute with this directory as current working dir:: import pytest import os - @pytest.mark.setup() + @pytest.setup() def mydir(tmpdir): tmpdir.join("myfile").write("example content") old = tmpdir.chdir() @@ -72,7 +72,7 @@ the test execute with this directory as current working dir:: assert not os.path.exists("anotherfile") Our ``mydir`` setup function is executed on a per-function basis, -the default scope used by the ``pytest.mark.setup`` decorator. +the default scope used by the ``pytest.setup`` decorator. It accesses the ``tmpdir`` resource which provides a new empty directory path object. The ``test_function2`` here checks that it executes with a fresh directory and specifically @@ -103,7 +103,7 @@ it into a ``conftest.py`` file into the root of your project:: import pytest import os - @pytest.mark.setup() + @pytest.setup() def cleandir(tmpdir): old = tmpdir.chdir() @@ -118,7 +118,7 @@ test modules accessing a global resource If you want test modules to access a global resource, you can stick the resource to the module globals in a per-module setup function. We use a :ref:`resource factory -<@pytest.mark.factory>` to create our global resource:: +<@pytest.factory>` to create our global resource:: # content of conftest.py import pytest @@ -127,11 +127,11 @@ a per-module setup function. We use a :ref:`resource factory def __init__(self): pass - @pytest.mark.factory(scope="session") + @pytest.factory(scope="session") def globresource(): return GlobalResource() - @pytest.mark.setup(scope="module") + @pytest.setup(scope="module") def setresource(testcontext, globresource): testcontext.module.globresource = globresource @@ -169,7 +169,7 @@ factory and also add a finalizer:: def __init__(self, param): self.param = param - @pytest.mark.factory(scope="session", params=[1,2]) + @pytest.factory(scope="session", params=[1,2]) def globresource(testcontext): g = GlobalResource(testcontext.param) def fin(): @@ -177,7 +177,7 @@ factory and also add a finalizer:: testcontext.addfinalizer(fin) return g - @pytest.mark.setup(scope="module") + @pytest.setup(scope="module") def setresource(testcontext, globresource): testcontext.module.globresource = globresource diff --git a/setup.py b/setup.py index b6f2ff1cc..7672143ac 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ def main(): name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.0.dev7', + version='2.3.0.dev8', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff --git a/testing/test_python.py b/testing/test_python.py index 65d60a796..5f0d73f36 100644 --- a/testing/test_python.py +++ b/testing/test_python.py @@ -1581,7 +1581,7 @@ class TestRequestAPI: result = testdir.makeconftest(""" import pytest - @pytest.mark.setup + @pytest.setup() def mysetup(testcontext): testcontext.uses_funcarg("db") """) @@ -1595,11 +1595,11 @@ class TestFuncargFactory: def test_receives_funcargs(self, testdir): testdir.makepyfile(""" import pytest - @pytest.mark.factory + @pytest.factory() def arg1(): return 1 - @pytest.mark.factory + @pytest.factory() def arg2(arg1): return arg1 + 1 @@ -1615,11 +1615,11 @@ class TestFuncargFactory: def test_receives_funcargs_scope_mismatch(self, testdir): testdir.makepyfile(""" import pytest - @pytest.mark.factory(scope="function") + @pytest.factory(scope="function") def arg1(): return 1 - @pytest.mark.factory(scope="module") + @pytest.factory(scope="module") def arg2(arg1): return arg1 + 1 @@ -1638,12 +1638,12 @@ class TestFuncargFactory: testdir.makepyfile(""" import pytest l = [] - @pytest.mark.factory(params=[1,2]) + @pytest.factory(params=[1,2]) def arg1(testcontext): l.append(1) return testcontext.param - @pytest.mark.factory + @pytest.factory() def arg2(arg1): return arg1 + 1 @@ -1725,14 +1725,14 @@ class TestSetupDiscovery: testdir = request.getfuncargvalue("testdir") testdir.makeconftest(""" import pytest - @pytest.mark.setup + @pytest.setup() def perfunction(testcontext, tmpdir): pass - @pytest.mark.factory + @pytest.factory() def arg1(tmpdir): pass - @pytest.mark.setup + @pytest.setup() def perfunction2(arg1): pass @@ -1767,11 +1767,11 @@ class TestSetupManagement: testdir.makepyfile(""" import pytest l = [] - @pytest.mark.factory(scope="module") + @pytest.factory(scope="module") def arg(): l.append(1) return 0 - @pytest.mark.setup(scope="class") + @pytest.setup(scope="class") def something(arg): l.append(2) @@ -1792,11 +1792,11 @@ class TestSetupManagement: testdir.makepyfile(""" import pytest l = [] - @pytest.mark.factory(params=[1,2]) + @pytest.factory(params=[1,2]) def arg(testcontext): return testcontext.param - @pytest.mark.setup + @pytest.setup() def something(arg): l.append(arg) @@ -1818,11 +1818,11 @@ class TestSetupManagement: l = [] - @pytest.mark.factory(scope="session", params=[1,2]) + @pytest.factory(scope="session", params=[1,2]) def arg(testcontext): return testcontext.param - @pytest.mark.setup(scope="function") + @pytest.setup(scope="function") def append(testcontext, arg): if testcontext.function.__name__ == "test_some": l.append(arg) @@ -1844,15 +1844,15 @@ class TestSetupManagement: l = [] - @pytest.mark.factory(scope="function", params=[1,2]) + @pytest.factory(scope="function", params=[1,2]) def farg(testcontext): return testcontext.param - @pytest.mark.factory(scope="class", params=list("ab")) + @pytest.factory(scope="class", params=list("ab")) def carg(testcontext): return testcontext.param - @pytest.mark.setup(scope="class") + @pytest.setup(scope="class") def append(testcontext, farg, carg): def fin(): l.append("fin_%s%s" % (carg, farg)) @@ -1878,7 +1878,7 @@ class TestFuncargMarker: def test_parametrize(self, testdir): testdir.makepyfile(""" import pytest - @pytest.mark.factory(params=["a", "b", "c"]) + @pytest.factory(params=["a", "b", "c"]) def arg(testcontext): return testcontext.param l = [] @@ -1894,7 +1894,7 @@ class TestFuncargMarker: testdir.makepyfile(""" import pytest l = [] - @pytest.mark.factory(scope="module") + @pytest.factory(scope="module") def arg(): l.append(1) return 1 @@ -1916,7 +1916,7 @@ class TestFuncargMarker: testdir.makepyfile(""" import pytest l = [] - @pytest.mark.factory(scope="module") + @pytest.factory(scope="module") def arg(): l.append(1) return 1 @@ -1939,7 +1939,7 @@ class TestFuncargMarker: import pytest finalized = [] created = [] - @pytest.mark.factory(scope="module") + @pytest.factory(scope="module") def arg(testcontext): created.append(1) assert testcontext.scope == "module" @@ -1978,14 +1978,14 @@ class TestFuncargMarker: import pytest finalized = [] created = [] - @pytest.mark.factory(scope="function") + @pytest.factory(scope="function") def arg(testcontext): pass """) testdir.makepyfile( test_mod1=""" import pytest - @pytest.mark.factory(scope="session") + @pytest.factory(scope="session") def arg(testcontext): %s def test_1(arg): @@ -2000,14 +2000,14 @@ class TestFuncargMarker: def test_register_only_with_mark(self, testdir): testdir.makeconftest(""" import pytest - @pytest.mark.factory + @pytest.factory() def arg(): return 1 """) testdir.makepyfile( test_mod1=""" import pytest - @pytest.mark.factory + @pytest.factory() def arg(arg): return arg + 1 def test_1(arg): @@ -2019,7 +2019,7 @@ class TestFuncargMarker: def test_parametrize_and_scope(self, testdir): testdir.makepyfile(""" import pytest - @pytest.mark.factory(scope="module", params=["a", "b", "c"]) + @pytest.factory(scope="module", params=["a", "b", "c"]) def arg(testcontext): return testcontext.param l = [] @@ -2037,13 +2037,13 @@ class TestFuncargMarker: def test_scope_mismatch(self, testdir): testdir.makeconftest(""" import pytest - @pytest.mark.factory(scope="function") + @pytest.factory(scope="function") def arg(testcontext): pass """) testdir.makepyfile(""" import pytest - @pytest.mark.factory(scope="session") + @pytest.factory(scope="session") def arg(arg): pass def test_mismatch(arg): @@ -2059,7 +2059,7 @@ class TestFuncargMarker: testdir.makepyfile(""" import pytest - @pytest.mark.factory(scope="module", params=[1, 2]) + @pytest.factory(scope="module", params=[1, 2]) def arg(testcontext): return testcontext.param @@ -2078,10 +2078,10 @@ class TestFuncargMarker: testdir.makeconftest(""" import pytest - @pytest.mark.factory(scope="session", params="s1 s2".split()) + @pytest.factory(scope="session", params="s1 s2".split()) def sarg(): pass - @pytest.mark.factory(scope="module", params="m1 m2".split()) + @pytest.factory(scope="module", params="m1 m2".split()) def marg(): pass """) @@ -2126,15 +2126,15 @@ class TestFuncargMarker: l = [] - @pytest.mark.factory(scope="function", params=[1,2]) + @pytest.factory(scope="function", params=[1,2]) def farg(testcontext): return testcontext.param - @pytest.mark.factory(scope="class", params=list("ab")) + @pytest.factory(scope="class", params=list("ab")) def carg(testcontext): return testcontext.param - @pytest.mark.setup(scope="class") + @pytest.setup(scope="class") def append(testcontext, farg, carg): def fin(): l.append("fin_%s%s" % (carg, farg)) @@ -2172,14 +2172,14 @@ class TestFuncargMarker: testdir.makepyfile(""" import pytest - @pytest.mark.factory(scope="function", params=[1, 2]) + @pytest.factory(scope="function", params=[1, 2]) def arg(testcontext): param = testcontext.param testcontext.addfinalizer(lambda: l.append("fin:%s" % param)) l.append("create:%s" % param) return testcontext.param - @pytest.mark.factory(scope="module", params=["mod1", "mod2"]) + @pytest.factory(scope="module", params=["mod1", "mod2"]) def modarg(testcontext): param = testcontext.param testcontext.addfinalizer(lambda: l.append("fin:%s" % param)) @@ -2216,7 +2216,7 @@ class TestFuncargMarker: testdir.makepyfile(""" import pytest - @pytest.mark.factory(scope="module", params=[1, 2]) + @pytest.factory(scope="module", params=[1, 2]) def arg(testcontext): testcontext.config.l = l # to access from outer x = testcontext.param @@ -2245,7 +2245,7 @@ class TestFuncargMarker: testdir.makepyfile(""" import pytest - @pytest.mark.factory(scope="function", params=[1, 2]) + @pytest.factory(scope="function", params=[1, 2]) def arg(testcontext): x = testcontext.param testcontext.addfinalizer(lambda: l.append("fin%s" % x)) @@ -2267,11 +2267,11 @@ class TestFuncargMarker: testdir.makepyfile(""" import pytest - @pytest.mark.factory(scope="module", params=[1, 2]) + @pytest.factory(scope="module", params=[1, 2]) def arg(testcontext): return testcontext.param - @pytest.mark.setup(scope="module") + @pytest.setup(scope="module") def mysetup(testcontext, arg): testcontext.addfinalizer(lambda: l.append("fin%s" % arg)) l.append("setup%s" % arg) @@ -2304,7 +2304,7 @@ class TestTestContextScopeAccess: def test_setup(self, testdir, scope, ok, error): testdir.makepyfile(""" import pytest - @pytest.mark.setup(scope=%r) + @pytest.setup(scope=%r) def myscoped(testcontext): for x in %r: assert hasattr(testcontext, x) @@ -2322,7 +2322,7 @@ class TestTestContextScopeAccess: def test_resource(self, testdir, scope, ok, error): testdir.makepyfile(""" import pytest - @pytest.mark.factory(scope=%r) + @pytest.factory(scope=%r) def arg(testcontext): for x in %r: assert hasattr(testcontext, x) @@ -2341,7 +2341,7 @@ class TestTestContextVarious: def test_newstyle_no_request(self, testdir): testdir.makepyfile(""" import pytest - @pytest.mark.factory + @pytest.factory() def arg(request): pass def test_1(arg): @@ -2355,11 +2355,11 @@ class TestTestContextVarious: def test_setupcontext_no_param(self, testdir): testdir.makepyfile(""" import pytest - @pytest.mark.factory(params=[1,2]) + @pytest.factory(params=[1,2]) def arg(testcontext): return testcontext.param - @pytest.mark.setup + @pytest.setup() def mysetup(testcontext, arg): assert not hasattr(testcontext, "param") def test_1(arg):