7 new commits in pytest:

https://bitbucket.org/hpk42/pytest/changeset/baed16626500/
changeset:   baed16626500
user:        hpk42
date:        2012-10-16 13:47:59
summary:     add plan for better fixture implementation, an xfailing test
and a slight refactoring of Metafunc tests/creation
affected #:  3 files

diff -r 0cf1ef1efbea1aae3d892ff97dec2b8d4ac4231d -r 
baed16626500839e76c81901e9112cc898b61536 IMPL.txt
--- /dev/null
+++ b/IMPL.txt
@@ -0,0 +1,40 @@
+
+internal fixture mechanics
+----------------------------
+
+fixture mechanics are contained in the python.py plugin.
+
+pytest fixtures are stored and managed from the FixtureManager (fm) class.
+Upon collection fm.parsefactories() is called multiple times to parse
+fixture function definitions into FixtureDef objects.
+
+During collection of test functions, metafunc needs to grow a "fixturenames"
+list so that pytest_generate_tests() hooks can check it.   These fixturenames
+are a closure of all known fixtures to be used for this function:
+
+- ini-defined usefixtures
+- autouse-marked fixtures along the collection chain up from the function
+- usefixtures markers at module/class/function level
+- test function funcargs
+
+The latter list (without the closure) is also called "_initialfixtures"
+and will be used during the test setup phase.
+
+Upon the test-setup phases initialfixtures are instantiated which
+will subsequently create the full fixture closure (as was computed in
+metafunc.fixturenames during collection).  As fixture functions
+can invoke request.getfuncargvalue() the actual closure may be even
+bigger.
+
+object model
+---------------------
+
+As part of the metafunc-protocol parents of Function nodes get a
+FuncFixtureInfos() object, containing helping/caching for 
+for a function. .getfixtureinfo(func) returns a FixtureInfo with these
+attributes:
+
+- names_initial: list of initial fixture names (see above)
+- names_closure: closure of all fixture names
+- name2fixturedeflist: for creating the value
+


diff -r 0cf1ef1efbea1aae3d892ff97dec2b8d4ac4231d -r 
baed16626500839e76c81901e9112cc898b61536 _pytest/python.py
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -309,8 +309,7 @@
         clscol = self.getparent(Class)
         cls = clscol and clscol.obj or None
         transfer_markers(funcobj, cls, module)
-        metafunc = Metafunc(funcobj, parentnode=self, config=self.config,
-            cls=cls, module=module)
+        metafunc = Metafunc(funcobj, parentnode=self, cls=cls, module=module)
         gentesthook = self.config.hook.pytest_generate_tests
         extra = [module]
         if cls is not None:
@@ -612,25 +611,23 @@
         return self.fixturenames
 
 class Metafunc(FuncargnamesCompatAttr):
-    def __init__(self, function, config=None, cls=None, module=None,
-                 parentnode=None):
-        self.config = config
+    def __init__(self, function, parentnode, cls=None, module=None):
+        self.config = parentnode.config
         self.module = module
         self.function = function
         self.parentnode = parentnode
-        self._parentid = getattr(parentnode, "nodeid", "")
         argnames = getfuncargnames(function, startindex=int(cls is not None))
-        if parentnode is not None:
+        try:
             fm = parentnode.session._fixturemanager
+        except AttributeError:
+            self.fixturenames = argnames
+        else:
             self.fixturenames, self._arg2fixturedeflist = fm.getfixtureclosure(
                 argnames, parentnode)
-        else:
-            self.fixturenames = argnames
         self.cls = cls
         self.module = module
         self._calls = []
         self._ids = py.builtin.set()
-        self._arg2scopenum = {}
 
     def parametrize(self, argnames, argvalues, indirect=False, ids=None,
         scope=None):


diff -r 0cf1ef1efbea1aae3d892ff97dec2b8d4ac4231d -r 
baed16626500839e76c81901e9112cc898b61536 testing/test_python.py
--- a/testing/test_python.py
+++ b/testing/test_python.py
@@ -284,22 +284,22 @@
             pass
         def func2():
             pass
-        f1 = pytest.Function(name="name", config=config,
-                args=(1,), callobj=func1, session=session)
+        f1 = pytest.Function(name="name", parent=session, config=config,
+                args=(1,), callobj=func1)
         f2 = pytest.Function(name="name",config=config,
-                args=(1,), callobj=func2, session=session)
+                args=(1,), callobj=func2, parent=session)
         assert not f1 == f2
         assert f1 != f2
-        f3 = pytest.Function(name="name", config=config,
-                args=(1,2), callobj=func2, session=session)
+        f3 = pytest.Function(name="name", parent=session, config=config,
+                args=(1,2), callobj=func2)
         assert not f3 == f2
         assert f3 != f2
 
         assert not f3 == f1
         assert f3 != f1
 
-        f1_b = pytest.Function(name="name", config=config,
-              args=(1,), callobj=func1, session=session)
+        f1_b = pytest.Function(name="name", parent=session, config=config,
+              args=(1,), callobj=func1)
         assert f1 == f1_b
         assert not f1 != f1_b
 
@@ -909,15 +909,20 @@
         ])
 
 class TestMetafunc:
+    def Metafunc(self, func):
+        class parent:
+            config = None
+        return funcargs.Metafunc(func, parentnode=parent)
+
     def test_no_funcargs(self, testdir):
         def function(): pass
-        metafunc = funcargs.Metafunc(function)
+        metafunc = self.Metafunc(function)
         assert not metafunc.fixturenames
         repr(metafunc._calls)
 
     def test_function_basic(self):
         def func(arg1, arg2="qwe"): pass
-        metafunc = funcargs.Metafunc(func)
+        metafunc = self.Metafunc(func)
         assert len(metafunc.fixturenames) == 1
         assert 'arg1' in metafunc.fixturenames
         assert metafunc.function is func
@@ -925,7 +930,7 @@
 
     def test_addcall_no_args(self):
         def func(arg1): pass
-        metafunc = funcargs.Metafunc(func)
+        metafunc = self.Metafunc(func)
         metafunc.addcall()
         assert len(metafunc._calls) == 1
         call = metafunc._calls[0]
@@ -934,7 +939,7 @@
 
     def test_addcall_id(self):
         def func(arg1): pass
-        metafunc = funcargs.Metafunc(func)
+        metafunc = self.Metafunc(func)
         pytest.raises(ValueError, "metafunc.addcall(id=None)")
 
         metafunc.addcall(id=1)
@@ -947,7 +952,7 @@
 
     def test_addcall_param(self):
         def func(arg1): pass
-        metafunc = funcargs.Metafunc(func)
+        metafunc = self.Metafunc(func)
         class obj: pass
         metafunc.addcall(param=obj)
         metafunc.addcall(param=obj)
@@ -959,7 +964,7 @@
 
     def test_addcall_funcargs(self):
         def func(x): pass
-        metafunc = funcargs.Metafunc(func)
+        metafunc = self.Metafunc(func)
         class obj: pass
         metafunc.addcall(funcargs={"x": 2})
         metafunc.addcall(funcargs={"x": 3})
@@ -971,7 +976,7 @@
 
     def test_parametrize_error(self):
         def func(x, y): pass
