speed up reorder for large higher-than-function-scoped parametrizations
This commit is contained in:
parent
a4466342ae
commit
ad2ac256de
|
@ -1626,7 +1626,7 @@ class FixtureManager:
|
||||||
|
|
||||||
def pytest_collection_modifyitems(self, items):
|
def pytest_collection_modifyitems(self, items):
|
||||||
# separate parametrized setups
|
# separate parametrized setups
|
||||||
items[:] = reorder_items(items, set(), 0)
|
items[:] = reorder_items(items)
|
||||||
|
|
||||||
def parsefactories(self, node_or_obj, nodeid=NOTSET, unittest=False):
|
def parsefactories(self, node_or_obj, nodeid=NOTSET, unittest=False):
|
||||||
if nodeid is not NOTSET:
|
if nodeid is not NOTSET:
|
||||||
|
@ -1824,14 +1824,25 @@ def getfuncargnames(function, startindex=None):
|
||||||
# down to the lower scopes such as to minimize number of "high scope"
|
# down to the lower scopes such as to minimize number of "high scope"
|
||||||
# setups and teardowns
|
# setups and teardowns
|
||||||
|
|
||||||
def reorder_items(items, ignore, scopenum):
|
def reorder_items(items):
|
||||||
|
argkeys_cache = {}
|
||||||
|
for scopenum in range(0, scopenum_function):
|
||||||
|
argkeys_cache[scopenum] = d = {}
|
||||||
|
for item in items:
|
||||||
|
keys = set(get_parametrized_fixture_keys(item, scopenum))
|
||||||
|
if keys:
|
||||||
|
d[item] = keys
|
||||||
|
return reorder_items_atscope(items, set(), argkeys_cache, 0)
|
||||||
|
|
||||||
|
def reorder_items_atscope(items, ignore, argkeys_cache, scopenum):
|
||||||
if scopenum >= scopenum_function or len(items) < 3:
|
if scopenum >= scopenum_function or len(items) < 3:
|
||||||
return items
|
return items
|
||||||
items_done = []
|
items_done = []
|
||||||
while 1:
|
while 1:
|
||||||
items_before, items_same, items_other, newignore = \
|
items_before, items_same, items_other, newignore = \
|
||||||
slice_items(items, ignore, scopenum)
|
slice_items(items, ignore, argkeys_cache[scopenum])
|
||||||
items_before = reorder_items(items_before, ignore, scopenum+1)
|
items_before = reorder_items_atscope(
|
||||||
|
items_before, ignore, argkeys_cache,scopenum+1)
|
||||||
if items_same is None:
|
if items_same is None:
|
||||||
# nothing to reorder in this scope
|
# nothing to reorder in this scope
|
||||||
assert items_other is None
|
assert items_other is None
|
||||||
|
@ -1841,54 +1852,58 @@ def reorder_items(items, ignore, scopenum):
|
||||||
ignore = newignore
|
ignore = newignore
|
||||||
|
|
||||||
|
|
||||||
def slice_items(items, ignore, scopenum):
|
def slice_items(items, ignore, scoped_argkeys_cache):
|
||||||
# we pick the first item which uses a fixture instance in the requested scope
|
# we pick the first item which uses a fixture instance in the
|
||||||
# and which we haven't seen yet. We slice the input items list into
|
# requested scope and which we haven't seen yet. We slice the input
|
||||||
# a list of items_nomatch, items_same and items_other
|
# items list into a list of items_nomatch, items_same and
|
||||||
slicing_argkey = None
|
# items_other
|
||||||
for i, item in enumerate(items):
|
if scoped_argkeys_cache: # do we need to do work at all?
|
||||||
argkeys = get_parametrized_fixture_keys(item, ignore, scopenum)
|
it = iter(items)
|
||||||
if slicing_argkey is None:
|
# first find a slicing key
|
||||||
if argkeys:
|
for i, item in enumerate(it):
|
||||||
slicing_argkey = argkeys.pop()
|
argkeys = scoped_argkeys_cache.get(item)
|
||||||
items_before = items[:i]
|
if argkeys is not None:
|
||||||
items_same = [item]
|
argkeys = argkeys.difference(ignore)
|
||||||
items_other = []
|
if argkeys: # found a slicing key
|
||||||
continue
|
slicing_argkey = argkeys.pop()
|
||||||
if slicing_argkey in argkeys:
|
items_before = items[:i]
|
||||||
items_same.append(item)
|
items_same = [item]
|
||||||
else:
|
items_other = []
|
||||||
items_other.append(item)
|
# now slice the remainder of the list
|
||||||
if slicing_argkey is None:
|
for item in it:
|
||||||
return items, None, None, None
|
argkeys = scoped_argkeys_cache.get(item)
|
||||||
newignore = ignore.copy()
|
if argkeys and slicing_argkey in argkeys and \
|
||||||
newignore.add(slicing_argkey)
|
slicing_argkey not in ignore:
|
||||||
return (items_before, items_same, items_other, newignore)
|
items_same.append(item)
|
||||||
|
else:
|
||||||
|
items_other.append(item)
|
||||||
|
newignore = ignore.copy()
|
||||||
|
newignore.add(slicing_argkey)
|
||||||
|
return (items_before, items_same, items_other, newignore)
|
||||||
|
return items, None, None, None
|
||||||
|
|
||||||
def get_parametrized_fixture_keys(item, ignore, scopenum):
|
def get_parametrized_fixture_keys(item, scopenum):
|
||||||
""" return list of keys for all parametrized arguments which match
|
""" return list of keys for all parametrized arguments which match
|
||||||
the specified scope. """
|
the specified scope. """
|
||||||
assert scopenum < scopenum_function # function
|
assert scopenum < scopenum_function # function
|
||||||
keys = set()
|
|
||||||
try:
|
try:
|
||||||
cs = item.callspec
|
cs = item.callspec
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return keys # no parametrization on this item
|
pass
|
||||||
# cs.indictes.items() is random order of argnames but
|
else:
|
||||||
# then again different functions (items) can change order of
|
# cs.indictes.items() is random order of argnames but
|
||||||
# arguments so it doesn't matter much probably
|
# then again different functions (items) can change order of
|
||||||
for argname, param_index in cs.indices.items():
|
# arguments so it doesn't matter much probably
|
||||||
if cs._arg2scopenum[argname] != scopenum:
|
for argname, param_index in cs.indices.items():
|
||||||
continue
|
if cs._arg2scopenum[argname] != scopenum:
|
||||||
if scopenum == 0: # session
|
continue
|
||||||
key = (argname, param_index)
|
if scopenum == 0: # session
|
||||||
elif scopenum == 1: # module
|
key = (argname, param_index)
|
||||||
key = (argname, param_index, item.fspath)
|
elif scopenum == 1: # module
|
||||||
elif scopenum == 2: # class
|
key = (argname, param_index, item.fspath)
|
||||||
key = (argname, param_index, item.fspath, item.cls)
|
elif scopenum == 2: # class
|
||||||
if key not in ignore:
|
key = (argname, param_index, item.fspath, item.cls)
|
||||||
keys.add(key)
|
yield key
|
||||||
return keys
|
|
||||||
|
|
||||||
|
|
||||||
def xunitsetup(obj, name):
|
def xunitsetup(obj, name):
|
||||||
|
|
Loading…
Reference in New Issue