[Pytest-commit] commit/pytest: 2 new changesets

commits-noreply at bitbucket.org commits-noreply at bitbucket.org
Sat Dec 7 16:37:57 CET 2013


2 new commits in pytest:

https://bitbucket.org/hpk42/pytest/commits/74c6f671db35/
Changeset:   74c6f671db35
User:        hpk42
Date:        2013-12-05 14:40:50
Summary:     remove unused line
Affected #:  1 file

diff -r 1338c1ed28ffbeda49a7a9dbf983de8b569f52db -r 74c6f671db35765fd4bfe51685a8ac015fd9ede7 _pytest/python.py
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -1081,8 +1081,6 @@
 
     def __init__(self, pyfuncitem):
         self._pyfuncitem = pyfuncitem
-        if hasattr(pyfuncitem, '_requestparam'):
-            self.param = pyfuncitem._requestparam
         #: fixture for which this request is being performed
         self.fixturename = None
         #: Scope string, one of "function", "cls", "module", "session"


https://bitbucket.org/hpk42/pytest/commits/25ca40a219a0/
Changeset:   25ca40a219a0
User:        hpk42
Date:        2013-12-07 16:37:46
Summary:     refactor internal finalization mechanics such that all fixture arguments
in a test invocation will have a corresponding FixtureDef instance.
also fixes issue246 (again).

simplify parametrized fixture teardown by making it work lazy:
during the setup of a parametrized fixture instance any previously
setup instance which was setup with a different param is torn down
before setting up the new one.
Affected #:  7 files

diff -r 74c6f671db35765fd4bfe51685a8ac015fd9ede7 -r 25ca40a219a02a9451e7f9cb4c3273a41499f331 CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,19 @@
 Unreleased
 -----------------------------------
 
+- simplified and fixed implementation for calling finalizers when
+  parametrized fixtures or function arguments are involved.  finalization 
+  is now performed lazily at setup time instead of in the "teardown phase".
+  While this might sound odd at first, it helps to ensure that we are 
+  correctly handling setup/teardown even in complex code.  User-level code
+  should not be affected unless it's implementing the pytest_runtest_teardown
+  hook and expecting certain fixture instances are torn down within (very
+  unlikely and would have been unreliable anyway).
+
+- fix issue246 (again) fix finalizer order to be LIFO on independent fixtures
+  depending on a parametrized higher-than-function scoped fixture. 
+  (fix quite some effort so please bear with the complexity of this sentence :)
+ 
 - fix issue244 by implementing special index for parameters to only use
   indices for paramentrized test ids
 

diff -r 74c6f671db35765fd4bfe51685a8ac015fd9ede7 -r 25ca40a219a02a9451e7f9cb4c3273a41499f331 _pytest/main.py
--- a/_pytest/main.py
+++ b/_pytest/main.py
@@ -230,6 +230,8 @@
         #: allow adding of extra keywords to use for matching
         self.extra_keyword_matches = set()
 
+        # used for storing artificial fixturedefs for direct parametrization
+        self._name2pseudofixturedef = {}
         #self.extrainit()
 
     @property
@@ -365,6 +367,8 @@
         self.session._setupstate.addfinalizer(fin, self)
 
     def getparent(self, cls):
+        """ get the next parent node (including ourself)
+        which is an instance of the given class"""
         current = self
         while current and not isinstance(current, cls):
             current = current.parent

diff -r 74c6f671db35765fd4bfe51685a8ac015fd9ede7 -r 25ca40a219a02a9451e7f9cb4c3273a41499f331 _pytest/python.py
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -341,12 +341,72 @@
         if not metafunc._calls:
             yield Function(name, parent=self)
         else:
+            # add funcargs() as fixturedefs to fixtureinfo.arg2fixturedefs
+            add_funcarg_pseudo_fixture_def(self, metafunc, fm)
+
             for callspec in metafunc._calls:
                 subname = "%s[%s]" %(name, callspec.id)
                 yield Function(name=subname, parent=self,
                                callspec=callspec, callobj=funcobj,
                                keywords={callspec.id:True})
 