-        metafunc = funcargs.Metafunc(func)
+        metafunc = self.Metafunc(func)
         metafunc.parametrize("x", [1,2])
         pytest.raises(ValueError, lambda: metafunc.parametrize("x", [5,6]))
         pytest.raises(ValueError, lambda: metafunc.parametrize("x", [5,6]))
@@ -981,7 +986,7 @@
 
     def test_parametrize_and_id(self):
         def func(x, y): pass
-        metafunc = funcargs.Metafunc(func)
+        metafunc = self.Metafunc(func)
 
         metafunc.parametrize("x", [1,2], ids=['basic', 'advanced'])
         metafunc.parametrize("y", ["abc", "def"])
@@ -990,7 +995,7 @@
 
     def test_parametrize_with_userobjects(self):
         def func(x, y): pass
-        metafunc = funcargs.Metafunc(func)
+        metafunc = self.Metafunc(func)
         class A:
             pass
         metafunc.parametrize("x", [A(), A()])
@@ -1002,7 +1007,7 @@
 
     def test_addcall_and_parametrize(self):
         def func(x, y): pass
-        metafunc = funcargs.Metafunc(func)
+        metafunc = self.Metafunc(func)
         metafunc.addcall({'x': 1})
         metafunc.parametrize('y', [2,3])
         assert len(metafunc._calls) == 2
@@ -1013,7 +1018,7 @@
 
     def test_parametrize_indirect(self):
         def func(x, y): pass
-        metafunc = funcargs.Metafunc(func)
+        metafunc = self.Metafunc(func)
         metafunc.parametrize('x', [1], indirect=True)
         metafunc.parametrize('y', [2,3], indirect=True)
         metafunc.parametrize('unnamed', [1], indirect=True)
@@ -1025,7 +1030,7 @@
 
     def test_addcalls_and_parametrize_indirect(self):
         def func(x, y): pass
-        metafunc = funcargs.Metafunc(func)
+        metafunc = self.Metafunc(func)
         metafunc.addcall(param="123")
         metafunc.parametrize('x', [1], indirect=True)
         metafunc.parametrize('y', [2,3], indirect=True)
@@ -1038,7 +1043,6 @@
     def test_parametrize_functional(self, testdir):
         testdir.makepyfile("""
             def pytest_generate_tests(metafunc):
-                assert "test_parametrize_functional" in metafunc._parentid
                 metafunc.parametrize('x', [1,2], indirect=True)
                 metafunc.parametrize('y', [2])
             def pytest_funcarg__x(request):
@@ -1058,7 +1062,7 @@
         ])
 
     def test_parametrize_onearg(self):
-        metafunc = funcargs.Metafunc(lambda x: None)
+        metafunc = self.Metafunc(lambda x: None)
         metafunc.parametrize("x", [1,2])
         assert len(metafunc._calls) == 2
         assert metafunc._calls[0].funcargs == dict(x=1)
@@ -1067,7 +1071,7 @@
         assert metafunc._calls[1].id == "2"
 
     def test_parametrize_onearg_indirect(self):
-        metafunc = funcargs.Metafunc(lambda x: None)
+        metafunc = self.Metafunc(lambda x: None)
         metafunc.parametrize("x", [1,2], indirect=True)
         assert metafunc._calls[0].params == dict(x=1)
         assert metafunc._calls[0].id == "1"
@@ -1075,7 +1079,7 @@
         assert metafunc._calls[1].id == "2"
 
     def test_parametrize_twoargs(self):
-        metafunc = funcargs.Metafunc(lambda x,y: None)
+        metafunc = self.Metafunc(lambda x,y: None)
         metafunc.parametrize(("x", "y"), [(1,2), (3,4)])
         assert len(metafunc._calls) == 2
         assert metafunc._calls[0].funcargs == dict(x=1, y=2)
@@ -2004,6 +2008,28 @@
         reprec = testdir.inline_run("-s")
         reprec.assertoutcome(passed=1)
 
+    @pytest.mark.xfail
+    def test_two_classes_separated_autouse(self, testdir):
+        testdir.makepyfile("""
+            import pytest
+            class TestA:
+                l = []
+                @pytest.fixture(autouse=True)
+                def setup1(self):
+                    self.l.append(1)
+                def test_setup1(self):
+                    assert self.l == [1]
+            class TestB:
+                l = []
+                @pytest.fixture(autouse=True)
+                def setup2(self):
+                    self.l.append(1)
+                def test_setup2(self):
+                    assert self.l == [1]
+        """)
+        reprec = testdir.inline_run()
+        reprec.assertoutcome(passed=2)
+
     def test_setup_at_classlevel(self, testdir):
         testdir.makepyfile("""
             import pytest



https://bitbucket.org/hpk42/pytest/changeset/20d77f40c3ab/
changeset:   20d77f40c3ab
user:        hpk42
date:        2012-10-16 13:47:59
summary:     implement fixture information stored on the parentnode of functions
to be reused by metafunc mechanics and Function setup
affected #:  4 files

diff -r baed16626500839e76c81901e9112cc898b61536 -r 
20d77f40c3abab579f2be1b310db8529b5234e8c IMPL.txt
--- a/IMPL.txt
+++ b/IMPL.txt
@@ -30,7 +30,7 @@
 ---------------------
 
 As part of the metafunc-protocol parents of Function nodes get a
-FuncFixtureInfos() object, containing helping/caching for 
+ParentFixtures() object, containing helping/caching for 
 for a function. .getfixtureinfo(func) returns a FixtureInfo with these
 attributes:
 


diff -r baed16626500839e76c81901e9112cc898b61536 -r 
20d77f40c3abab579f2be1b310db8529b5234e8c _pytest/python.py
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -309,7 +309,11 @@
         clscol = self.getparent(Class)
         cls = clscol and clscol.obj or None
         transfer_markers(funcobj, cls, module)
-        metafunc = Metafunc(funcobj, parentnode=self, cls=cls, module=module)
+        if not hasattr(self, "_fixturemapper"):
+            self._fixturemapper = FixtureMapper(self)
+        fixtureinfo = self._fixturemapper.getfixtureinfo(funcobj, cls)
+        metafunc = Metafunc(funcobj, fixtureinfo, self.config,
+                            cls=cls, module=module)
         gentesthook = self.config.hook.pytest_generate_tests
         extra = [module]
         if cls is not None:
@@ -326,6 +330,32 @@
                                callspec=callspec, callobj=funcobj,
                                keywords={callspec.id:True})
 
+class FixtureMapper:
+    def __init__(self, node):
+        self.node = node
+        self.fm = node.session._fixturemanager
+        self._name2fixtureinfo = {}
+
+    def getfixtureinfo(self, func, cls):
+        try:
+            return self._name2fixtureinfo[func]
+        except KeyError:
+            pass
+        argnames = getfuncargnames(func, int(cls is not None))
+        usefixtures = getattr(func, "usefixtures", None)
+        if usefixtures is not None:
+            argnames = usefixtures.args + argnames
+        names_closure, arg2fixturedeflist = self.fm.getfixtureclosure(
+                argnames, self.node)
+        fixtureinfo = FuncFixtureInfo(names_closure, arg2fixturedeflist)
+        self._name2fixtureinfo[func] = fixtureinfo
+        return fixtureinfo
+
+class FuncFixtureInfo:
+    def __init__(self, names_closure, name2fixturedeflist):
+        self.names_closure = names_closure
+        self.name2fixturedeflist = name2fixturedeflist
+
 def transfer_markers(funcobj, cls, mod):
     # XXX this should rather be code in the mark plugin or the mark
     # plugin should merge with the python plugin.
