Commits

holger krekel  committed b2aa891

simplify/integrate fixturemapper into FixtureManager
also fix jstests test failures

  • Participants
  • Parent commits c5d7910

Comments (0)

Files changed (5)

File _pytest/__init__.py

 #
-__version__ = '2.3.0.dev24'
+__version__ = '2.3.0.dev25'

File _pytest/python.py

         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"):
         clscol = self.getparent(Class)
         cls = clscol and clscol.obj or None
         transfer_markers(funcobj, cls, module)
-        if not hasattr(self, "_fixturemapper_memo"):
-            self._fixturemapper = FixtureMapper(self)
-        fixtureinfo = self._fixturemapper.getfixtureinfo(funcobj, cls)
+        fm = self.session._fixturemanager
+        fixtureinfo = fm.getfixtureinfo(self, funcobj, cls)
         metafunc = Metafunc(funcobj, fixtureinfo, self.config,
                             cls=cls, module=module)
         gentesthook = self.config.hook.pytest_generate_tests
                                callspec=callspec, callobj=funcobj,
                                keywords={callspec.id:True})
 
-class FixtureMapper:
-    """
-    pytest fixtures definitions and information is stored and managed
-    from this class.
-
-    During collection fm.parsefactories() is called multiple times to parse
-    fixture function definitions into FixtureDef objects and internal
-    data structures.
-
-    During collection of test functions, metafunc-mechanics instantiate
-    a FuncFixtureInfo object which is cached in a FixtureMapper instance
-    which itself lives on the parent collector.  This FuncFixtureInfo object
-    is later retrieved by Function nodes which themselves offer a fixturenames
-    attribute.
-
-    The FuncFixtureInfo object holds information about fixtures and FixtureDefs
-    relevant for a particular function.  An initial list of fixtures is
-    assembled like this:
-
-    - ini-defined usefixtures
-    - autouse-marked fixtures along the collection chain up from the function
-    - usefixtures markers at module/class/function level
-    - test function funcargs
-
-    Subsequently the funcfixtureinfo.fixturenames attribute is computed
-    as the closure of the fixtures needed to setup the initial fixtures,
-    i. e. fixtures needed by fixture functions themselves are appended
-    to the fixturenames list.
-
-    Upon the test-setup phases all fixturenames are instantiated, retrieved
-    by a lookup on a FixtureMapper().
-    """
-
-    def __init__(self, node, funcargs=True):
-        self.node = node
-        self._name2fixtureinfo = {}
-        self.hasfuncargs = funcargs
-
-    def getfixtureinfo(self, func, cls):
-        try:
-            return self._name2fixtureinfo[func]
-        except KeyError:
-            pass
-        if self.hasfuncargs:
-            argnames = getfuncargnames(func, int(cls is not None))
-        else:
-            argnames = ()
-        usefixtures = getattr(func, "usefixtures", None)
-        initialnames = argnames
-        if usefixtures is not None:
-            initialnames = usefixtures.args + initialnames
-        fm = self.node.session._fixturemanager
-        names_closure, arg2fixturedefs = fm.getfixtureclosure(initialnames,
-                                                              self.node)
-        fixtureinfo = FuncFixtureInfo(argnames, names_closure,
-                                      arg2fixturedefs)
-        self._name2fixtureinfo[func] = fixtureinfo
-        return fixtureinfo
 
 class FuncFixtureInfo:
     def __init__(self, argnames, names_closure, name2fixturedefs):
         try:
             request = function._request
         except AttributeError:
-            # the special jstests class with a custom .obj
-            fi = FixtureMapper(function).getfixtureinfo(function.obj, None)
+            # XXX this special code path is only expected to execute
+            # with the oejskit plugin.  It uses classes with funcargs
+            # and we thus have to work a bit to allow this.
+            fm = function.session._fixturemanager
+            fi = fm.getfixtureinfo(function.parent, function.obj, None)
             function._fixtureinfo = fi
             request = function._request = FixtureRequest(function)
-        request._fillfixtures()
+            request._fillfixtures()
+            # prune out funcargs for jstests
+            newfuncargs = {}
+            for name in fi.argnames:
+                newfuncargs[name] = function.funcargs[name]
+            function.funcargs = newfuncargs
+        else:
+            request._fillfixtures()
+
 
 _notexists = object()
 
             for name, val in keywords.items():
                 setattr(self.markers, name, val)
 