+def add_funcarg_pseudo_fixture_def(collector, metafunc, fixturemanager):
+    # this function will transform all collected calls to a functions
+    # if they use direct funcargs (i.e. direct parametrization)
+    # because we want later test execution to be able to rely on
+    # an existing FixtureDef structure for all arguments.
+    # XXX we can probably avoid this algorithm  if we modify CallSpec2
+    # to directly care for creating the fixturedefs within its methods.
+    if not metafunc._calls[0].funcargs:
+        return # this function call does not have direct parametrization
+    # collect funcargs of all callspecs into a list of values
+    arg2params = {}
+    arg2scope = {}
+    arg2fixturedefs = metafunc._arg2fixturedefs
+    for param_index, callspec in enumerate(metafunc._calls):
+        for argname, argvalue in callspec.funcargs.items():
+            assert argname not in arg2fixturedefs
+            arg2params.setdefault(argname, []).append(argvalue)
+            if argname not in arg2scope:
+                scopenum = callspec._arg2scopenum.get(argname, scopenum_function)
+                arg2scope[argname] = scopes[scopenum]
+            callspec.indices[argname] = param_index
+
+        for argname in callspec.funcargs:
+            assert argname not in callspec.params
+        callspec.params.update(callspec.funcargs)
+        callspec.funcargs.clear()
+
+    # register artificial FixtureDef's so that later at test execution
+    # time we can rely on a proper FixtureDef to exist for fixture setup.
+    for argname, valuelist in arg2params.items():
+        # if we have a scope that is higher than function we need
+        # to make sure we only ever create an according fixturedef on
+        # a per-scope basis. We thus store and cache the fixturedef on the
+        # node related to the scope.
+        assert argname not in arg2fixturedefs, (argname, arg2fixturedefs)
+        scope = arg2scope[argname]
+        node = None
+        if scope != "function":
+            node = get_scope_node(collector, scope)
+            if node is None:
+                assert scope == "class" and isinstance(collector, Module)
+                # use module-level collector for class-scope (for now)
+                node = collector
+        if node and argname in node._name2pseudofixturedef:
+            arg2fixturedefs[argname] = [node._name2pseudofixturedef[argname]]
+        else:
+            fixturedef =  FixtureDef(fixturemanager, '', argname,
+                           get_direct_param_fixture_func,
+                           arg2scope[argname],
+                           valuelist, False, False)
+            arg2fixturedefs[argname] = [fixturedef]
+            if node is not None:
+                node._name2pseudofixturedef[argname] = fixturedef
+
+
+def get_direct_param_fixture_func(request):
+    return request.param
 
 class FuncFixtureInfo:
     def __init__(self, argnames, names_closure, name2fixturedefs):
@@ -560,25 +620,24 @@
 
 def fillfixtures(function):
     """ fill missing funcargs for a test function. """
-    if 1 or getattr(function, "_args", None) is None:  # not a yielded function
-        try:
-            request = function._request
-        except AttributeError:
-            # 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()
-            # prune out funcargs for jstests
-            newfuncargs = {}
-            for name in fi.argnames:
-                newfuncargs[name] = function.funcargs[name]
-            function.funcargs = newfuncargs
-        else:
-            request._fillfixtures()
+    try:
+        request = function._request
+    except AttributeError:
+        # 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()
+        # prune out funcargs for jstests
+        newfuncargs = {}
+        for name in fi.argnames:
+            newfuncargs[name] = function.funcargs[name]
+        function.funcargs = newfuncargs
+    else:
+        request._fillfixtures()
 
 
 _notexists = object()
@@ -630,10 +689,6 @@
         for arg,val in zip(argnames, valset):
             self._checkargnotcontained(arg)
             getattr(self, valtype)[arg] = val
-            # we want self.params to be always set because of
-            # reorder_items() which groups tests by params/scope
-            if valtype == "funcargs":
-                self.params[arg] = id
             self.indices[arg] = param_index
             self._arg2scopenum[arg] = scopenum
             if val is _notexists:
