optimize fixtures.reorder_items

This commit is contained in:
Aaron 2018-01-10 15:13:42 -08:00
parent b0032ba2b3
commit ee6c9f50a2
1 changed files with 42 additions and 48 deletions

View File

@ -4,7 +4,7 @@ import functools
import inspect import inspect
import sys import sys
import warnings import warnings
from collections import OrderedDict from collections import OrderedDict, deque, defaultdict
import attr import attr
import py import py
@ -163,62 +163,56 @@ def get_parametrized_fixture_keys(item, scopenum):
def reorder_items(items): def reorder_items(items):
argkeys_cache = {} argkeys_cache = {}
items_by_argkey = {}
for scopenum in range(0, scopenum_function): for scopenum in range(0, scopenum_function):
argkeys_cache[scopenum] = d = {} argkeys_cache[scopenum] = d = {}
items_by_argkey[scopenum] = item_d = defaultdict(list)
for item in items: for item in items:
keys = OrderedDict.fromkeys(get_parametrized_fixture_keys(item, scopenum)) keys = OrderedDict.fromkeys(get_parametrized_fixture_keys(item, scopenum))
if keys: if keys:
d[item] = keys d[item] = keys
return reorder_items_atscope(items, set(), argkeys_cache, 0) for key in keys:
item_d[key].append(item)
return list(reorder_items_atscope(items, set(), argkeys_cache, items_by_argkey, 0))
def reorder_items_atscope(items, ignore, argkeys_cache, items_by_argkey, scopenum):
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 = deque(items)
while 1: items_done = OrderedDict()
items_before, items_same, items_other, newignore = \ scoped_items_by_argkey = items_by_argkey[scopenum]
slice_items(items, ignore, argkeys_cache[scopenum]) scoped_argkeys_cache = argkeys_cache[scopenum]
items_before = reorder_items_atscope( while items:
items_before, ignore, argkeys_cache, scopenum + 1)
if items_same is None: no_argkey_group = OrderedDict()
# nothing to reorder in this scope slicing_argkey = None
assert items_other is None
return items_done + items_before while items:
items_done.extend(items_before) item = items.popleft()
items = items_same + items_other if item in items_done:
ignore = newignore continue
argkeys = OrderedDict.fromkeys(k for k in scoped_argkeys_cache.get(item, ()) if k not in ignore)
def slice_items(items, ignore, scoped_argkeys_cache): if not argkeys:
# we pick the first item which uses a fixture instance in the no_argkey_group[item] = None
# requested scope and which we haven't seen yet. We slice the input
# items list into a list of items_nomatch, items_same and else:
# items_other slicing_argkey, _ = argkeys.popitem()
if scoped_argkeys_cache: # do we need to do work at all? #we don't have to remove relevant items from later in the deque because they'll just be ignored
it = iter(items) items.extendleft(reversed(scoped_items_by_argkey[slicing_argkey]))
# first find a slicing key break
for i, item in enumerate(it):
argkeys = scoped_argkeys_cache.get(item) if no_argkey_group:
if argkeys is not None: no_argkey_group = reorder_items_atscope(
newargkeys = OrderedDict.fromkeys(k for k in argkeys if k not in ignore) no_argkey_group, set(), argkeys_cache, items_by_argkey, scopenum + 1)
if newargkeys: # found a slicing key for item in no_argkey_group:
slicing_argkey, _ = newargkeys.popitem() items_done[item] = None
items_before = items[:i]
items_same = [item] ignore.add(slicing_argkey)
items_other = []
# now slice the remainder of the list return items_done
for item in it:
argkeys = scoped_argkeys_cache.get(item)
if argkeys and slicing_argkey in argkeys and \
slicing_argkey not in ignore:
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 fillfixtures(function): def fillfixtures(function):