-        fi = self.parent._fixturemapper.getfixtureinfo(self.obj, self.cls)
-        self._fixtureinfo = fi
+        fm = self.session._fixturemanager
+        self._fixtureinfo = fi = fm.getfixtureinfo(self.parent,
+                                                   self.obj, self.cls)
         self.fixturenames = fi.names_closure
         if self._isyieldedfunction():
             assert not callspec, (
         tw.line("%s:%d" % (self.filename, self.firstlineno+1))
 
 class FixtureManager:
+    """
+    pytest fixtures definitions and information is stored and managed
+    from this class.
+
+    During collection fm.parsefactories() is called multiple times to parse
+    fixture function definitions into FixtureDef objects and internal
+    data structures.
+
+    During collection of test functions, metafunc-mechanics instantiate
+    a FuncFixtureInfo object which is cached per node/func-name.
+    This FuncFixtureInfo object is later retrieved by Function nodes
+    which themselves offer a fixturenames attribute.
+
+    The FuncFixtureInfo object holds information about fixtures and FixtureDefs
+    relevant for a particular function.  An initial list of fixtures is
+    assembled like this:
+
+    - ini-defined usefixtures
+    - autouse-marked fixtures along the collection chain up from the function
+    - usefixtures markers at module/class/function level
+    - test function funcargs
+
+    Subsequently the funcfixtureinfo.fixturenames attribute is computed
+    as the closure of the fixtures needed to setup the initial fixtures,
+    i. e. fixtures needed by fixture functions themselves are appended
+    to the fixturenames list.
+
+    Upon the test-setup phases all fixturenames are instantiated, retrieved
+    by a lookup of their FuncFixtureInfo.
+    """
+
     _argprefix = "pytest_funcarg__"
     FixtureLookupError = FixtureLookupError
     FixtureLookupErrorRepr = FixtureLookupErrorRepr
         self._nodeid_and_autousenames = [("", self.config.getini("usefixtures"))]
         session.config.pluginmanager.register(self, "funcmanage")
 
+        self._nodename2fixtureinfo = {}
+
+    def getfixtureinfo(self, node, func, cls):
+        key = (node, func.__name__)
+        try:
+            return self._nodename2fixtureinfo[key]
+        except KeyError:
+            pass
+        if not hasattr(node, "nofuncargs"):
+            if cls is not None:
+                startindex = 1
+            else:
+                startindex = None
+            argnames = getfuncargnames(func, startindex)
+        else:
+            argnames = ()
+        usefixtures = getattr(func, "usefixtures", None)
+        initialnames = argnames
+        if usefixtures is not None:
+            initialnames = usefixtures.args + initialnames
+        fm = node.session._fixturemanager
+        names_closure, arg2fixturedefs = fm.getfixtureclosure(initialnames,
+                                                              node)
+        fixtureinfo = FuncFixtureInfo(argnames, names_closure,
+                                      arg2fixturedefs)
+        self._nodename2fixtureinfo[key] = fixtureinfo
+        return fixtureinfo
+
     ### XXX this hook should be called for historic events like pytest_configure
     ### so that we don't have to do the below pytest_configure hook
     def pytest_plugin_registered(self, plugin):

File _pytest/unittest.py

             return UnitTestCase(name, parent=collector)
 
 class UnitTestCase(pytest.Class):
+    nofuncargs = True  # marker for fixturemanger.getfixtureinfo()
+                       # to declare that our children do not support funcargs
 
     def collect(self):
         self.session._fixturemanager.parsefactories(self, unittest=True)
         name='pytest',
         description='py.test: simple powerful testing with Python',
         long_description = long_description,
-        version='2.3.0.dev24',
+        version='2.3.0.dev25',
         url='http://pytest.org',
         license='MIT license',
         platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],

File testing/test_python.py

         config = testdir.parseconfigure()
         session = testdir.Session(config)
         session._fixturemanager = FixtureManager(session)
-        session._fixturemapper = funcargs.FixtureMapper(session, funcargs=False)
         def func1():
             pass
         def func2():
         "*2 passed*"
     ])
 
-def test_funcarg_non_pycollectobj(testdir): # rough jstests usage
-    testdir.makeconftest("""
-        import pytest
-        def pytest_pycollect_makeitem(collector, name, obj):
-            if name == "MyClass":
-                return MyCollector(name, parent=collector)
-        class MyCollector(pytest.Collector):
-            def reportinfo(self):
-                return self.fspath, 3, "xyz"
-    """)
-    modcol = testdir.getmodulecol("""
-        def pytest_funcarg__arg1(request):
-            return 42
-        class MyClass:
-            pass
-    """)
-    # this hook finds funcarg factories
-    rep = modcol.ihook.pytest_make_collect_report(collector=modcol)
-    clscol = rep.result[0]
-    clscol.obj = lambda arg1: None
-    clscol.funcargs = {}
-    funcargs.fillfixtures(clscol)
-    assert clscol.funcargs['arg1'] == 42
+class TestOEJSKITSpecials:
+    def test_funcarg_non_pycollectobj(self, testdir): # rough jstests usage
+        testdir.makeconftest("""
+            import pytest
+            def pytest_pycollect_makeitem(collector, name, obj):
+                if name == "MyClass":
+                    return MyCollector(name, parent=collector)
+            class MyCollector(pytest.Collector):
+                def reportinfo(self):
+                    return self.fspath, 3, "xyz"
+        """)
+        modcol = testdir.getmodulecol("""
+            def pytest_funcarg__arg1(request):
+                return 42
+            class MyClass:
+                pass
+        """)
+        # this hook finds funcarg factories
+        rep = modcol.ihook.pytest_make_collect_report(collector=modcol)
+        clscol = rep.result[0]
+        clscol.obj = lambda arg1: None
+        clscol.funcargs = {}
+        funcargs.fillfixtures(clscol)
+        assert clscol.funcargs['arg1'] == 42
+
+    def test_autouse_fixture(self, testdir): # rough jstests usage
+        testdir.makeconftest("""
+            import pytest
+            def pytest_pycollect_makeitem(collector, name, obj):
+                if name == "MyClass":
+                    return MyCollector(name, parent=collector)
+            class MyCollector(pytest.Collector):
+                def reportinfo(self):
+                    return self.fspath, 3, "xyz"
+        """)
+        modcol = testdir.getmodulecol("""
+            import pytest
+            @pytest.fixture(autouse=True)
+            def hello():
+                pass
+            def pytest_funcarg__arg1(request):
+                return 42
+            class MyClass:
+                pass
+        """)
+        # this hook finds funcarg factories
+        rep = modcol.ihook.pytest_make_collect_report(collector=modcol)
+        clscol = rep.result[0]
+        clscol.obj = lambda: None
+        clscol.funcargs = {}
+        funcargs.fillfixtures(clscol)
+        assert not clscol.funcargs