Type annotate tricky reorder_items() function in fixtures.py

This commit is contained in:
Ran Benita 2020-05-01 14:40:16 +03:00
parent e68a26199c
commit 387d9d04f7
1 changed files with 42 additions and 18 deletions

View File

@ -50,6 +50,7 @@ from _pytest.outcomes import fail
from _pytest.outcomes import TEST_OUTCOME from _pytest.outcomes import TEST_OUTCOME
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Deque
from typing import NoReturn from typing import NoReturn
from typing import Type from typing import Type
from typing_extensions import Literal from typing_extensions import Literal
@ -213,7 +214,11 @@ def getfixturemarker(obj):
return None return None
def get_parametrized_fixture_keys(item, scopenum): # Parametrized fixture key, helper alias for code below.
_Key = Tuple[object, ...]
def get_parametrized_fixture_keys(item: "nodes.Item", scopenum: int) -> Iterator[_Key]:
""" 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
@ -230,13 +235,14 @@ def get_parametrized_fixture_keys(item, scopenum):
if cs._arg2scopenum[argname] != scopenum: if cs._arg2scopenum[argname] != scopenum:
continue continue
if scopenum == 0: # session if scopenum == 0: # session
key = (argname, param_index) key = (argname, param_index) # type: _Key
elif scopenum == 1: # package elif scopenum == 1: # package
key = (argname, param_index, item.fspath.dirpath()) key = (argname, param_index, item.fspath.dirpath())
elif scopenum == 2: # module elif scopenum == 2: # module
key = (argname, param_index, item.fspath) key = (argname, param_index, item.fspath)
elif scopenum == 3: # class elif scopenum == 3: # class
key = (argname, param_index, item.fspath, item.cls) item_cls = item.cls # type: ignore[attr-defined] # noqa: F821
key = (argname, param_index, item.fspath, item_cls)
yield key yield key
@ -246,47 +252,65 @@ def get_parametrized_fixture_keys(item, scopenum):
# setups and teardowns # setups and teardowns
def reorder_items(items): def reorder_items(items: "Sequence[nodes.Item]") -> "List[nodes.Item]":
argkeys_cache = {} argkeys_cache = {} # type: Dict[int, Dict[nodes.Item, Dict[_Key, None]]]
items_by_argkey = {} items_by_argkey = {} # type: Dict[int, Dict[_Key, Deque[nodes.Item]]]
for scopenum in range(0, scopenum_function): for scopenum in range(0, scopenum_function):
argkeys_cache[scopenum] = d = {} d = {} # type: Dict[nodes.Item, Dict[_Key, None]]
items_by_argkey[scopenum] = item_d = defaultdict(deque) argkeys_cache[scopenum] = d
item_d = defaultdict(deque) # type: Dict[_Key, Deque[nodes.Item]]
items_by_argkey[scopenum] = item_d
for item in items: for item in items:
keys = order_preserving_dict.fromkeys( # cast is a workaround for https://github.com/python/typeshed/issues/3800.
get_parametrized_fixture_keys(item, scopenum) keys = cast(
"Dict[_Key, None]",
order_preserving_dict.fromkeys(
get_parametrized_fixture_keys(item, scopenum), None
),
) )
if keys: if keys:
d[item] = keys d[item] = keys
for key in keys: for key in keys:
item_d[key].append(item) item_d[key].append(item)
items = order_preserving_dict.fromkeys(items) # cast is a workaround for https://github.com/python/typeshed/issues/3800.
return list(reorder_items_atscope(items, argkeys_cache, items_by_argkey, 0)) items_dict = cast(
"Dict[nodes.Item, None]", order_preserving_dict.fromkeys(items, None)
)
return list(reorder_items_atscope(items_dict, argkeys_cache, items_by_argkey, 0))
def fix_cache_order(item, argkeys_cache, items_by_argkey) -> None: def fix_cache_order(
item: "nodes.Item",
argkeys_cache: "Dict[int, Dict[nodes.Item, Dict[_Key, None]]]",
items_by_argkey: "Dict[int, Dict[_Key, Deque[nodes.Item]]]",
) -> None:
for scopenum in range(0, scopenum_function): for scopenum in range(0, scopenum_function):
for key in argkeys_cache[scopenum].get(item, []): for key in argkeys_cache[scopenum].get(item, []):
items_by_argkey[scopenum][key].appendleft(item) items_by_argkey[scopenum][key].appendleft(item)
def reorder_items_atscope(items, argkeys_cache, items_by_argkey, scopenum): def reorder_items_atscope(
items: "Dict[nodes.Item, None]",
argkeys_cache: "Dict[int, Dict[nodes.Item, Dict[_Key, None]]]",
items_by_argkey: "Dict[int, Dict[_Key, Deque[nodes.Item]]]",
scopenum: int,
) -> "Dict[nodes.Item, None]":
if scopenum >= scopenum_function or len(items) < 3: if scopenum >= scopenum_function or len(items) < 3:
return items return items
ignore = set() ignore = set() # type: Set[Optional[_Key]]
items_deque = deque(items) items_deque = deque(items)
items_done = order_preserving_dict() items_done = order_preserving_dict() # type: Dict[nodes.Item, None]
scoped_items_by_argkey = items_by_argkey[scopenum] scoped_items_by_argkey = items_by_argkey[scopenum]
scoped_argkeys_cache = argkeys_cache[scopenum] scoped_argkeys_cache = argkeys_cache[scopenum]
while items_deque: while items_deque:
no_argkey_group = order_preserving_dict() no_argkey_group = order_preserving_dict() # type: Dict[nodes.Item, None]
slicing_argkey = None slicing_argkey = None
while items_deque: while items_deque:
item = items_deque.popleft() item = items_deque.popleft()
if item in items_done or item in no_argkey_group: if item in items_done or item in no_argkey_group:
continue continue
argkeys = order_preserving_dict.fromkeys( argkeys = order_preserving_dict.fromkeys(
k for k in scoped_argkeys_cache.get(item, []) if k not in ignore (k for k in scoped_argkeys_cache.get(item, []) if k not in ignore), None
) )
if not argkeys: if not argkeys:
no_argkey_group[item] = None no_argkey_group[item] = None