@@ -650,6 +705,8 @@
         if param is not _notexists:
             assert self._globalparam is _notexists
             self._globalparam = param
+        for arg in funcargs:
+            self._arg2scopenum[arg] = scopenum_function
 
 
 class FuncargnamesCompatAttr:
@@ -728,7 +785,7 @@
             argvalues = [(_notexists,) * len(argnames)]
 
         if scope is None:
-            scope = "subfunction"
+            scope = "function"
         scopenum = scopes.index(scope)
         if not indirect:
             #XXX should we also check for the opposite case?
@@ -971,30 +1028,26 @@
             for name, val in keywords.items():
                 self.keywords[name] = val
 
-        fm = self.session._fixturemanager
         isyield = self._isyieldedfunction()
-        self._fixtureinfo = fi = fm.getfixtureinfo(self.parent, self.obj,
-                                                   self.cls,
-                                                   funcargs=not isyield)
+        self._fixtureinfo = fi = self.session._fixturemanager.getfixtureinfo(
+                self.parent, self.obj, self.cls, funcargs=not isyield)
         self.fixturenames = fi.names_closure
         if callspec is not None:
             self.callspec = callspec
         self._initrequest()
 
     def _initrequest(self):
+        self.funcargs = {}
         if self._isyieldedfunction():
             assert not hasattr(self, "callspec"), (
                 "yielded functions (deprecated) cannot have funcargs")
-            self.funcargs = {}
         else:
             if hasattr(self, "callspec"):
                 callspec = self.callspec
-                self.funcargs = callspec.funcargs.copy()
+                assert not callspec.funcargs
                 self._genid = callspec.id
                 if hasattr(callspec, "param"):
                     self.param = callspec.param
-            else:
-                self.funcargs = {}
         self._request = FixtureRequest(self)
 
     @property
@@ -1085,9 +1138,10 @@
         self.fixturename = None
         #: Scope string, one of "function", "cls", "module", "session"
         self.scope = "function"
-        self._funcargs  = self._pyfuncitem.funcargs.copy()
+        self._funcargs  = {}
+        self._fixturedefs = {}
         fixtureinfo = pyfuncitem._fixtureinfo
-        self._arg2fixturedefs = fixtureinfo.name2fixturedefs
+        self._arg2fixturedefs = fixtureinfo.name2fixturedefs.copy()
         self._arg2index = {}
         self.fixturenames = fixtureinfo.names_closure
         self._fixturemanager = pyfuncitem.session._fixturemanager
@@ -1097,15 +1151,17 @@
         """ underlying collection node (depends on current request scope)"""
         return self._getscopeitem(self.scope)
 
+
     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
+            # we arrive here because of a  a dynamic call to
+            # getfuncargvalue(argname) usage which was naturally
+            # not known at parsing/collection time
             fixturedefs = self._fixturemanager.getfixturedefs(
                             argname, self._pyfuncitem.parent.nodeid)
             self._arg2fixturedefs[argname] = fixturedefs
-        # fixturedefs is immutable so we maintain a decreasing index
+        # fixturedefs list 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)
@@ -1137,7 +1193,9 @@
         try:
             return self._pyfuncitem._testcase
         except AttributeError:
-            return py.builtin._getimself(self.function)
+            function = getattr(self, "function", None)
+            if function is not None:
+                return py.builtin._getimself(function)
 
     @scopeproperty()
     def module(self):
@@ -1167,12 +1225,7 @@
         self._addfinalizer(finalizer, scope=self.scope)
 
     def _addfinalizer(self, finalizer, scope):
-        if scope != "function" and hasattr(self, "param"):
-            # parametrized resources are sorted by param
-            # so we rather store finalizers per (argname, param)
-            colitem = (self.fixturename, self.param)
-        else:
-            colitem = self._getscopeitem(scope)
+        colitem = self._getscopeitem(scope)
         self._pyfuncitem.session._setupstate.addfinalizer(
             finalizer=finalizer, colitem=colitem)
 
