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, scopenum): def reorder_items_atscope(items, ignore, argkeys_cache, items_by_argkey, 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:
# nothing to reorder in this scope
assert items_other is None
return items_done + items_before
items_done.extend(items_before)
items = items_same + items_other
ignore = newignore
no_argkey_group = OrderedDict()
slicing_argkey = None
while items:
item = items.popleft()
if item in items_done:
continue
argkeys = OrderedDict.fromkeys(k for k in scoped_argkeys_cache.get(item, ()) if k not in ignore)
if not argkeys:
no_argkey_group[item] = None
def slice_items(items, ignore, scoped_argkeys_cache):
# 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_same and
# items_other
if scoped_argkeys_cache: # do we need to do work at all?
it = iter(items)
# first find a slicing key
for i, item in enumerate(it):
argkeys = scoped_argkeys_cache.get(item)
if argkeys is not None:
newargkeys = OrderedDict.fromkeys(k for k in argkeys if k not in ignore)
if newargkeys: # found a slicing key
slicing_argkey, _ = newargkeys.popitem()
items_before = items[:i]
items_same = [item]
items_other = []
# now slice the remainder of the list
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: else:
items_other.append(item) slicing_argkey, _ = argkeys.popitem()
newignore = ignore.copy() #we don't have to remove relevant items from later in the deque because they'll just be ignored
newignore.add(slicing_argkey) items.extendleft(reversed(scoped_items_by_argkey[slicing_argkey]))
return (items_before, items_same, items_other, newignore) break
return items, None, None, None
if no_argkey_group:
no_argkey_group = reorder_items_atscope(
no_argkey_group, set(), argkeys_cache, items_by_argkey, scopenum + 1)
for item in no_argkey_group:
items_done[item] = None
ignore.add(slicing_argkey)
return items_done
def fillfixtures(function): def fillfixtures(function):