@@ -611,19 +641,12 @@
         return self.fixturenames
 
 class Metafunc(FuncargnamesCompatAttr):
-    def __init__(self, function, parentnode, cls=None, module=None):
-        self.config = parentnode.config
+    def __init__(self, function, fixtureinfo, config, cls=None, module=None):
+        self.config = config
         self.module = module
         self.function = function
-        self.parentnode = parentnode
-        argnames = getfuncargnames(function, startindex=int(cls is not None))
-        try:
-            fm = parentnode.session._fixturemanager
-        except AttributeError:
-            self.fixturenames = argnames
-        else:
-            self.fixturenames, self._arg2fixturedeflist = fm.getfixtureclosure(
-                argnames, parentnode)
+        self.fixturenames = fixtureinfo.names_closure
+        self._arg2fixturedeflist = fixtureinfo.name2fixturedeflist
         self.cls = cls
         self.module = module
         self._calls = []
@@ -878,7 +901,7 @@
     Python test function.
     """
     _genid = None
-    def __init__(self, name, parent=None, args=None, config=None,
+    def __init__(self, name, parent, args=None, config=None,
                  callspec=None, callobj=_dummy, keywords=None, session=None):
         super(Function, self).__init__(name, parent, config=config,
                                        session=session)
@@ -908,9 +931,9 @@
 
         # contstruct a list of all neccessary fixtures for this test function
         try:
-            usefixtures = list(self.markers.usefixtures.args)
+            usefixtures = self.markers.usefixtures.args
         except AttributeError:
-            usefixtures = []
+            usefixtures = ()
         self.fixturenames = (self.session._fixturemanager.getdefaultfixtures() 
+
                 usefixtures + self._getfuncargnames())
 
@@ -1377,16 +1400,16 @@
             self.pytest_plugin_registered(plugin)
 
     def getdefaultfixtures(self):
-        """ return a list of default fixture names (XXX for the given file 
path). """
+        """ return a tuple of default fixture names (XXX for the given file 
path). """
         try:
             return self._defaultfixtures
         except AttributeError:
-            defaultfixtures = list(self.config.getini("usefixtures"))
+            defaultfixtures = tuple(self.config.getini("usefixtures"))
             # make sure the self._autofixtures list is sorted
             # by scope, scopenum 0 is session
             self._autofixtures.sort(
                 key=lambda x: self.arg2fixturedeflist[x][-1].scopenum)
-            defaultfixtures.extend(self._autofixtures)
+            defaultfixtures = defaultfixtures + tuple(self._autofixtures)
             self._defaultfixtures = defaultfixtures
             return defaultfixtures
 
@@ -1573,8 +1596,8 @@
                        getattr(function, '__defaults__', None)) or ()
     numdefaults = len(defaults)
     if numdefaults:
-        return argnames[startindex:-numdefaults]
-    return argnames[startindex:]
+        return tuple(argnames[startindex:-numdefaults])
+    return tuple(argnames[startindex:])
 
 # algorithm for sorting on a per-parametrized resource setup basis
 


diff -r baed16626500839e76c81901e9112cc898b61536 -r 
20d77f40c3abab579f2be1b310db8529b5234e8c _pytest/unittest.py
--- a/_pytest/unittest.py
+++ b/_pytest/unittest.py
@@ -53,7 +53,7 @@
     _excinfo = None
 
     def _getfuncargnames(self):
-        return []
+        return ()
 
     def setup(self):
         self._testcase = self.parent.obj(self.name)


diff -r baed16626500839e76c81901e9112cc898b61536 -r 
20d77f40c3abab579f2be1b310db8529b5234e8c testing/test_python.py
--- a/testing/test_python.py
+++ b/testing/test_python.py
@@ -535,17 +535,17 @@
     def f(): pass
     assert not funcargs.getfuncargnames(f)
     def g(arg): pass
-    assert funcargs.getfuncargnames(g) == ['arg']
+    assert funcargs.getfuncargnames(g) == ('arg',)
     def h(arg1, arg2="hello"): pass
-    assert funcargs.getfuncargnames(h) == ['arg1']
+    assert funcargs.getfuncargnames(h) == ('arg1',)
     def h(arg1, arg2, arg3="hello"): pass
-    assert funcargs.getfuncargnames(h) == ['arg1', 'arg2']
+    assert funcargs.getfuncargnames(h) == ('arg1', 'arg2')
     class A:
         def f(self, arg1, arg2="hello"):
             pass
-    assert funcargs.getfuncargnames(A().f) == ['arg1']
+    assert funcargs.getfuncargnames(A().f) == ('arg1',)
     if sys.version_info < (3,0):
-        assert funcargs.getfuncargnames(A.f) == ['arg1']
+        assert funcargs.getfuncargnames(A.f) == ('arg1',)
 
 
 class TestFillFixtures:
@@ -910,9 +910,16 @@
 
 class TestMetafunc:
     def Metafunc(self, func):
-        class parent:
-            config = None
-        return funcargs.Metafunc(func, parentnode=parent)
+        # the unit tests of this class check if things work correctly
+        # on the funcarg level, so we don't need a full blown
+        # initiliazation
+        class FixtureInfo:
+            name2fixturedeflist = None
+            def __init__(self, names):
+                self.names_closure = names
+        names = funcargs.getfuncargnames(func)
+        fixtureinfo = FixtureInfo(names)
+        return funcargs.Metafunc(func, fixtureinfo, None)
 
     def test_no_funcargs(self, testdir):
         def function(): pass
@@ -1393,6 +1400,20 @@
         reprec = testdir.inline_run()
         reprec.assertoutcome(passed=5)
 
+    def test_usemarkers_seen_in_generate_tests(self, testdir):
+        testdir.makepyfile("""
+            import pytest
+            def pytest_generate_tests(metafunc):
+                assert "abc" in metafunc.fixturenames
+                metafunc.parametrize("abc", [1])
+
+            @pytest.mark.usefixtures("abc")
+            def test_function():
+                pass
+        """)
+        reprec = testdir.inline_run()
+        reprec.assertoutcome(passed=1)
+
 def test_conftest_funcargs_only_available_in_subdir(testdir):
     sub1 = testdir.mkpydir("sub1")
     sub2 = testdir.mkpydir("sub2")



https://bitbucket.org/hpk42/pytest/changeset/08b363d15353/
changeset:   08b363d15353
user:        hpk42
date:        2012-10-16 13:48:00
summary:     use fixturemapper/fixtureinfo from Function objects
affected #:  3 files

diff -r 20d77f40c3abab579f2be1b310db8529b5234e8c -r 
08b363d153530c7821817f10a4b2dce355ee2724 _pytest/python.py
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -266,6 +266,18 @@
         return fspath, lineno, modpath
 
 class PyCollector(PyobjMixin, pytest.Collector):
+    def _fixturemapper():
+        def get(self):
+            try:
+                return self._fixturemapper_memo
+            except AttributeError:
+                self._fixturemapper_memo = FixtureMapper(self, funcargs=False)
+                return self._fixturemapper_memo
+        def set(self, val):
+            assert not hasattr(self, "_fixturemapper_memo")
+            self._fixturemapper_memo = val
+        return property(get, set)
+    _fixturemapper = _fixturemapper()
 
     def funcnamefilter(self, name):
         for prefix in self.config.getini("python_functions"):
@@ -309,7 +321,7 @@
         clscol = self.getparent(Class)
         cls = clscol and clscol.obj or None
         transfer_markers(funcobj, cls, module)
-        if not hasattr(self, "_fixturemapper"):
+        if not hasattr(self, "_fixturemapper_memo"):
             self._fixturemapper = FixtureMapper(self)
         fixtureinfo = self._fixturemapper.getfixtureinfo(funcobj, cls)
         metafunc = Metafunc(funcobj, fixtureinfo, self.config,
@@ -331,17 +343,21 @@
                                keywords={callspec.id:True})
 
 class FixtureMapper:
-    def __init__(self, node):
+    def __init__(self, node, funcargs=True):
         self.node = node
         self.fm = node.session._fixturemanager
         self._name2fixtureinfo = {}
+        self.hasfuncargs = funcargs
 
     def getfixtureinfo(self, func, cls):
         try:
             return self._name2fixtureinfo[func]
         except KeyError:
             pass
-        argnames = getfuncargnames(func, int(cls is not None))
+        if self.hasfuncargs:
+            argnames = getfuncargnames(func, int(cls is not None))
+        else:
+            argnames = ()
         usefixtures = getattr(func, "usefixtures", None)
         if usefixtures is not None:
             argnames = usefixtures.args + argnames
@@ -929,17 +945,9 @@
             for name, val in keywords.items():
                 setattr(self.markers, name, val)
 
-        # contstruct a list of all neccessary fixtures for this test function
-        try:
-            usefixtures = self.markers.usefixtures.args
-        except AttributeError:
-            usefixtures = ()
-        self.fixturenames = (self.session._fixturemanager.getdefaultfixtures() 
+
-                usefixtures + self._getfuncargnames())
-
-    def _getfuncargnames(self):
-        startindex = int(self.cls is not None)
-        return getfuncargnames(self.obj, startindex=startindex)
+        fixtureinfo = self.parent._fixturemapper.getfixtureinfo(self.obj,
+                                                                self.cls)
+        self.fixturenames = fixtureinfo.names_closure
 
     @property
     def function(self):


diff -r 20d77f40c3abab579f2be1b310db8529b5234e8c -r 
08b363d153530c7821817f10a4b2dce355ee2724 _pytest/unittest.py
--- a/_pytest/unittest.py
+++ b/_pytest/unittest.py
@@ -20,6 +20,7 @@
             return UnitTestCase(name, parent=collector)
 
 class UnitTestCase(pytest.Class):
+
     def collect(self):
         self.session._fixturemanager._parsefactories(self.obj, self.nodeid,
                 unittest=True)
@@ -52,9 +53,6 @@
 class TestCaseFunction(pytest.Function):
     _excinfo = None
 
-    def _getfuncargnames(self):
-        return ()
-
     def setup(self):
         self._testcase = self.parent.obj(self.name)
         self._obj = getattr(self._testcase, self.name)


diff -r 20d77f40c3abab579f2be1b310db8529b5234e8c -r 
08b363d153530c7821817f10a4b2dce355ee2724 testing/test_python.py
--- a/testing/test_python.py
+++ b/testing/test_python.py
@@ -280,6 +280,7 @@
         config = testdir.parseconfigure()
         session = testdir.Session(config)
         session._fixturemanager = FixtureManager(session)
+        session._fixturemapper = funcargs.FixtureMapper(session, 
funcargs=False)
         def func1():
             pass
         def func2():
@@ -579,6 +580,7 @@
                 pass
         """)
         funcargs.fillfixtures(item)