@@ -1246,19 +1299,24 @@
         setup time, you may use this function to retrieve it inside a fixture
         function body.
         """
+        return self._get_active_fixturedef(argname).cached_result[0]
+
+    def _get_active_fixturedef(self, argname):
         try:
-            return self._funcargs[argname]
+            return self._fixturedefs[argname]
         except KeyError:
-            pass
-        try:
-            fixturedef = self._getnextfixturedef(argname)
-        except FixtureLookupError:
-            if argname == "request":
-                return self
-            raise
-        result = self._getfuncargvalue(fixturedef)
-        self._funcargs[argname] = result
-        return result
+            try:
+                fixturedef = self._getnextfixturedef(argname)
+            except FixtureLookupError:
+                if argname == "request":
+                    class PseudoFixtureDef:
+                        cached_result = (self, [0])
+                    return PseudoFixtureDef
+                raise
+            result = self._getfuncargvalue(fixturedef)
+            self._funcargs[argname] = result
+            self._fixturedefs[argname] = fixturedef
+            return fixturedef
 
     def _get_fixturestack(self):
         current = self
@@ -1272,28 +1330,26 @@
             current = current._parent_request
 
     def _getfuncargvalue(self, fixturedef):
-        try:
-            return fixturedef.cached_result # set by fixturedef.execute()
-        except AttributeError:
-            pass
         # prepare a subrequest object before calling fixture function
         # (latter managed by fixturedef)
         argname = fixturedef.argname
-        node = self._pyfuncitem
+        funcitem = self._pyfuncitem
         scope = fixturedef.scope
         try:
-            param = node.callspec.getparam(argname)
+            param = funcitem.callspec.getparam(argname)
         except (AttributeError, ValueError):
             param = NOTSET
+            param_index = 0
         else:
+            # indices might not be set if old-style metafunc.addcall() was used
+            param_index = funcitem.callspec.indices.get(argname, 0)
             # if a parametrize invocation set a scope it will override
             # the static scope defined with the fixture function
-            paramscopenum = node.callspec._arg2scopenum.get(argname)
-            if paramscopenum is not None and \
-               paramscopenum != scopenum_subfunction:
+            paramscopenum = funcitem.callspec._arg2scopenum.get(argname)
+            if paramscopenum is not None:
                 scope = scopes[paramscopenum]
 
-        subrequest = SubRequest(self, scope, param, fixturedef)
+        subrequest = SubRequest(self, scope, param, param_index, fixturedef)
 
         # check if a higher-level scoped fixture accesses a lower level one
         if scope is not None:
@@ -1308,19 +1364,12 @@
             __tracebackhide__ = False
 
         try:
-            # perform the fixture call
+            # call the fixture function
             val = fixturedef.execute(request=subrequest)
         finally:
-            # if the fixture function failed it might still have
-            # registered finalizers so we can register
-            # prepare finalization according to scope
-            # (XXX analyse exact finalizing mechanics / cleanup)
+            # if fixture function failed it might have registered finalizers
             self.session._setupstate.addfinalizer(fixturedef.finish,
                                                   subrequest.node)
-            self._fixturemanager.addargfinalizer(fixturedef.finish, argname)
-            for subargname in fixturedef.argnames: # XXX all deps?
-                self._fixturemanager.addargfinalizer(fixturedef.finish,
-                                                     subargname)
         return val
 
     def _factorytraceback(self):
@@ -1336,18 +1385,14 @@
 
     def _getscopeitem(self, scope):
         if scope == "function":
+            # this might also be a non-function Item despite its attribute name
             return self._pyfuncitem
-        elif scope == "session":
-            return self.session
-        elif scope == "class":
-            x = self._pyfuncitem.getparent(pytest.Class)
-            if x is not None:
-                return x
-            # fallback to function
-            return self._pyfuncitem
-        if scope == "module":
-            return self._pyfuncitem.getparent(pytest.Module)
-        raise ValueError("unknown finalization scope %r" %(scope,))
+        node = get_scope_node(self._pyfuncitem, scope)
+        if node is None and scope == "class":
+            # fallback to function item itself
+            node = self._pyfuncitem
+        assert node
+        return node
 
     def __repr__(self):
         return "<FixtureRequest for %r>" %(self.node)
@@ -1356,16 +1401,18 @@
 class SubRequest(FixtureRequest):
     """ a sub request for handling getting a fixture from a
     test function/fixture. """
-    def __init__(self, request, scope, param, fixturedef):
+    def __init__(self, request, scope, param, param_index, fixturedef):
         self._parent_request = request
         self.fixturename = fixturedef.argname
         if param is not NOTSET:
             self.param = param
+        self.param_index = param_index
         self.scope = scope
         self._fixturedef = fixturedef
         self.addfinalizer = fixturedef.addfinalizer
         self._pyfuncitem = request._pyfuncitem
         self._funcargs  = request._funcargs
+        self._fixturedefs = request._fixturedefs
         self._arg2fixturedefs = request._arg2fixturedefs
         self._arg2index = request._arg2index
         self.fixturenames = request.fixturenames
@@ -1380,8 +1427,7 @@
     which has a lower scope (e.g. a Session one calls a function one)
     """
 
