Clear collection caches after collection is done

Also rename the involved variables to convey its intent better and
add type hints
This commit is contained in:
Bruno Oliveira 2020-01-17 09:02:12 -03:00
parent 2f0d0fb349
commit 7b1e3d1c9a
1 changed files with 32 additions and 20 deletions

View File

@ -6,21 +6,29 @@ import importlib
import os import os
import sys import sys
from typing import Dict from typing import Dict
from typing import FrozenSet
from typing import List
import attr import attr
import py import py
import _pytest._code import _pytest._code
from _pytest import nodes from _pytest import nodes
from _pytest.compat import TYPE_CHECKING
from _pytest.config import directory_arg from _pytest.config import directory_arg
from _pytest.config import hookimpl from _pytest.config import hookimpl
from _pytest.config import UsageError from _pytest.config import UsageError
from _pytest.fixtures import FixtureManager from _pytest.fixtures import FixtureManager
from _pytest.nodes import Node
from _pytest.outcomes import exit from _pytest.outcomes import exit
from _pytest.runner import collect_one_node from _pytest.runner import collect_one_node
from _pytest.runner import SetupState from _pytest.runner import SetupState
if TYPE_CHECKING:
from _pytest.python import Package
class ExitCode(enum.IntEnum): class ExitCode(enum.IntEnum):
""" """
.. versionadded:: 5.0 .. versionadded:: 5.0
@ -381,7 +389,7 @@ class Session(nodes.FSCollector):
_setupstate = None # type: SetupState _setupstate = None # type: SetupState
_fixturemanager = None # type: FixtureManager _fixturemanager = None # type: FixtureManager
def __init__(self, config): def __init__(self, config) -> None:
nodes.FSCollector.__init__( nodes.FSCollector.__init__(
self, config.rootdir, parent=None, config=config, session=self, nodeid="" self, config.rootdir, parent=None, config=config, session=self, nodeid=""
) )
@ -392,14 +400,16 @@ class Session(nodes.FSCollector):
self.trace = config.trace.root.get("collection") self.trace = config.trace.root.get("collection")
self._norecursepatterns = config.getini("norecursedirs") self._norecursepatterns = config.getini("norecursedirs")
self.startdir = config.invocation_dir self.startdir = config.invocation_dir
self._initialpaths = frozenset() self._initialpaths = frozenset() # type: FrozenSet[py.path.local]
# Keep track of any collected nodes in here, so we don't duplicate fixtures # Keep track of any collected nodes in here, so we don't duplicate fixtures
self._node_cache = {} self._collection_node_cache = {} # type: Dict[str, List[Node]]
# Dirnames of pkgs with dunder-init files.
self._collection_pkg_roots = {} # type: Dict[py.path.local, Package]
self._bestrelpathcache = _bestrelpath_cache( self._bestrelpathcache = _bestrelpath_cache(
config.rootdir config.rootdir
) # type: Dict[str, str] ) # type: Dict[str, str]
# Dirnames of pkgs with dunder-init files.
self._pkg_roots = {}
self.config.pluginmanager.register(self, name="session") self.config.pluginmanager.register(self, name="session")
@ -511,6 +521,8 @@ class Session(nodes.FSCollector):
self._notfound.append((report_arg, sys.exc_info()[1])) self._notfound.append((report_arg, sys.exc_info()[1]))
self.trace.root.indent -= 1 self.trace.root.indent -= 1
self._collection_node_cache.clear()
self._collection_pkg_roots.clear()
def _collect(self, arg): def _collect(self, arg):
from _pytest.python import Package from _pytest.python import Package
@ -530,13 +542,13 @@ class Session(nodes.FSCollector):
if parent.isdir(): if parent.isdir():
pkginit = parent.join("__init__.py") pkginit = parent.join("__init__.py")
if pkginit.isfile(): if pkginit.isfile():
if pkginit not in self._node_cache: if pkginit not in self._collection_node_cache:
col = self._collectfile(pkginit, handle_dupes=False) col = self._collectfile(pkginit, handle_dupes=False)
if col: if col:
if isinstance(col[0], Package): if isinstance(col[0], Package):
self._pkg_roots[parent] = col[0] self._collection_pkg_roots[parent] = col[0]
# always store a list in the cache, matchnodes expects it # always store a list in the cache, matchnodes expects it
self._node_cache[col[0].fspath] = [col[0]] self._collection_node_cache[col[0].fspath] = [col[0]]
# If it's a directory argument, recurse and look for any Subpackages. # If it's a directory argument, recurse and look for any Subpackages.
# Let the Package collector deal with subnodes, don't collect here. # Let the Package collector deal with subnodes, don't collect here.
@ -556,28 +568,28 @@ class Session(nodes.FSCollector):
for x in self._collectfile(pkginit): for x in self._collectfile(pkginit):
yield x yield x
if isinstance(x, Package): if isinstance(x, Package):
self._pkg_roots[dirpath] = x self._collection_pkg_roots[dirpath] = x
if dirpath in self._pkg_roots: if dirpath in self._collection_pkg_roots:
# Do not collect packages here. # Do not collect packages here.
continue continue
for x in self._collectfile(path): for x in self._collectfile(path):
key = (type(x), x.fspath) key = (type(x), x.fspath)
if key in self._node_cache: if key in self._collection_node_cache:
yield self._node_cache[key] yield self._collection_node_cache[key]
else: else:
self._node_cache[key] = x self._collection_node_cache[key] = x
yield x yield x
else: else:
assert argpath.check(file=1) assert argpath.check(file=1)
if argpath in self._node_cache: if argpath in self._collection_node_cache:
col = self._node_cache[argpath] col = self._collection_node_cache[argpath]
else: else:
collect_root = self._pkg_roots.get(argpath.dirname, self) collect_root = self._collection_pkg_roots.get(argpath.dirname, self)
col = collect_root._collectfile(argpath, handle_dupes=False) col = collect_root._collectfile(argpath, handle_dupes=False)
if col: if col:
self._node_cache[argpath] = col self._collection_node_cache[argpath] = col
m = self.matchnodes(col, names) m = self.matchnodes(col, names)
# If __init__.py was the only file requested, then the matched node will be # 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__ # the corresponding Package, and the first yielded item will be the __init__
@ -690,11 +702,11 @@ class Session(nodes.FSCollector):
continue continue
assert isinstance(node, nodes.Collector) assert isinstance(node, nodes.Collector)
key = (type(node), node.nodeid) key = (type(node), node.nodeid)
if key in self._node_cache: if key in self._collection_node_cache:
rep = self._node_cache[key] rep = self._collection_node_cache[key]
else: else:
rep = collect_one_node(node) rep = collect_one_node(node)
self._node_cache[key] = rep self._collection_node_cache[key] = rep
if rep.passed: if rep.passed:
has_matched = False has_matched = False
for x in rep.result: for x in rep.result: