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

commits-noreply at bitbucket.org commits-noreply at bitbucket.org
Thu Dec 5 06:31:56 CET 2013


2 new commits in pytest:

https://bitbucket.org/hpk42/pytest/commits/20f0da86518e/
Changeset:   20f0da86518e
User:        hpk42
Date:        2013-12-04 16:09:37
Summary:     reopen #246 -- it turns out parametrized finalization ordering is not fully fixed -- i modified the test and marked it xfail for now.
Affected #:  1 file

diff -r aa1f0505a3156b9feca43cd67c5afc95622b9ac5 -r 20f0da86518e3636726e2880b3b4d761fce56ec2 testing/python/fixture.py
--- a/testing/python/fixture.py
+++ b/testing/python/fixture.py
@@ -1855,6 +1855,7 @@
         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):
@@ -1876,15 +1877,18 @@
             def base(request, fix1):
                 def cleanup_base():
                     l.append("fin_base")
+                    print ("finalizing base")
                 request.addfinalizer(cleanup_base)
 
+            def test_begin():
+                pass
             def test_baz(base, fix2):
                 pass
             def test_other():
                 pass
         """ % {"scope": scope})
-        reprec = testdir.inline_run()
-        reprec.assertoutcome(passed=2)
+        reprec = testdir.inline_run("-lvs")
+        reprec.assertoutcome(passed=3)
 
     def test_parametrize_setup_function(self, testdir):
         testdir.makepyfile("""


https://bitbucket.org/hpk42/pytest/commits/1338c1ed28ff/
Changeset:   1338c1ed28ff
User:        hpk42
Date:        2013-12-05 06:09:29
Summary:     refactor and document parametrized sorting code.
Affected #:  1 file

diff -r 20f0da86518e3636726e2880b3b4d761fce56ec2 -r 1338c1ed28ffbeda49a7a9dbf983de8b569f52db _pytest/python.py
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -631,7 +631,7 @@
             self._checkargnotcontained(arg)
             getattr(self, valtype)[arg] = val
             # we want self.params to be always set because of
-            # parametrize_sorted() which groups tests by params/scope
+            # reorder_items() which groups tests by params/scope
             if valtype == "funcargs":
                 self.params[arg] = id
             self.indices[arg] = param_index
@@ -1600,7 +1600,7 @@
 
     def pytest_collection_modifyitems(self, items):
         # separate parametrized setups
-        items[:] = parametrize_sorted(items, set(), {}, 0)
+        items[:] = reorder_items(items, set(), {}, 0)
 
     @pytest.mark.trylast
     def pytest_runtest_teardown(self, item, nextitem):
@@ -1822,85 +1822,77 @@
     return tuple(argnames[startindex:])
 
 # algorithm for sorting on a per-parametrized resource setup basis
+# it is called for scopenum==0 (session) first and performs sorting
+# down to the lower scopes such as to minimize number of "high scope"
+# setups and teardowns
 
-def parametrize_sorted(items, ignore, cache, scopenum):
-    if scopenum >= 3:
+def reorder_items(items, ignore, cache, scopenum):
+    if scopenum >= scopenum_function:
         return items
+    if len(items) < 2:
+        return items
+    #print "\nparametrize_Sorted", items, ignore, cache, scopenum
 
-    # we pick the first item which has a arg/param combo in the
-    # requested scope and sort other items with the same combo
-    # into "newitems" which then is a list of all items using this
-    # arg/param.
+    # we pick the first item which uses a fixture instance in the requested scope
+    # and which we haven't seen yet.  We slice the input items list into
+    # a list of items_nomatch, items_using_same_fixtureinstance and
+    # items_remaining
+    slicing_argkey = None
+    for i, item in enumerate(items):
+        argkeys = get_parametrized_fixture_keys(item, ignore, scopenum, cache)
+        if slicing_argkey is None:
+            if argkeys:
+                slicing_argkey = argkeys.pop()
+                items_using_same_fixtureinstance = [item]
+                items_nomatch = items[:i]
+                items_remaining = []
+            continue
+        if slicing_argkey in argkeys:
+            items_using_same_fixtureinstance.append(item)
+        else:
+            items_remaining.append(item)
 
