Skip to content

Commit

Permalink
Build a stack of all previous packages instead of just the one closes…
Browse files Browse the repository at this point in the history
…t to the initial argument(s).

Address #3358 by caching nodes in a session dict.
  • Loading branch information
turturica authored and turturica committed Apr 22, 2018
1 parent b047439 commit fedc785
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 14 deletions.
59 changes: 46 additions & 13 deletions _pytest/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,8 @@ def __init__(self, config):
self.trace = config.trace.root.get("collection")
self._norecursepatterns = config.getini("norecursedirs")
self.startdir = py.path.local()
# Keep track of any collected nodes in here, so we don't duplicate fixtures
self._node_cache = {}

self.config.pluginmanager.register(self, name="session")

Expand Down Expand Up @@ -407,31 +409,58 @@ def _collect(self, arg):
names = self._parsearg(arg)
argpath = names.pop(0)
paths = []

root = self
# Start with a Session root, and delve to argpath item (dir or file)
# and stack all Packages found on the way.
# No point in finding packages when collecting doctests
if not self.config.option.doctestmodules:
for parent in argpath.parts():
pm = self.config.pluginmanager
if pm._confcutdir and pm._confcutdir.relto(parent):
continue

if parent.isdir():
pkginit = parent.join("__init__.py")
if pkginit.isfile():
if pkginit in self._node_cache:
root = self._node_cache[pkginit]
else:
col = root._collectfile(pkginit)
if col:
root = col[0]
self._node_cache[root.fspath] = root

# If it's a directory argument, recurse and look for any Subpackages.
# Let the Package collector deal with subnodes, don't collect here.
if argpath.check(dir=1):
assert not names, "invalid arg %r" % (arg,)
for path in argpath.visit(fil=lambda x: x.check(file=1),
rec=self._recurse, bf=True, sort=True):
pkginit = path.dirpath().join('__init__.py')
if pkginit.exists() and not any(x in pkginit.parts() for x in paths):
for x in self._collectfile(pkginit):
for x in root._collectfile(pkginit):
yield x
paths.append(x.fspath.dirpath())

if not any(x in path.parts() for x in paths):
for x in self._collectfile(path):
yield x
for x in root._collectfile(path):
if (type(x), x.fspath) in self._node_cache:
yield self._node_cache[(type(x), x.fspath)]
else:
yield x
self._node_cache[(type(x), x.fspath)] = x
else:
assert argpath.check(file=1)
pkginit = argpath.dirpath().join('__init__.py')
if not self.isinitpath(pkginit):
self._initialpaths.add(pkginit)
if pkginit.exists():
for x in self._collectfile(pkginit):
for y in self.matchnodes(x._collectfile(argpath), names):
yield y

if argpath in self._node_cache:
col = self._node_cache[argpath]
else:
for x in self.matchnodes(self._collectfile(argpath), names):
yield x
col = root._collectfile(argpath)
if col:
self._node_cache[argpath] = col
for y in self.matchnodes(col, names):
yield y

def _collectfile(self, path):
ihook = self.gethookproxy(path)
Expand Down Expand Up @@ -516,7 +545,11 @@ def _matchnodes(self, matching, names):
resultnodes.append(node)
continue
assert isinstance(node, nodes.Collector)
rep = collect_one_node(node)
if node.nodeid in self._node_cache:
rep = self._node_cache[node.nodeid]
else:
rep = collect_one_node(node)
self._node_cache[node.nodeid] = rep
if rep.passed:
has_matched = False
for x in rep.result:
Expand Down
2 changes: 1 addition & 1 deletion testing/test_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ class TestY(TestX):
started = reprec.getcalls("pytest_collectstart")
finished = reprec.getreports("pytest_collectreport")
assert len(started) == len(finished)
assert len(started) == 8 # XXX extra TopCollector
assert len(started) == 7 # XXX extra TopCollector
colfail = [x for x in finished if x.failed]
assert len(colfail) == 1

Expand Down

0 comments on commit fedc785

Please sign in to comment.