diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 3c908ec4c..d626816a8 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -9,6 +9,7 @@ import os import pkgutil import sys +import attr import py import six @@ -369,6 +370,16 @@ class Failed(Exception): """ signals a stop as failed test run. """ +@attr.s +class _bestrelpath_cache(dict): + path = attr.ib() + + def __missing__(self, path): + r = self.path.bestrelpath(path) + self[path] = r + return r + + class Session(nodes.FSCollector): Interrupted = Interrupted Failed = Failed @@ -387,9 +398,14 @@ class Session(nodes.FSCollector): self._initialpaths = frozenset() # Keep track of any collected nodes in here, so we don't duplicate fixtures self._node_cache = {} + self._bestrelpathcache = _bestrelpath_cache(config.rootdir) self.config.pluginmanager.register(self, name="session") + def _node_location_to_relpath(self, node_path): + # bestrelpath is a quite slow function + return self._bestrelpathcache[node_path] + @hookimpl(tryfirst=True) def pytest_collectstart(self): if self.shouldfail: diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 76d8d6a8a..0d65dc965 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -523,13 +523,7 @@ class Item(Node): return self._location except AttributeError: location = self.reportinfo() - # bestrelpath is a quite slow function - cache = self.config.__dict__.setdefault("_bestrelpathcache", {}) - try: - fspath = cache[location[0]] - except KeyError: - fspath = self.session.fspath.bestrelpath(location[0]) - cache[location[0]] = fspath + fspath = self.session._node_location_to_relpath(location[0]) location = (fspath, location[1], str(location[2])) self._location = location return location