+        del item.funcargs["request"]
         assert len(item.funcargs) == 2
         assert item.funcargs['some'] == "test_func"
         assert item.funcargs['other'] == 42
@@ -685,7 +687,9 @@
         val2 = req.getfuncargvalue("other")  # see about caching
         assert val2 == 2
         pytest._fillfuncargs(item)
-        assert item.funcargs == {'something': 1}
+        assert item.funcargs["something"] == 1
+        assert len(item.funcargs) == 2
+        assert "request" in item.funcargs
         #assert item.funcargs == {'something': 1, "other": 2}
 
     def test_request_addfinalizer(self, testdir):



https://bitbucket.org/hpk42/pytest/changeset/4bdfe68104d5/
changeset:   4bdfe68104d5
user:        hpk42
date:        2012-10-16 13:48:00
summary:     use FixtureInfo from FixtureRequest
affected #:  1 file

diff -r 08b363d153530c7821817f10a4b2dce355ee2724 -r 
4bdfe68104d5ec3a93670bf9f1e6b0702cd3a8c8 _pytest/python.py
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -372,6 +372,16 @@
         self.names_closure = names_closure
         self.name2fixturedeflist = name2fixturedeflist
 
+    def getname2fixturedeflist_copy(self):
+        d = {}
+        for name, val in self.name2fixturedeflist.items():
+            try:
+                val = list(val)
+            except TypeError:
+                pass
+            d[name] = val
+        return d
+
 def transfer_markers(funcobj, cls, mod):
     # XXX this should rather be code in the mark plugin or the mark
     # plugin should merge with the python plugin.
@@ -580,6 +590,9 @@
         try:
             request = function._request
         except AttributeError:
+            # the special jstests class with a custom .obj
+            fi = FixtureMapper(function).getfixtureinfo(function.obj, None)
+            function._fixtureinfo = fi
             request = function._request = FixtureRequest(function)
         request._fillfixtures()
 
@@ -922,6 +935,18 @@
         super(Function, self).__init__(name, parent, config=config,
                                        session=session)
         self._args = args
+        if callobj is not _dummy:
+            self.obj = callobj
+
+        for name, val in (py.builtin._getfuncdict(self.obj) or {}).items():
+            setattr(self.markers, name, val)
+        if keywords:
+            for name, val in keywords.items():
+                setattr(self.markers, name, val)
+
+        fi = self.parent._fixturemapper.getfixtureinfo(self.obj, self.cls)
+        self._fixtureinfo = fi
+        self.fixturenames = fi.names_closure
         if self._isyieldedfunction():
             assert not callspec, (
                 "yielded functions (deprecated) cannot have funcargs")
@@ -936,18 +961,6 @@
                 self.funcargs = {}
             self._request = req = FixtureRequest(self)
             #req._discoverfactories()
-        if callobj is not _dummy:
-            self.obj = callobj
-
-        for name, val in (py.builtin._getfuncdict(self.obj) or {}).items():
-            setattr(self.markers, name, val)
-        if keywords:
-            for name, val in keywords.items():
-                setattr(self.markers, name, val)
-
-        fixtureinfo = self.parent._fixturemapper.getfixtureinfo(self.obj,
-                                                                self.cls)
-        self.fixturenames = fixtureinfo.names_closure
 
     @property
     def function(self):
