diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 7ce4c19ea..e4eb4bbc8 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -348,18 +348,6 @@ def pytest_collection_modifyitems(items, config): items[:] = remaining -class FSHookProxy: - def __init__(self, fspath, pm, remove_mods): - self.fspath = fspath - self.pm = pm - self.remove_mods = remove_mods - - def __getattr__(self, name): - x = self.pm.subset_hook_caller(name, remove_plugins=self.remove_mods) - self.__dict__[name] = x - return x - - class NoMatch(Exception): """ raised if matching cannot locate a matching names. """ @@ -401,7 +389,6 @@ class Session(nodes.FSCollector): self.shouldstop = False self.shouldfail = False self.trace = config.trace.root.get("collection") - self._norecursepatterns = config.getini("norecursedirs") self.startdir = config.invocation_dir self._initialpaths = frozenset() # type: FrozenSet[py.path.local] @@ -449,19 +436,8 @@ class Session(nodes.FSCollector): def isinitpath(self, path): return path in self._initialpaths - def gethookproxy(self, fspath): - # check if we have the common case of running - # hooks with all conftest.py files - pm = self.config.pluginmanager - my_conftestmodules = pm._getconftestmodules(fspath) - remove_mods = pm._conftest_plugins.difference(my_conftestmodules) - if remove_mods: - # one or more conftests are not in use at this fspath - proxy = FSHookProxy(fspath, pm, remove_mods) - else: - # all plugins are active for this fspath - proxy = self.config.hook - return proxy + def gethookproxy(self, fspath: py.path.local): + return super()._gethookproxy(fspath) def perform_collect(self, args=None, genitems=True): hook = self.config.hook @@ -625,19 +601,6 @@ class Session(nodes.FSCollector): return ihook.pytest_collect_file(path=path, parent=self) - def _recurse(self, dirpath): - if dirpath.basename == "__pycache__": - return False - ihook = self.gethookproxy(dirpath.dirpath()) - if ihook.pytest_ignore_collect(path=dirpath, config=self.config): - return False - for pat in self._norecursepatterns: - if dirpath.check(fnmatch=pat): - return False - ihook = self.gethookproxy(dirpath) - ihook.pytest_collect_directory(path=dirpath, parent=self) - return True - @staticmethod def _visit_filter(f): return f.check(file=1) diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index ab976efae..5447f2541 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -19,6 +19,7 @@ from _pytest.compat import cached_property from _pytest.compat import getfslineno from _pytest.compat import TYPE_CHECKING from _pytest.config import Config +from _pytest.config import PytestPluginManager from _pytest.fixtures import FixtureDef from _pytest.fixtures import FixtureLookupError from _pytest.fixtures import FixtureLookupErrorRepr @@ -393,6 +394,20 @@ def _check_initialpaths_for_relpath(session, fspath): return fspath.relto(initial_path) +class FSHookProxy: + def __init__( + self, fspath: py.path.local, pm: PytestPluginManager, remove_mods + ) -> None: + self.fspath = fspath + self.pm = pm + self.remove_mods = remove_mods + + def __getattr__(self, name: str): + x = self.pm.subset_hook_caller(name, remove_plugins=self.remove_mods) + self.__dict__[name] = x + return x + + class FSCollector(Collector): def __init__( self, fspath: py.path.local, parent=None, config=None, session=None, nodeid=None @@ -417,6 +432,35 @@ class FSCollector(Collector): super().__init__(name, parent, config, session, nodeid=nodeid, fspath=fspath) + self._norecursepatterns = self.config.getini("norecursedirs") + + def _gethookproxy(self, fspath: py.path.local): + # check if we have the common case of running + # hooks with all conftest.py files + pm = self.config.pluginmanager + my_conftestmodules = pm._getconftestmodules(fspath) + remove_mods = pm._conftest_plugins.difference(my_conftestmodules) + if remove_mods: + # one or more conftests are not in use at this fspath + proxy = FSHookProxy(fspath, pm, remove_mods) + else: + # all plugins are active for this fspath + proxy = self.config.hook + return proxy + + def _recurse(self, dirpath: py.path.local) -> bool: + if dirpath.basename == "__pycache__": + return False + ihook = self._gethookproxy(dirpath.dirpath()) + if ihook.pytest_ignore_collect(path=dirpath, config=self.config): + return False + for pat in self._norecursepatterns: + if dirpath.check(fnmatch=pat): + return False + ihook = self._gethookproxy(dirpath) + ihook.pytest_collect_directory(path=dirpath, parent=self) + return True + class File(FSCollector): """ base class for collecting tests from a file. """ diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 82dca3bcc..c30dbc477 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -35,7 +35,6 @@ from _pytest.compat import safe_isclass from _pytest.compat import STRING_TYPES from _pytest.config import hookimpl from _pytest.deprecated import FUNCARGNAMES -from _pytest.main import FSHookProxy from _pytest.mark import MARK_GEN from _pytest.mark.structures import get_unpacked_marks from _pytest.mark.structures import normalize_mark_list @@ -545,15 +544,23 @@ class Module(nodes.File, PyCollector): class Package(Module): - def __init__(self, fspath, parent=None, config=None, session=None, nodeid=None): + def __init__( + self, + fspath: py.path.local, + parent: nodes.Collector, + # NOTE: following args are unused: + config=None, + session=None, + nodeid=None, + ) -> None: + # NOTE: could be just the following, but kept as-is for compat. + # nodes.FSCollector.__init__(self, fspath, parent=parent) session = parent.session nodes.FSCollector.__init__( self, fspath, parent=parent, config=config, session=session, nodeid=nodeid ) + self.name = fspath.dirname - self.trace = session.trace - self._norecursepatterns = session._norecursepatterns - self.fspath = fspath def setup(self): # not using fixtures to call setup_module here because autouse fixtures @@ -571,32 +578,8 @@ class Package(Module): func = partial(_call_with_optional_argument, teardown_module, self.obj) self.addfinalizer(func) - def _recurse(self, dirpath): - if dirpath.basename == "__pycache__": - return False - ihook = self.gethookproxy(dirpath.dirpath()) - if ihook.pytest_ignore_collect(path=dirpath, config=self.config): - return - for pat in self._norecursepatterns: - if dirpath.check(fnmatch=pat): - return False - ihook = self.gethookproxy(dirpath) - ihook.pytest_collect_directory(path=dirpath, parent=self) - return True - - def gethookproxy(self, fspath): - # check if we have the common case of running - # hooks with all conftest.py filesall conftest.py - pm = self.config.pluginmanager - my_conftestmodules = pm._getconftestmodules(fspath) - remove_mods = pm._conftest_plugins.difference(my_conftestmodules) - if remove_mods: - # one or more conftests are not in use at this fspath - proxy = FSHookProxy(fspath, pm, remove_mods) - else: - # all plugins are active for this fspath - proxy = self.config.hook - return proxy + def gethookproxy(self, fspath: py.path.local): + return super()._gethookproxy(fspath) def _collectfile(self, path, handle_dupes=True): assert (