-scopes = "session module class function subfunction".split()
-scopenum_subfunction = scopes.index("subfunction")
+scopes = "session module class function".split()
 scopenum_function = scopes.index("function")
 def scopemismatch(currentscope, newscope):
     return scopes.index(newscope) > scopes.index(currentscope)
@@ -1581,14 +1627,14 @@
                 if argname in arg2fixturedefs:
                     continue
                 fixturedefs = self.getfixturedefs(argname, parentid)
-                arg2fixturedefs[argname] = fixturedefs
                 if fixturedefs:
+                    arg2fixturedefs[argname] = fixturedefs
                     merge(fixturedefs[-1].argnames)
         return fixturenames_closure, arg2fixturedefs
 
     def pytest_generate_tests(self, metafunc):
         for argname in metafunc.fixturenames:
-            faclist = metafunc._arg2fixturedefs[argname]
+            faclist = metafunc._arg2fixturedefs.get(argname)
             if faclist is None:
                 continue # will raise FixtureLookupError at setup time
             for fixturedef in faclist:
@@ -1600,38 +1646,6 @@
         # separate parametrized setups
         items[:] = reorder_items(items, set(), {}, 0)
 
-    @pytest.mark.trylast
-    def pytest_runtest_teardown(self, item, nextitem):
-        # XXX teardown needs to be normalized for parametrized and
-        # no-parametrized functions
-        try:
-            cs1 = item.callspec
-        except AttributeError:
-            return
-
-        # determine which fixtures are not needed anymore for the next test
-        keylist = []
-        for name in cs1.params:
-            try:
-                if name in nextitem.callspec.params and \
-                    cs1.params[name] == nextitem.callspec.params[name]:
-                    continue
-            except AttributeError:
-                pass
-            key = (-cs1._arg2scopenum[name], name, cs1.params[name])
-            keylist.append(key)
-
-        # sort by scope (function scope first, then higher ones)
-        keylist.sort()
-        for (scopenum, name, param) in keylist:
-            #if -scopenum >= scopenum_function:
-            #    continue # handled by runner.pytest_runtest_teardown
-            item.session._setupstate._callfinalizers((name, param))
-            l = self._arg2finish.pop(name, None)
-            if l is not None:
-                for fin in reversed(l):
-                    fin()
-
     def parsefactories(self, node_or_obj, nodeid=NOTSET, unittest=False):
         if nodeid is not NOTSET:
             holderobj = node_or_obj
@@ -1693,16 +1707,6 @@
             if nodeid.startswith(fixturedef.baseid):
                 yield fixturedef
 
-    def addargfinalizer(self, finalizer, argname):
-        l = self._arg2finish.setdefault(argname, [])
-        l.append(finalizer)
-
-    def removefinalizer(self, finalizer):
-        for l in self._arg2finish.values():
-            try:
-                l.remove(finalizer)
-            except ValueError:
-                pass
 
 def fail_fixturefunc(fixturefunc, msg):
     fs, lineno = getfslineno(fixturefunc)
