diff --git a/_pytest/main.py b/_pytest/main.py index 452079762..002dc59a7 100644 --- a/_pytest/main.py +++ b/_pytest/main.py @@ -251,6 +251,24 @@ class Node(object): def teardown(self): pass + def _memoizedcall(self, attrname, function): + exattrname = "_ex_" + attrname + failure = getattr(self, exattrname, None) + if failure is not None: + py.builtin._reraise(failure[0], failure[1], failure[2]) + if hasattr(self, attrname): + return getattr(self, attrname) + try: + res = function() + except py.builtin._sysex: + raise + except: + failure = py.std.sys.exc_info() + setattr(self, exattrname, failure) + raise + setattr(self, attrname, res) + return res + def listchain(self): """ return list of all parent collectors up to self, starting from root of collection tree. """ @@ -327,6 +345,10 @@ class Collector(Node): return str(exc.args[0]) return self._repr_failure_py(excinfo, style="short") + def _memocollect(self): + """ internal helper method to cache results of calling collect(). """ + return self._memoizedcall('_collected', lambda: list(self.collect())) + def _prunetraceback(self, excinfo): if hasattr(self, 'fspath'): path = self.fspath diff --git a/_pytest/python.py b/_pytest/python.py index 3276f16ae..b2ad05f8a 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -332,12 +332,8 @@ def transfer_markers(funcobj, cls, mod): class Module(pytest.File, PyCollector): """ Collector for test classes and functions. """ - _obj = None - def _getobj(self): - if self._obj is None: - self._obj = self._importtestmodule() - return _obj + return self._memoizedcall('_obj', self._importtestmodule) def collect(self): self.session.funcargmanager._parsefactories(self.obj, self.nodeid) diff --git a/_pytest/runner.py b/_pytest/runner.py index fb8366090..c774835de 100644 --- a/_pytest/runner.py +++ b/_pytest/runner.py @@ -244,7 +244,7 @@ class TeardownErrorReport(BaseReport): self.__dict__.update(extra) def pytest_make_collect_report(collector): - call = CallInfo(lambda: list(collector.collect()), "collect") + call = CallInfo(collector._memocollect, "memocollect") longrepr = None if not call.excinfo: outcome = "passed"