@@ -1041,13 +1054,11 @@
         self.scope = "function"
         self.getparent = pyfuncitem.getparent
         self._funcargs  = self._pyfuncitem.funcargs.copy()
-        self._arg2fixturedeflist = {}
+        self._fixtureinfo = fi = pyfuncitem._fixtureinfo
+        self._arg2fixturedeflist = fi.getname2fixturedeflist_copy()
+        self.fixturenames = self._fixtureinfo.names_closure
         self._fixturemanager = pyfuncitem.session._fixturemanager
         self._parentid = pyfuncitem.parent.nodeid
-        self.fixturenames, self._arg2fixturedeflist_ = \
-            self._fixturemanager.getfixtureclosure(
-                getfuncargnames(self.function), # XXX _pyfuncitem...
-                pyfuncitem.parent)
         self._fixturestack = []
 
     @property



https://bitbucket.org/hpk42/pytest/changeset/648c93461248/
changeset:   648c93461248
user:        hpk42
date:        2012-10-16 13:59:12
summary:     make factorydeflist immutable by using an index
affected #:  3 files

diff -r 4bdfe68104d5ec3a93670bf9f1e6b0702cd3a8c8 -r 
648c9346124894406687104a05891d6b68eb844b IMPL.txt
--- a/IMPL.txt
+++ b/IMPL.txt
@@ -36,5 +36,5 @@
 
 - names_initial: list of initial fixture names (see above)
 - names_closure: closure of all fixture names
-- name2fixturedeflist: for creating the value
+- name2fixturedefs: for creating the value
 


diff -r 4bdfe68104d5ec3a93670bf9f1e6b0702cd3a8c8 -r 
648c9346124894406687104a05891d6b68eb844b _pytest/python.py
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -361,26 +361,16 @@
         usefixtures = getattr(func, "usefixtures", None)
         if usefixtures is not None:
             argnames = usefixtures.args + argnames
