From f3967333a145d8a793a0ab53ac5e0cb0b6c87cac Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 24 Jan 2020 11:43:14 +0200 Subject: [PATCH] Split Session._collection_node_cache to 3 mutually exclusive parts Previously, this cache was used with 3 different and mutually exclusive key-type -> value-type combinations. Mypy can't properly type this. It's also quite confusing. Split to 3 different dicts instead. --- src/_pytest/main.py | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 5a0de75c7..a0cae18ed 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -8,6 +8,7 @@ import sys from typing import Dict from typing import FrozenSet from typing import List +from typing import Sequence from typing import Tuple import attr @@ -21,13 +22,15 @@ from _pytest.config import directory_arg from _pytest.config import hookimpl from _pytest.config import UsageError from _pytest.fixtures import FixtureManager -from _pytest.nodes import Node from _pytest.outcomes import exit +from _pytest.reports import CollectReport from _pytest.runner import collect_one_node from _pytest.runner import SetupState if TYPE_CHECKING: + from typing import Type + from _pytest.python import Package @@ -407,7 +410,16 @@ class Session(nodes.FSCollector): self._initialpaths = frozenset() # type: FrozenSet[py.path.local] # Keep track of any collected nodes in here, so we don't duplicate fixtures - self._collection_node_cache = {} # type: Dict[str, List[Node]] + self._collection_node_cache1 = ( + {} + ) # type: Dict[py.path.local, Sequence[nodes.Collector]] + self._collection_node_cache2 = ( + {} + ) # type: Dict[Tuple[Type[nodes.Collector], py.path.local], nodes.Collector] + self._collection_node_cache3 = ( + {} + ) # type: Dict[Tuple[Type[nodes.Collector], str], CollectReport] + # Dirnames of pkgs with dunder-init files. self._collection_pkg_roots = {} # type: Dict[py.path.local, Package] @@ -525,7 +537,9 @@ class Session(nodes.FSCollector): self._notfound.append((report_arg, sys.exc_info()[1])) self.trace.root.indent -= 1 - self._collection_node_cache.clear() + self._collection_node_cache1.clear() + self._collection_node_cache2.clear() + self._collection_node_cache3.clear() self._collection_pkg_roots.clear() def _collect(self, argpath, names): @@ -543,13 +557,13 @@ class Session(nodes.FSCollector): if parent.isdir(): pkginit = parent.join("__init__.py") if pkginit.isfile(): - if pkginit not in self._collection_node_cache: + if pkginit not in self._collection_node_cache1: col = self._collectfile(pkginit, handle_dupes=False) if col: if isinstance(col[0], Package): self._collection_pkg_roots[parent] = col[0] # always store a list in the cache, matchnodes expects it - self._collection_node_cache[col[0].fspath] = [col[0]] + self._collection_node_cache1[col[0].fspath] = [col[0]] # If it's a directory argument, recurse and look for any Subpackages. # Let the Package collector deal with subnodes, don't collect here. @@ -576,21 +590,21 @@ class Session(nodes.FSCollector): for x in self._collectfile(path): key = (type(x), x.fspath) - if key in self._collection_node_cache: - yield self._collection_node_cache[key] + if key in self._collection_node_cache2: + yield self._collection_node_cache2[key] else: - self._collection_node_cache[key] = x + self._collection_node_cache2[key] = x yield x else: assert argpath.check(file=1) - if argpath in self._collection_node_cache: - col = self._collection_node_cache[argpath] + if argpath in self._collection_node_cache1: + col = self._collection_node_cache1[argpath] else: collect_root = self._collection_pkg_roots.get(argpath.dirname, self) col = collect_root._collectfile(argpath, handle_dupes=False) if col: - self._collection_node_cache[argpath] = col + self._collection_node_cache1[argpath] = col m = self.matchnodes(col, names) # If __init__.py was the only file requested, then the matched node will be # the corresponding Package, and the first yielded item will be the __init__ @@ -703,11 +717,11 @@ class Session(nodes.FSCollector): continue assert isinstance(node, nodes.Collector) key = (type(node), node.nodeid) - if key in self._collection_node_cache: - rep = self._collection_node_cache[key] + if key in self._collection_node_cache3: + rep = self._collection_node_cache3[key] else: rep = collect_one_node(node) - self._collection_node_cache[key] = rep + self._collection_node_cache3[key] = rep if rep.passed: has_matched = False for x in rep.result: