Merge pull request #3108 from cheezman34/features

Optimize reorder_items in fixtures.py
This commit is contained in:
Bruno Oliveira 2018-01-27 23:21:09 -02:00 committed by GitHub
commit 0e1be01b7a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 38 additions and 47 deletions

View File

@ -3,6 +3,7 @@ merlinux GmbH, Germany, office at merlinux eu
Contributors include:: Contributors include::
Aaron Coleman
Abdeali JK Abdeali JK
Abhijeet Kasurde Abhijeet Kasurde
Ahn Ki-Wook Ahn Ki-Wook

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,51 @@ 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)
items = OrderedDict.fromkeys(items)
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 = 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_deque:
items_before, ignore, argkeys_cache, scopenum + 1) no_argkey_group = OrderedDict()
if items_same is None: slicing_argkey = None
# nothing to reorder in this scope while items_deque:
assert items_other is None item = items_deque.popleft()
return items_done + items_before if item in items_done or item in no_argkey_group:
items_done.extend(items_before) continue
items = items_same + items_other argkeys = OrderedDict.fromkeys(k for k in scoped_argkeys_cache.get(item, []) if k not in ignore)
ignore = newignore 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) for i in reversed(scoped_items_by_argkey[slicing_argkey]):
return (items_before, items_same, items_other, newignore) if i in items:
return items, None, None, None items_deque.appendleft(i)
break
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):

1
changelog/3107.feature Normal file
View File

@ -0,0 +1 @@
Improve performance when collecting tests using many fixtures.