From 9b8039cf094063a80bd13ce32e3f4b48a77fe89c Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Tue, 14 Jan 2020 18:58:02 +0100 Subject: [PATCH 1/5] Sync `{Session,Package}._recurse` --- src/_pytest/main.py | 2 +- src/_pytest/python.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 7ce4c19ea05..283ff29b155 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -625,7 +625,7 @@ def _collectfile(self, path, handle_dupes=True): return ihook.pytest_collect_file(path=path, parent=self) - def _recurse(self, dirpath): + def _recurse(self, dirpath: py.path.local) -> bool: if dirpath.basename == "__pycache__": return False ihook = self.gethookproxy(dirpath.dirpath()) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 82dca3bcc9b..869e452615e 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -571,12 +571,12 @@ def setup(self): func = partial(_call_with_optional_argument, teardown_module, self.obj) self.addfinalizer(func) - def _recurse(self, dirpath): + 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 + return False for pat in self._norecursepatterns: if dirpath.check(fnmatch=pat): return False From 817c094ce62b322ad5a6ccce07ddd0ea3b75710e Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Tue, 14 Jan 2020 17:57:24 +0100 Subject: [PATCH 2/5] Clean up Package.__init__ Makes `parent` a required arg, which would have failed before via `parent.session` anyway. Keeps calling/passing unused args for B/C. --- src/_pytest/python.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 869e452615e..9def7e49e43 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -545,15 +545,23 @@ def _importtestmodule(self): 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 From 6b7e1a246cbaec554166b855c74fdb58cf54c08b Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Tue, 14 Jan 2020 19:10:56 +0100 Subject: [PATCH 3/5] Sync `{Session,Package}.gethookproxy` Only copy'n'paste error from c416b1d935. --- src/_pytest/python.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 9def7e49e43..916ef7bd569 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -594,7 +594,7 @@ def _recurse(self, dirpath: py.path.local) -> bool: def gethookproxy(self, fspath): # check if we have the common case of running - # hooks with all conftest.py filesall conftest.py + # hooks with all conftest.py files pm = self.config.pluginmanager my_conftestmodules = pm._getconftestmodules(fspath) remove_mods = pm._conftest_plugins.difference(my_conftestmodules) From e2934c3f8c03c83469f4c6670c207773a6e02df4 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Tue, 14 Jan 2020 19:15:26 +0100 Subject: [PATCH 4/5] Move common code between Session and Package to FSCollector --- src/_pytest/main.py | 39 +-------------------------------------- src/_pytest/nodes.py | 41 +++++++++++++++++++++++++++++++++++++++++ src/_pytest/python.py | 27 +-------------------------- 3 files changed, 43 insertions(+), 64 deletions(-) diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 283ff29b155..066c885b851 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 @@ def __init__(self, config: Config) -> None: 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] @@ -450,18 +437,7 @@ 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 + return super()._gethookproxy(fspath) def perform_collect(self, args=None, genitems=True): hook = self.config.hook @@ -625,19 +601,6 @@ def _collectfile(self, path, handle_dupes=True): return ihook.pytest_collect_file(path=path, parent=self) - 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 - @staticmethod def _visit_filter(f): return f.check(file=1) diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index ab976efae72..f9f1f4f6877 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -393,6 +393,18 @@ def _check_initialpaths_for_relpath(session, fspath): return fspath.relto(initial_path) +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 FSCollector(Collector): def __init__( self, fspath: py.path.local, parent=None, config=None, session=None, nodeid=None @@ -417,6 +429,35 @@ def __init__( super().__init__(name, parent, config, session, nodeid=nodeid, fspath=fspath) + self._norecursepatterns = self.config.getini("norecursedirs") + + 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 _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 916ef7bd569..23a67d023fb 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -35,7 +35,6 @@ 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 @@ -579,32 +578,8 @@ def setup(self): func = partial(_call_with_optional_argument, teardown_module, self.obj) self.addfinalizer(func) - 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 - 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 + return super()._gethookproxy(fspath) def _collectfile(self, path, handle_dupes=True): assert ( From ae5d16be10c139da6a21eab34f7decbf93af721b Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Mon, 27 Jan 2020 20:57:32 +0100 Subject: [PATCH 5/5] typing: FSHookProxy/gethookproxy Taken out of https://github.com/pytest-dev/pytest/pull/6556. --- src/_pytest/main.py | 2 +- src/_pytest/nodes.py | 9 ++++++--- src/_pytest/python.py | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 066c885b851..e4eb4bbc813 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -436,7 +436,7 @@ def pytest_runtest_logreport(self, report): def isinitpath(self, path): return path in self._initialpaths - def gethookproxy(self, fspath): + def gethookproxy(self, fspath: py.path.local): return super()._gethookproxy(fspath) def perform_collect(self, args=None, genitems=True): diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index f9f1f4f6877..5447f254173 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -19,6 +19,7 @@ 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 @@ -394,12 +395,14 @@ def _check_initialpaths_for_relpath(session, fspath): class FSHookProxy: - def __init__(self, fspath, pm, remove_mods): + 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): + def __getattr__(self, name: str): x = self.pm.subset_hook_caller(name, remove_plugins=self.remove_mods) self.__dict__[name] = x return x @@ -431,7 +434,7 @@ def __init__( self._norecursepatterns = self.config.getini("norecursedirs") - def _gethookproxy(self, fspath): + 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 diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 23a67d023fb..c30dbc47704 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -578,7 +578,7 @@ def setup(self): func = partial(_call_with_optional_argument, teardown_module, self.obj) self.addfinalizer(func) - def gethookproxy(self, fspath): + def gethookproxy(self, fspath: py.path.local): return super()._gethookproxy(fspath) def _collectfile(self, path, handle_dupes=True):