@@ -1764,41 +1768,56 @@
         while self._finalizer:
             func = self._finalizer.pop()
             func()
-        # check neccesity of next commented call
-        self._fixturemanager.removefinalizer(self.finish)
         try:
             del self.cached_result
         except AttributeError:
             pass
 
     def execute(self, request):
+        # get required arguments and register our own finish()
+        # with their finalization
         kwargs = {}
-        for newname in self.argnames:
-            kwargs[newname] = request.getfuncargvalue(newname)
+        for argname in self.argnames:
+            fixturedef = request._get_active_fixturedef(argname)
+            result, arg_cache_key = fixturedef.cached_result
+            kwargs[argname] = result
+            if argname != "request":
+                fixturedef.addfinalizer(self.finish)
+
+        my_cache_key = request.param_index
+        cached_result = getattr(self, "cached_result", None)
+        if cached_result is not None:
+            #print argname, "Found cached_result", cached_result
+            #print argname, "param_index", param_index
+            result, cache_key = cached_result
+            if my_cache_key == cache_key:
+                #print request.fixturename, "CACHE HIT", repr(my_cache_key)
+                return result
+            #print request.fixturename, "CACHE MISS"
+            # we have a previous but differently parametrized fixture instance
+            # so we need to tear it down before creating a new one
+            self.finish()
+            assert not hasattr(self, "cached_result")
+
         if self.unittest:
             result = self.func(request.instance, **kwargs)
         else:
             fixturefunc = self.func
             # the fixture function needs to be bound to the actual
             # request.instance so that code working with "self" behaves
-            # as expected. XXX request.instance should maybe return None
-            # instead of raising AttributeError
-            try:
-                if request.instance is not None:
-                    fixturefunc = getimfunc(self.func)
-                    if fixturefunc != self.func:
-                        fixturefunc = fixturefunc.__get__(request.instance)
-            except AttributeError:
-                pass
+            # as expected.
+            if request.instance is not None:
+                fixturefunc = getimfunc(self.func)
+                if fixturefunc != self.func:
+                    fixturefunc = fixturefunc.__get__(request.instance)
             result = call_fixture_func(fixturefunc, request, kwargs,
                                        self.yieldctx)
-        assert not hasattr(self, "cached_result")
-        self.cached_result = result
+        self.cached_result = (result, my_cache_key)
         return result
 
     def __repr__(self):
-        return ("<FixtureDef name=%r scope=%r baseid=%r module=%r>" %
-                (self.argname, self.scope, self.baseid, self.func.__module__))
+        return ("<FixtureDef name=%r scope=%r baseid=%r >" %
+                (self.argname, self.scope, self.baseid))
 
 def getfuncargnames(function, startindex=None):
     # XXX merge with main.py's varnames
@@ -1910,3 +1929,15 @@
         # we don't expect them to be fixture functions
         return None
 
+scopename2class = {
+    'class': Class,
+    'module': Module,
+    'function': pytest.Item,
+}
+def get_scope_node(node, scope):
+    cls = scopename2class.get(scope)
+    if cls is None:
+        if scope == "session":
+            return node.session
+        raise ValueError("unknown scope")
+    return node.getparent(cls)

diff -r 74c6f671db35765fd4bfe51685a8ac015fd9ede7 -r 25ca40a219a02a9451e7f9cb4c3273a41499f331 testing/python/collect.py
--- a/testing/python/collect.py
+++ b/testing/python/collect.py
@@ -334,7 +334,7 @@
             def test_function(arg):
                 assert arg.__class__.__name__ == "A"
         """)
-        reprec = testdir.inline_run()
+        reprec = testdir.inline_run("--fulltrace")
         reprec.assertoutcome(passed=1)
 
     def test_parametrize_with_non_hashable_values(self, testdir):

diff -r 74c6f671db35765fd4bfe51685a8ac015fd9ede7 -r 25ca40a219a02a9451e7f9cb4c3273a41499f331 testing/python/fixture.py
--- a/testing/python/fixture.py
+++ b/testing/python/fixture.py
@@ -1315,6 +1315,7 @@
                     l.append("step2-%d" % item)
 
             def test_finish():
+                print (l)
                 assert l == ["setup-1", "step1-1", "step2-1", "teardown-1",
                              "setup-2", "step1-2", "step2-2", "teardown-2",]
         """)