-        names_closure, arg2fixturedeflist = self.fm.getfixtureclosure(
+        names_closure, arg2fixturedefs = self.fm.getfixtureclosure(
                 argnames, self.node)
-        fixtureinfo = FuncFixtureInfo(names_closure, arg2fixturedeflist)
+        fixtureinfo = FuncFixtureInfo(names_closure, arg2fixturedefs)
         self._name2fixtureinfo[func] = fixtureinfo
         return fixtureinfo
 
 class FuncFixtureInfo:
-    def __init__(self, names_closure, name2fixturedeflist):
+    def __init__(self, names_closure, name2fixturedefs):
         self.names_closure = names_closure
-        self.name2fixturedeflist = name2fixturedeflist
-
-    def getname2fixturedeflist_copy(self):
-        d = {}
-        for name, val in self.name2fixturedeflist.items():
-            try:
-                val = list(val)
-            except TypeError:
-                pass
-            d[name] = val
-        return d
+        self.name2fixturedefs = name2fixturedefs
 
 def transfer_markers(funcobj, cls, mod):
     # XXX this should rather be code in the mark plugin or the mark
@@ -675,7 +665,7 @@
         self.module = module
         self.function = function
         self.fixturenames = fixtureinfo.names_closure
-        self._arg2fixturedeflist = fixtureinfo.name2fixturedeflist
+        self._arg2fixturedefs = fixtureinfo.name2fixturedefs
         self.cls = cls
         self.module = module
         self._calls = []
@@ -804,12 +794,12 @@
     fm = session._fixturemanager
 
     available = []
-    for argname in fm.arg2fixturedeflist:
-        fixturedeflist = fm.getfixturedeflist(argname, nodeid)
-        assert fixturedeflist is not None
-        if not fixturedeflist:
+    for argname in fm.arg2fixturedefs:
+        fixturedefs = fm.getfixturedefs(argname, nodeid)
+        assert fixturedefs is not None
+        if not fixturedefs:
             continue
-        fixturedef = fixturedeflist[-1]
+        fixturedef = fixturedefs[-1]
         loc = getlocation(fixturedef.func, curdir)
         available.append((len(fixturedef.baseid),
                           curdir.bestrelpath(loc),
@@ -1055,7 +1045,8 @@
         self.getparent = pyfuncitem.getparent
         self._funcargs  = self._pyfuncitem.funcargs.copy()
         self._fixtureinfo = fi = pyfuncitem._fixtureinfo
-        self._arg2fixturedeflist = fi.getname2fixturedeflist_copy()
+        self._arg2fixturedefs = fi.name2fixturedefs
+        self._arg2index = {}
         self.fixturenames = self._fixtureinfo.names_closure
         self._fixturemanager = pyfuncitem.session._fixturemanager
         self._parentid = pyfuncitem.parent.nodeid
@@ -1066,15 +1057,20 @@
         """ underlying collection node (depends on current request scope)"""
         return self._getscopeitem(self.scope)
 
-    def _getfixturedeflist(self, argname):
-        fixturedeflist = self._arg2fixturedeflist.get(argname, None)
-        if fixturedeflist is None:
-            fixturedeflist = self._fixturemanager.getfixturedeflist(
+    def _getnextfixturedef(self, argname):
+        fixturedefs = self._arg2fixturedefs.get(argname, None)
+        if fixturedefs is None:
+            # we arrive here because of a getfuncargvalue(argname) usage which
+            # was naturally not knowable at parsing/collection time
+            fixturedefs = self._fixturemanager.getfixturedefs(
                             argname, self._parentid)
-            self._arg2fixturedeflist[argname] = fixturedeflist
-        if not fixturedeflist:
+            self._arg2fixturedefs[argname] = fixturedefs
+        # fixturedefs is immutable so we maintain a decreasing index
+        index = self._arg2index.get(argname, 0) - 1
+        if fixturedefs is None or (-index > len(fixturedefs)):
             raise FixtureLookupError(argname, self)
-        return fixturedeflist
+        self._arg2index[argname] = index
+        return fixturedefs[index]
 
     @property
     def config(self):
@@ -1212,12 +1208,11 @@
         except KeyError:
             pass
         try:
-            fixturedeflist = self._getfixturedeflist(argname)
+            fixturedef = self._getnextfixturedef(argname)
         except FixtureLookupError:
             if argname == "request":
                 return self
             raise
-        fixturedef = fixturedeflist.pop()
         self._fixturestack.append(fixturedef)
         try:
             result = self._getfuncargvalue(fixturedef)
@@ -1353,7 +1348,7 @@
             fm = self.request._fixturemanager
             nodeid = self.request._parentid
             available = []
-            for name, fixturedef in fm.arg2fixturedeflist.items():
+            for name, fixturedef in fm.arg2fixturedefs.items():
                 faclist = list(fm._matchfactories(fixturedef, 
self.request._parentid))
                 if faclist:
                     available.append(name)
@@ -1388,7 +1383,7 @@
     def __init__(self, session):
         self.session = session
         self.config = session.config
-        self.arg2fixturedeflist = {}
+        self.arg2fixturedefs = {}
         self._seenplugins = set()
         self._holderobjseen = set()
         self._arg2finish = {}
@@ -1427,7 +1422,7 @@
             # make sure the self._autofixtures list is sorted
             # by scope, scopenum 0 is session
             self._autofixtures.sort(
-                key=lambda x: self.arg2fixturedeflist[x][-1].scopenum)
+                key=lambda x: self.arg2fixturedefs[x][-1].scopenum)
             defaultfixtures = defaultfixtures + tuple(self._autofixtures)
             self._defaultfixtures = defaultfixtures
             return defaultfixtures
@@ -1435,7 +1430,7 @@
     def getfixtureclosure(self, fixturenames, parentnode):
         # collect the closure of all fixtures , starting with the given
         # fixturenames as the initial set.  As we have to visit all
-        # factory definitions anyway, we also return a arg2fixturedeflist
+        # factory definitions anyway, we also return a arg2fixturedefs
         # mapping so that the caller can reuse it and does not have
         # to re-discover fixturedefs again for each fixturename
         # (discovering matching fixtures for a given name/node is expensive)
@@ -1447,23 +1442,23 @@
                 if arg not in fixturenames_closure:
                     fixturenames_closure.append(arg)
         merge(fixturenames)
-        arg2fixturedeflist = {}
+        arg2fixturedefs = {}
         lastlen = -1
         while lastlen != len(fixturenames_closure):
             lastlen = len(fixturenames_closure)
             for argname in fixturenames_closure:
-                if argname in arg2fixturedeflist:
+                if argname in arg2fixturedefs:
                     continue
-                fixturedeflist = self.getfixturedeflist(argname, parentid)
-                arg2fixturedeflist[argname] = fixturedeflist
-                if fixturedeflist is not None:
-                    for fixturedef in fixturedeflist:
+                fixturedefs = self.getfixturedefs(argname, parentid)
+                arg2fixturedefs[argname] = fixturedefs
+                if fixturedefs is not None:
+                    for fixturedef in fixturedefs:
                         merge(fixturedef.fixturenames)
-        return fixturenames_closure, arg2fixturedeflist
+        return fixturenames_closure, arg2fixturedefs
 
     def pytest_generate_tests(self, metafunc):
         for argname in metafunc.fixturenames:
-            faclist = metafunc._arg2fixturedeflist[argname]
+            faclist = metafunc._arg2fixturedefs[argname]
             if faclist is None:
                 continue # will raise FixtureLookupError at setup time
             for fixturedef in faclist:
@@ -1527,7 +1522,7 @@
             fixturedef = FixtureDef(self, nodeid, name, obj,
                                     marker.scope, marker.params,
                                     unittest=unittest)
-            faclist = self.arg2fixturedeflist.setdefault(name, [])
+            faclist = self.arg2fixturedefs.setdefault(name, [])
             faclist.append(fixturedef)
             if marker.autouse:
                 self._autofixtures.append(name)
@@ -1536,16 +1531,16 @@
                 except AttributeError:
                     pass
 
-    def getfixturedeflist(self, argname, nodeid):
+    def getfixturedefs(self, argname, nodeid):
         try:
-            fixturedeflist = self.arg2fixturedeflist[argname]
+            fixturedefs = self.arg2fixturedefs[argname]
         except KeyError:
             return None
         else:
-            return list(self._matchfactories(fixturedeflist, nodeid))
+            return tuple(self._matchfactories(fixturedefs, nodeid))
 
-    def _matchfactories(self, fixturedeflist, nodeid):
-        for fixturedef in fixturedeflist:
+    def _matchfactories(self, fixturedefs, nodeid):
+        for fixturedef in fixturedefs:
             if nodeid.startswith(fixturedef.baseid):
                 yield fixturedef
 


diff -r 4bdfe68104d5ec3a93670bf9f1e6b0702cd3a8c8 -r 
648c9346124894406687104a05891d6b68eb844b testing/test_python.py
--- a/testing/test_python.py
+++ b/testing/test_python.py
@@ -640,7 +640,7 @@
         assert req.cls.__name__ == "TestB"
         assert req.instance.__class__ == req.cls
 
-    def XXXtest_request_contains_funcarg_arg2fixturedeflist(self, testdir):
+    def XXXtest_request_contains_funcarg_arg2fixturedefs(self, testdir):
         modcol = testdir.getmodulecol("""
             def pytest_funcarg__something(request):
                 pass
@@ -650,9 +650,9 @@
         """)
         item1, = testdir.genitems([modcol])
         assert item1.name == "test_method"
-        arg2fixturedeflist = funcargs.FixtureRequest(item1)._arg2fixturedeflist
-        assert len(arg2fixturedeflist) == 1
-        assert arg2fixturedeflist[0].__name__ == "pytest_funcarg__something"
+        arg2fixturedefs = funcargs.FixtureRequest(item1)._arg2fixturedefs
+        assert len(arg2fixturedefs) == 1
+        assert arg2fixturedefs[0].__name__ == "pytest_funcarg__something"
 
     def test_getfuncargvalue_recursive(self, testdir):
         testdir.makeconftest("""
@@ -918,7 +918,7 @@
         # on the funcarg level, so we don't need a full blown
         # initiliazation
         class FixtureInfo:
-            name2fixturedeflist = None
+            name2fixturedefs = None
             def __init__(self, names):
                 self.names_closure = names
         names = funcargs.getfuncargnames(func)
@@ -1974,7 +1974,7 @@
         testdir.makepyfile("""
             def test_hello(item, fm):
                 for name in ("fm", "hello", "item"):
-                    faclist = fm.getfixturedeflist(name, item.nodeid)
+                    faclist = fm.getfixturedefs(name, item.nodeid)
                     assert len(faclist) == 1
                     fac = faclist[0]
                     assert fac.func.__name__ == "pytest_funcarg__" + name
@@ -1990,7 +1990,7 @@
                 def pytest_funcarg__hello(self, request):
                     return "class"
                 def test_hello(self, item, fm):
-                    faclist = fm.getfixturedeflist("hello", item.nodeid)
+                    faclist = fm.getfixturedefs("hello", item.nodeid)
                     print (faclist)
                     assert len(faclist) == 3
                     assert faclist[0].func(item._request) == "conftest"



https://bitbucket.org/hpk42/pytest/changeset/eb495bd8132e/
changeset:   eb495bd8132e
user:        hpk42
date:        2012-10-16 14:19:38
summary:     strike another use of getfuncargnames() and rename 
FixtureDef.fixturenames to "argnames" because it's really just the fixture 
function arguments
affected #:  1 file

diff -r 648c9346124894406687104a05891d6b68eb844b -r 
eb495bd8132e038f69f45ca775d60cdd1a7af818 _pytest/python.py
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -142,25 +142,9 @@
         if pyfuncitem._isyieldedfunction():
             testfunction(*pyfuncitem._args)
         else:
-            try:
-                fixturenames = pyfuncitem.fixturenames
-            except AttributeError:
-                funcargs = pyfuncitem.funcargs
-            else:
-                funcargs = {}
-                for name in fixturenames:
-                    funcargs[name] = pyfuncitem.funcargs[name]
-            testfunction(**funcargs)
-
-def pytest_pyfunc_call(__multicall__, pyfuncitem):
-    if not __multicall__.execute():
-        testfunction = pyfuncitem.obj
-        if pyfuncitem._isyieldedfunction():
-            testfunction(*pyfuncitem._args)
-        else:
             funcargs = pyfuncitem.funcargs
             testargs = {}
-            for arg in getfuncargnames(testfunction):
+            for arg in pyfuncitem._fixtureinfo.argnames:
                 testargs[arg] = funcargs[arg]
             testfunction(**testargs)
 
@@ -359,16 +343,19 @@
         else:
             argnames = ()
         usefixtures = getattr(func, "usefixtures", None)
+        initialnames = argnames
         if usefixtures is not None:
-            argnames = usefixtures.args + argnames
+            initialnames = usefixtures.args + initialnames
         names_closure, arg2fixturedefs = self.fm.getfixtureclosure(
-                argnames, self.node)
-        fixtureinfo = FuncFixtureInfo(names_closure, arg2fixturedefs)
+                initialnames, self.node)
+        fixtureinfo = FuncFixtureInfo(argnames, names_closure,
+                                      arg2fixturedefs)
         self._name2fixtureinfo[func] = fixtureinfo
         return fixtureinfo
 
 class FuncFixtureInfo:
-    def __init__(self, names_closure, name2fixturedefs):
+    def __init__(self, argnames, names_closure, name2fixturedefs):
+        self.argnames = argnames
         self.names_closure = names_closure
         self.name2fixturedefs = name2fixturedefs
 
@@ -1265,7 +1252,7 @@
         # (XXX analyse exact finalizing mechanics / cleanup)
         self.session._setupstate.addfinalizer(fixturedef.finish, self.node)
         self._fixturemanager.addargfinalizer(fixturedef.finish, argname)
-        for subargname in fixturedef.fixturenames: # XXX all deps?
+        for subargname in fixturedef.argnames: # XXX all deps?
             self._fixturemanager.addargfinalizer(fixturedef.finish, subargname)
         mp.setattr(self, "addfinalizer", fixturedef.addfinalizer)
         # finally perform the fixture call
@@ -1453,7 +1440,7 @@
                 arg2fixturedefs[argname] = fixturedefs
                 if fixturedefs is not None:
                     for fixturedef in fixturedefs:
-                        merge(fixturedef.fixturenames)
+                        merge(fixturedef.argnames)
         return fixturenames_closure, arg2fixturedefs
 
     def pytest_generate_tests(self, metafunc):
@@ -1567,7 +1554,7 @@
         self.scopenum = scopes.index(scope or "function")
         self.params = params
         startindex = unittest and 1 or None
-        self.fixturenames = getfuncargnames(func, startindex=startindex)
+        self.argnames = getfuncargnames(func, startindex=startindex)
         self.unittest = unittest
         self.active = False
         self._finalizer = []
@@ -1587,7 +1574,7 @@
 
     def execute(self, request):
         kwargs = {}
-        for newname in self.fixturenames:
+        for newname in self.argnames:
             kwargs[newname] = request.getfuncargvalue(newname)
         if self.unittest:
             result = self.func(request.instance, **kwargs)



https://bitbucket.org/hpk42/pytest/changeset/5f7bfa93ff2e/
changeset:   5f7bfa93ff2e
user:        hpk42
date:        2012-10-16 16:13:12
summary:     refine parsefactories interface, fix two_classes test originally 
reported by Alex Okrushko, also add a few more tests to make sure 
autouse-fixtures are properly distinguished
affected #:  5 files

diff -r eb495bd8132e038f69f45ca775d60cdd1a7af818 -r 
5f7bfa93ff2eb7a7dded4c2096704264e692311d _pytest/__init__.py
--- a/_pytest/__init__.py
+++ b/_pytest/__init__.py
@@ -1,2 +1,2 @@
 #
-__version__ = '2.3.0.dev23'
+__version__ = '2.3.0.dev24'


diff -r eb495bd8132e038f69f45ca775d60cdd1a7af818 -r 
5f7bfa93ff2eb7a7dded4c2096704264e692311d _pytest/python.py
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -83,7 +83,7 @@
     group.addoption('--fixtures', '--fixtures',
                action="store_true", dest="showfixtures", default=False,
                help="show available fixtures, sorted by plugin appearance")
-    parser.addini("usefixtures", type="args", default=(),
+    parser.addini("usefixtures", type="args", default=[],
         help="list of default fixtures to be used with this project")
     parser.addini("python_files", type="args",
         default=('test_*.py', '*_test.py'),
@@ -379,7 +379,7 @@
         return self._memoizedcall('_obj', self._importtestmodule)
 
     def collect(self):
-        self.session._fixturemanager._parsefactories(self.obj, self.nodeid)
+        self.session._fixturemanager.parsefactories(self)
         return super(Module, self).collect()
 
     def _importtestmodule(self):
@@ -452,7 +452,7 @@
         return obj
 
     def collect(self):
-        self.session._fixturemanager._parsefactories(self.obj, self.nodeid)
+        self.session._fixturemanager.parsefactories(self)
         return super(Instance, self).collect()
 
     def newinstance(self):
@@ -781,7 +781,7 @@
     fm = session._fixturemanager
 
     available = []
-    for argname in fm.arg2fixturedefs:
+    for argname in fm._arg2fixturedefs:
         fixturedefs = fm.getfixturedefs(argname, nodeid)
         assert fixturedefs is not None
         if not fixturedefs:
@@ -1335,7 +1335,7 @@
             fm = self.request._fixturemanager
             nodeid = self.request._parentid
             available = []
-            for name, fixturedef in fm.arg2fixturedefs.items():
+            for name, fixturedef in fm._arg2fixturedefs.items():
                 faclist = list(fm._matchfactories(fixturedef, 
self.request._parentid))
                 if faclist:
                     available.append(name)
@@ -1370,11 +1370,11 @@
     def __init__(self, session):
         self.session = session
         self.config = session.config
-        self.arg2fixturedefs = {}
+        self._arg2fixturedefs = {}
         self._seenplugins = set()
         self._holderobjseen = set()
         self._arg2finish = {}
-        self._autofixtures = []
+        self._nodeid_and_autousenames = [("", 
self.config.getini("usefixtures"))]
         session.config.pluginmanager.register(self, "funcmanage")
 
     ### XXX this hook should be called for historic events like 
pytest_configure
@@ -1391,7 +1391,7 @@
         else:
             if p.basename.startswith("conftest.py"):
                 nodeid = p.dirpath().relto(self.session.fspath)
-        self._parsefactories(plugin, nodeid)
+        self.parsefactories(plugin, nodeid)
         self._seenplugins.add(plugin)
 
     @pytest.mark.tryfirst
@@ -1400,19 +1400,21 @@
         for plugin in plugins:
             self.pytest_plugin_registered(plugin)
 
-    def getdefaultfixtures(self):
-        """ return a tuple of default fixture names (XXX for the given file 
path). """
-        try:
-            return self._defaultfixtures
-        except AttributeError:
-            defaultfixtures = tuple(self.config.getini("usefixtures"))
-            # make sure the self._autofixtures list is sorted
-            # by scope, scopenum 0 is session
-            self._autofixtures.sort(
-                key=lambda x: self.arg2fixturedefs[x][-1].scopenum)
-            defaultfixtures = defaultfixtures + tuple(self._autofixtures)
-            self._defaultfixtures = defaultfixtures
-            return defaultfixtures
+    def _getautousenames(self, nodeid):
+        """ return a tuple of fixture names to be used. """
+        autousenames = []
+        for baseid, basenames in self._nodeid_and_autousenames:
+            if nodeid.startswith(baseid):
+                if baseid:
+                    i = len(baseid) + 1
+                    nextchar = nodeid[i:i+1]
+                    if nextchar and nextchar not in ":/":
+                        continue
+                autousenames.extend(basenames)
+        # make sure autousenames are sorted by scope, scopenum 0 is session
+        autousenames.sort(
+            key=lambda x: self._arg2fixturedefs[x][-1].scopenum)
+        return autousenames
 
     def getfixtureclosure(self, fixturenames, parentnode):
         # collect the closure of all fixtures , starting with the given
@@ -1423,7 +1425,7 @@
         # (discovering matching fixtures for a given name/node is expensive)
 
         parentid = parentnode.nodeid
-        fixturenames_closure = list(self.getdefaultfixtures())
+        fixturenames_closure = self._getautousenames(parentid)
         def merge(otherlist):
             for arg in otherlist:
                 if arg not in fixturenames_closure:
@@ -1484,10 +1486,16 @@
                 for fin in l:
                     fin()
 
-    def _parsefactories(self, holderobj, nodeid, unittest=False):
+    def parsefactories(self, node_or_obj, nodeid=None, unittest=False):
+        if nodeid is not None:
+            holderobj = node_or_obj
+        else:
+            holderobj = node_or_obj.obj
+            nodeid = node_or_obj.nodeid
         if holderobj in self._holderobjseen:
             return
         self._holderobjseen.add(holderobj)
+        autousenames = []
         for name in dir(holderobj):
             obj = getattr(holderobj, name)
             if not callable(obj):
@@ -1509,18 +1517,16 @@
             fixturedef = FixtureDef(self, nodeid, name, obj,
                                     marker.scope, marker.params,
                                     unittest=unittest)
-            faclist = self.arg2fixturedefs.setdefault(name, [])
+            faclist = self._arg2fixturedefs.setdefault(name, [])
             faclist.append(fixturedef)
             if marker.autouse:
-                self._autofixtures.append(name)
-                try:
-                    del self._defaultfixtures
-                except AttributeError:
-                    pass
+                autousenames.append(name)
+        if autousenames:
+            self._nodeid_and_autousenames.append((nodeid, autousenames))
 
     def getfixturedefs(self, argname, nodeid):
         try:
-            fixturedefs = self.arg2fixturedefs[argname]
+            fixturedefs = self._arg2fixturedefs[argname]
         except KeyError:
             return None
         else:


diff -r eb495bd8132e038f69f45ca775d60cdd1a7af818 -r 
5f7bfa93ff2eb7a7dded4c2096704264e692311d _pytest/unittest.py
--- a/_pytest/unittest.py
+++ b/_pytest/unittest.py
@@ -22,8 +22,7 @@
 class UnitTestCase(pytest.Class):
 
     def collect(self):
-        self.session._fixturemanager._parsefactories(self.obj, self.nodeid,
-                unittest=True)
+        self.session._fixturemanager.parsefactories(self, unittest=True)
         loader = py.std.unittest.TestLoader()
         module = self.getparent(pytest.Module).obj
         cls = self.obj


diff -r eb495bd8132e038f69f45ca775d60cdd1a7af818 -r 
5f7bfa93ff2eb7a7dded4c2096704264e692311d setup.py
--- a/setup.py
+++ b/setup.py
@@ -24,7 +24,7 @@
         name='pytest',
         description='py.test: simple powerful testing with Python',
         long_description = long_description,
-        version='2.3.0.dev23',
+        version='2.3.0.dev24',
         url='http://pytest.org',
         license='MIT license',
         platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],


diff -r eb495bd8132e038f69f45ca775d60cdd1a7af818 -r 
5f7bfa93ff2eb7a7dded4c2096704264e692311d testing/test_python.py
--- a/testing/test_python.py
+++ b/testing/test_python.py
@@ -2026,14 +2026,14 @@
     def test_parsefactories_conftest(self, testdir):
         testdir.makepyfile("""
             def test_check_setup(item, fm):
-                assert len(fm._autofixtures) == 2
-                assert "perfunction2" in fm._autofixtures
-                assert "perfunction" in fm._autofixtures
+                autousenames = fm._getautousenames(item.nodeid)
+                assert len(autousenames) == 2
+                assert "perfunction2" in autousenames
+                assert "perfunction" in autousenames
         """)
         reprec = testdir.inline_run("-s")
         reprec.assertoutcome(passed=1)
 
-    @pytest.mark.xfail
     def test_two_classes_separated_autouse(self, testdir):
         testdir.makepyfile("""
             import pytest
@@ -2113,6 +2113,45 @@
         reprec = testdir.inline_run("-s")
         reprec.assertoutcome(failed=0, passed=0)
 
+    def test_autouse_in_conftests(self, testdir):
+        a = testdir.mkdir("a")
+        b = testdir.mkdir("a1")
+        conftest = testdir.makeconftest("""
+            import pytest
+            @pytest.fixture(autouse=True)
+            def hello():
+                xxx
+        """)
+        conftest.move(a.join(conftest.basename))
+        a.join("test_something.py").write("def test_func(): pass")
+        b.join("test_otherthing.py").write("def test_func(): pass")
+        result = testdir.runpytest()
+        result.stdout.fnmatch_lines("""
+            *1 passed*1 error*
+        """)
+
+    def test_autouse_in_module_and_two_classes(self, testdir):
+        testdir.makepyfile("""
+            import pytest
+            l = []
+            @pytest.fixture(autouse=True)
+            def append1():
+                l.append("module")
+            def test_x():
+                assert l == ["module"]
+
+            class TestA:
+                @pytest.fixture(autouse=True)
+                def append2(self):
+                    l.append("A")
+                def test_hello(self):
+                    assert l == ["module", "module", "A"], l
+            class TestA2:
+                def test_world(self):
+                    assert l == ["module", "module", "A", "module"], l
+        """)
+        reprec = testdir.inline_run()
+        reprec.assertoutcome(passed=3)
 
 class TestSetupManagement:
     def test_funcarg_and_setup(self, testdir):
@@ -2220,7 +2259,7 @@
                 def test_2(self):
                     pass
         """)
-        reprec = testdir.inline_run("-v",)
+        reprec = testdir.inline_run("-v","-s")
         reprec.assertoutcome(passed=8)
         config = reprec.getcalls("pytest_unconfigure")[0].config
         l = config._conftest.getconftestmodules(p)[0].l

Repository URL: https://bitbucket.org/hpk42/pytest/

--

This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
_______________________________________________
py-svn mailing list
py-svn@codespeak.net
http://codespeak.net/mailman/listinfo/py-svn

Reply via email to