-    similar_items = []
-    other_items = []
-    slicing_argparam = None
-    slicing_index = 0
-    for item in items:
-        argparamlist = getfuncargparams(item, ignore, scopenum, cache)
-        if slicing_argparam is None and argparamlist:
-            slicing_argparam = argparamlist[0]
-            slicing_index = len(other_items)
-        if slicing_argparam in argparamlist:
-            similar_items.append(item)
-        else:
-            other_items.append(item)
+    if slicing_argkey is None or len(items_using_same_fixtureinstance) == 1:
+        # nothing to sort on this level
+        return reorder_items(items, ignore, cache, scopenum+1)
 
-    if (len(similar_items) + slicing_index) > 1:
-        newignore = ignore.copy()
-        newignore.add(slicing_argparam)
-        part2 = parametrize_sorted(
-                        similar_items + other_items[slicing_index:],
-                        newignore, cache, scopenum)
-        part1 = parametrize_sorted(
-                        other_items[:slicing_index], newignore,
-                        cache, scopenum+1)
-        return part1 + part2
-    else:
-        other_items = parametrize_sorted(other_items, ignore, cache, scopenum+1)
-    return other_items + similar_items
+    items_nomatch = reorder_items(items_nomatch, ignore, cache, scopenum+1)
+    newignore = ignore.copy()
+    newignore.add(slicing_argkey)
+    part2 = reorder_items(items_using_same_fixtureinstance + items_remaining,
+                          newignore, cache, scopenum)
+    return items_nomatch + part2
 
-def getfuncargparams(item, ignore, scopenum, cache):
-    """ return list of (arg,param) tuple, sorted by broader scope first. """
-    assert scopenum < 3  # function
+def get_parametrized_fixture_keys(item, ignore, scopenum, cache):
+    """ return list of keys for all parametrized arguments which match
+    the specified scope. """
+    assert scopenum < scopenum_function  # function
+    keys = set()
     try:
         cs = item.callspec
     except AttributeError:
-        return []
-    if scopenum == 0:
-        argparams = [x for x in cs.indices.items() if x not in ignore
-                        and cs._arg2scopenum[x[0]] == scopenum]
-    elif scopenum == 1:  # module
-        argparams = []
-        for argname, valindex in cs.indices.items():
-            if cs._arg2scopenum[argname] == scopenum:
-                key = (argname, valindex, item.fspath)
-                if key in ignore:
-                    continue
-                argparams.append(key)
-    elif scopenum == 2:  # class
-        argparams = []
-        for argname, valindex in cs.indices.items():
-            if cs._arg2scopenum[argname] == scopenum:
-                l = cache.setdefault(item.fspath, [])
-                try:
-                    i = l.index(item.cls)
-                except ValueError:
-                    i = len(l)
-                    l.append(item.cls)
-                key = (argname, valindex, item.fspath, i)
-                if key in ignore:
-                    continue
-                argparams.append(key)
-    #elif scopenum == 3:
-    #    argparams = []
-    #    for argname, param in cs.indices.items():
-    #        if cs._arg2scopenum[argname] == scopenum:
-    #            key = (argname, param, getfslineno(item.obj))
-    #            if key in ignore:
-    #                continue
-    #            argparams.append(key)
-    return argparams
+        return keys  # no parametrization on this item
+    # cs.indictes.items() is random order of argnames but
+    # then again different functions (items) can change order of
+    # arguments so it doesn't matter much probably
+    for argname, param_index in cs.indices.items():
+        if cs._arg2scopenum[argname] != scopenum:
+            continue
+        if scopenum == 0:    # session
+            key = (argname, param_index)
+        elif scopenum == 1:  # module
+            key = (argname, param_index, item.fspath)
+        elif scopenum == 2:  # class
+            l = cache.setdefault(item.fspath, [])
+            try:
+                i = l.index(item.cls)
+            except ValueError:
+                i = len(l)
+                l.append(item.cls)
+            key = (argname, param_index, item.fspath, i)
+        if key not in ignore:
+            keys.add(key)
+    return keys
 
 
 def xunitsetup(obj, name):

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