@@ -1683,23 +1684,22 @@
                 l.append("test3")
             def test_4(modarg, arg):
                 l.append("test4")
-            def test_5():
-                assert len(l) == 12 * 3
-                expected = [
-                    'create:1', 'test1', 'fin:1', 'create:2', 'test1',
-                    'fin:2', 'create:mod1', 'test2', 'create:1', 'test3',
-                    'fin:1', 'create:2', 'test3', 'fin:2', 'create:1',
-                    'test4', 'fin:1', 'create:2', 'test4', 'fin:2',
-                    'fin:mod1', 'create:mod2', 'test2', 'create:1', 'test3',
-                    'fin:1', 'create:2', 'test3', 'fin:2', 'create:1',
-                    'test4', 'fin:1', 'create:2', 'test4', 'fin:2',
-                'fin:mod2']
-                import pprint
-                pprint.pprint(list(zip(l, expected)))
-                assert l == expected
         """)
         reprec = testdir.inline_run("-v")
-        reprec.assertoutcome(passed=12+1)
+        reprec.assertoutcome(passed=12)
+        l = reprec.getcalls("pytest_runtest_call")[0].item.module.l
+        expected = [
+            'create:1', 'test1', 'fin:1', 'create:2', 'test1',
+            'fin:2', 'create:mod1', 'test2', 'create:1', 'test3',
+            'fin:1', 'create:2', 'test3', 'fin:2', 'create:1',
+            'test4', 'fin:1', 'create:2', 'test4', 'fin:2',
+            'fin:mod1', 'create:mod2', 'test2', 'create:1', 'test3',
+            'fin:1', 'create:2', 'test3', 'fin:2', 'create:1',
+            'test4', 'fin:1', 'create:2', 'test4', 'fin:2',
+        'fin:mod2']
+        import pprint
+        pprint.pprint(list(zip(l, expected)))
+        assert l == expected
 
     def test_parametrized_fixture_teardown_order(self, testdir):
         testdir.makepyfile("""
@@ -1855,7 +1855,6 @@
         reprec.assertoutcome(passed=5)
 
 
-    @pytest.mark.xfail
     @pytest.mark.issue246
     @pytest.mark.parametrize("scope", ["session", "function", "module"])
     def test_finalizer_order_on_parametrization(self, scope, testdir):

diff -r 74c6f671db35765fd4bfe51685a8ac015fd9ede7 -r 25ca40a219a02a9451e7f9cb4c3273a41499f331 testing/python/metafunc.py
--- a/testing/python/metafunc.py
+++ b/testing/python/metafunc.py
@@ -193,8 +193,8 @@
                 metafunc.parametrize('y', [2])
             def pytest_funcarg__x(request):
                 return request.param * 10
-            def pytest_funcarg__y(request):
-                return request.param
+            #def pytest_funcarg__y(request):
+            #    return request.param
 
             def test_simple(x,y):
                 assert x in (10,20)

diff -r 74c6f671db35765fd4bfe51685a8ac015fd9ede7 -r 25ca40a219a02a9451e7f9cb4c3273a41499f331 tox.ini
--- a/tox.ini
+++ b/tox.ini
@@ -115,7 +115,7 @@
 minversion=2.0
 plugins=pytester
 #--pyargs --doctest-modules --ignore=.tox
-addopts= -rxs
+addopts= -rxsX
 rsyncdirs=tox.ini pytest.py _pytest testing
 python_files=test_*.py *_test.py testing/*/*.py
 python_classes=Test Acceptance

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.


More information about the pytest-commit mailing list