refactor and document parametrized sorting code.
This commit is contained in:
parent
cb485e5af4
commit
98c6ced46e
|
@ -631,7 +631,7 @@ class CallSpec2(object):
|
||||||
self._checkargnotcontained(arg)
|
self._checkargnotcontained(arg)
|
||||||
getattr(self, valtype)[arg] = val
|
getattr(self, valtype)[arg] = val
|
||||||
# we want self.params to be always set because of
|
# 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":
|
if valtype == "funcargs":
|
||||||
self.params[arg] = id
|
self.params[arg] = id
|
||||||
self.indices[arg] = param_index
|
self.indices[arg] = param_index
|
||||||
|
@ -1600,7 +1600,7 @@ class FixtureManager:
|
||||||
|
|
||||||
def pytest_collection_modifyitems(self, items):
|
def pytest_collection_modifyitems(self, items):
|
||||||
# separate parametrized setups
|
# separate parametrized setups
|
||||||
items[:] = parametrize_sorted(items, set(), {}, 0)
|
items[:] = reorder_items(items, set(), {}, 0)
|
||||||
|
|
||||||
@pytest.mark.trylast
|
@pytest.mark.trylast
|
||||||
def pytest_runtest_teardown(self, item, nextitem):
|
def pytest_runtest_teardown(self, item, nextitem):
|
||||||
|
@ -1822,85 +1822,77 @@ def getfuncargnames(function, startindex=None):
|
||||||
return tuple(argnames[startindex:])
|
return tuple(argnames[startindex:])
|
||||||
|
|
||||||
# algorithm for sorting on a per-parametrized resource setup basis
|
# 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):
|
def reorder_items(items, ignore, cache, scopenum):
|
||||||
if scopenum >= 3:
|
if scopenum >= scopenum_function:
|
||||||
return items
|
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
|
# we pick the first item which uses a fixture instance in the requested scope
|
||||||
# requested scope and sort other items with the same combo
|
# and which we haven't seen yet. We slice the input items list into
|
||||||
# into "newitems" which then is a list of all items using this
|
# a list of items_nomatch, items_using_same_fixtureinstance and
|
||||||
# arg/param.
|
# items_remaining
|
||||||
|
slicing_argkey = None
|
||||||
similar_items = []
|
for i, item in enumerate(items):
|
||||||
other_items = []
|
argkeys = get_parametrized_fixture_keys(item, ignore, scopenum, cache)
|
||||||
slicing_argparam = None
|
if slicing_argkey is None:
|
||||||
slicing_index = 0
|
if argkeys:
|
||||||
for item in items:
|
slicing_argkey = argkeys.pop()
|
||||||
argparamlist = getfuncargparams(item, ignore, scopenum, cache)
|
items_using_same_fixtureinstance = [item]
|
||||||
if slicing_argparam is None and argparamlist:
|
items_nomatch = items[:i]
|
||||||
slicing_argparam = argparamlist[0]
|
items_remaining = []
|
||||||
slicing_index = len(other_items)
|
continue
|
||||||
if slicing_argparam in argparamlist:
|
if slicing_argkey in argkeys:
|
||||||
similar_items.append(item)
|
items_using_same_fixtureinstance.append(item)
|
||||||
else:
|
else:
|
||||||
other_items.append(item)
|
items_remaining.append(item)
|
||||||
|
|
||||||
if (len(similar_items) + slicing_index) > 1:
|
if slicing_argkey is None or len(items_using_same_fixtureinstance) == 1:
|
||||||
newignore = ignore.copy()
|
# nothing to sort on this level
|
||||||
newignore.add(slicing_argparam)
|
return reorder_items(items, ignore, cache, scopenum+1)
|
||||||
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
|
|
||||||
|
|
||||||
def getfuncargparams(item, ignore, scopenum, cache):
|
items_nomatch = reorder_items(items_nomatch, ignore, cache, scopenum+1)
|
||||||
""" return list of (arg,param) tuple, sorted by broader scope first. """
|
newignore = ignore.copy()
|
||||||
assert scopenum < 3 # function
|
newignore.add(slicing_argkey)
|
||||||
|
part2 = reorder_items(items_using_same_fixtureinstance + items_remaining,
|
||||||
|
newignore, cache, scopenum)
|
||||||
|
return items_nomatch + part2
|
||||||
|
|
||||||
|
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:
|
try:
|
||||||
cs = item.callspec
|
cs = item.callspec
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return []
|
return keys # no parametrization on this item
|
||||||
if scopenum == 0:
|
# cs.indictes.items() is random order of argnames but
|
||||||
argparams = [x for x in cs.indices.items() if x not in ignore
|
# then again different functions (items) can change order of
|
||||||
and cs._arg2scopenum[x[0]] == scopenum]
|
# arguments so it doesn't matter much probably
|
||||||
elif scopenum == 1: # module
|
for argname, param_index in cs.indices.items():
|
||||||
argparams = []
|
if cs._arg2scopenum[argname] != scopenum:
|
||||||
for argname, valindex in cs.indices.items():
|
continue
|
||||||
if cs._arg2scopenum[argname] == scopenum:
|
if scopenum == 0: # session
|
||||||
key = (argname, valindex, item.fspath)
|
key = (argname, param_index)
|
||||||
if key in ignore:
|
elif scopenum == 1: # module
|
||||||
continue
|
key = (argname, param_index, item.fspath)
|
||||||
argparams.append(key)
|
elif scopenum == 2: # class
|
||||||
elif scopenum == 2: # class
|
l = cache.setdefault(item.fspath, [])
|
||||||
argparams = []
|
try:
|
||||||
for argname, valindex in cs.indices.items():
|
i = l.index(item.cls)
|
||||||
if cs._arg2scopenum[argname] == scopenum:
|
except ValueError:
|
||||||
l = cache.setdefault(item.fspath, [])
|
i = len(l)
|
||||||
try:
|
l.append(item.cls)
|
||||||
i = l.index(item.cls)
|
key = (argname, param_index, item.fspath, i)
|
||||||
except ValueError:
|
if key not in ignore:
|
||||||
i = len(l)
|
keys.add(key)
|
||||||
l.append(item.cls)
|
return keys
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def xunitsetup(obj, name):
|
def xunitsetup(obj, name):
|
||||||
|
|
Loading…
Reference in New Issue