From bff3878ea32845d77b7d9e6b5d40729ab79a7f5b Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Sun, 9 Dec 2018 12:56:11 +0100 Subject: [PATCH 1/5] Add load_entrypoint_plugins ini option/marker --- src/_pytest/config/__init__.py | 37 +++++++++++++++++++++++++++++++--- tox.ini | 1 + 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 4542f06ab7c..fca895e5747 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -154,7 +154,9 @@ def directory_arg(path, optname): def get_config(args=None): # subsequent calls to main will create a fresh instance pluginmanager = PytestPluginManager() - config = Config(pluginmanager) + # XXX: storing it on the instance is required to make it available in + # is_blocked, which is the callback used with load_entrypoint_plugins. + pluginmanager.config = Config(pluginmanager) if args is not None: # Handle any "-p no:plugin" args. @@ -162,7 +164,7 @@ def get_config(args=None): for spec in default_plugins: pluginmanager.import_plugin(spec) - return config + return pluginmanager.config def get_plugin_manager(): @@ -246,6 +248,19 @@ def __init__(self): # Used to know when we are importing conftests after the pytest_configure stage self._configured = False + def is_blocked(self, name): + ret = super(PytestPluginManager, self).is_blocked(name) + if not ret: + try: + load_entrypoint_plugins = self.config.getini("load_entrypoint_plugins") + except (AttributeError, ValueError): + pass + else: + if load_entrypoint_plugins is not notset: + if name not in load_entrypoint_plugins: + return True + return ret + def addhooks(self, module_or_class): """ .. deprecated:: 2.8 @@ -334,6 +349,13 @@ def hasplugin(self, name): """Return True if the plugin with the given name is registered.""" return bool(self.get_plugin(name)) + def pytest_addoption(self, parser): + parser.addini( + "load_entrypoint_plugins", + help="only load specified plugins via entrypoint", + default=notset, + ) + def pytest_configure(self, config): # XXX now that the pluginmanager exposes hookimpl(tryfirst...) # we should remove tryfirst/trylast as markers @@ -347,6 +369,9 @@ def pytest_configure(self, config): "trylast: mark a hook implementation function such that the " "plugin machinery will try to call it last/as late as possible.", ) + config.addinivalue_line( + "markers", "load_entrypoint_plugins: only load the given entrypoint plugins" + ) self._configured = True # @@ -792,6 +817,11 @@ def _mark_plugins_for_rewrite(self, hook): self.pluginmanager.rewrite_hook = hook + load_entrypoint_plugins = self.getini("load_entrypoint_plugins") + if load_entrypoint_plugins is not notset and not len(load_entrypoint_plugins): + assert isinstance(load_entrypoint_plugins, list) + return + if os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"): # We don't autoload from setuptools entry points, no need to continue. return @@ -810,7 +840,8 @@ def _mark_plugins_for_rewrite(self, hook): ) for name in _iter_rewritable_modules(package_files): - hook.mark_rewrite(name) + if load_entrypoint_plugins is notset or name in load_entrypoint_plugins: + hook.mark_rewrite(name) def _validate_args(self, args, via): """Validate known args.""" diff --git a/tox.ini b/tox.ini index 16984dd43c8..8c7bd3c8989 100644 --- a/tox.ini +++ b/tox.ini @@ -140,6 +140,7 @@ commands = python scripts/release.py {posargs} [pytest] minversion = 2.0 addopts = -ra -p pytester +load_entrypoint_plugins=pytester xdist rsyncdirs = tox.ini doc src testing python_files = test_*.py *_test.py testing/*/*.py python_classes = Test Acceptance From 0407d8631d36b8e6d26342ac2db793313a0992a8 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Sun, 9 Dec 2018 14:01:18 +0100 Subject: [PATCH 2/5] remove marker: option should be enough, and marker should be with pytester anyway --- src/_pytest/config/__init__.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index fca895e5747..10c51308e43 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -369,9 +369,6 @@ def pytest_configure(self, config): "trylast: mark a hook implementation function such that the " "plugin machinery will try to call it last/as late as possible.", ) - config.addinivalue_line( - "markers", "load_entrypoint_plugins: only load the given entrypoint plugins" - ) self._configured = True # From 5c672c513c23c67f15de88ef77475ee7b33c7abd Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Wed, 13 Mar 2019 23:20:32 +0100 Subject: [PATCH 3/5] type=args --- src/_pytest/config/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 10c51308e43..1e271431077 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -354,6 +354,7 @@ def pytest_addoption(self, parser): "load_entrypoint_plugins", help="only load specified plugins via entrypoint", default=notset, + type="args", ) def pytest_configure(self, config): @@ -816,7 +817,6 @@ def _mark_plugins_for_rewrite(self, hook): load_entrypoint_plugins = self.getini("load_entrypoint_plugins") if load_entrypoint_plugins is not notset and not len(load_entrypoint_plugins): - assert isinstance(load_entrypoint_plugins, list) return if os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"): From 8b0bdab8b946a3c03f56f97689a58c42ab00a4b2 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Thu, 14 Mar 2019 16:55:49 +0100 Subject: [PATCH 4/5] Handle AttributeError in _getini TODO: should it not be called in this state? --- src/_pytest/config/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 1e271431077..69d27ef8661 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -252,10 +252,11 @@ def is_blocked(self, name): ret = super(PytestPluginManager, self).is_blocked(name) if not ret: try: - load_entrypoint_plugins = self.config.getini("load_entrypoint_plugins") - except (AttributeError, ValueError): + config = self.config + except AttributeError: pass else: + load_entrypoint_plugins = config.getini("load_entrypoint_plugins") if load_entrypoint_plugins is not notset: if name not in load_entrypoint_plugins: return True @@ -972,7 +973,7 @@ def _getini(self, name): if value is None: try: value = self.inicfg[name] - except KeyError: + except (AttributeError, KeyError): if default is not None: return default if type is None: From ebd606a8b2aa3aeed46a68dc1004280967f79927 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Fri, 15 Mar 2019 23:50:47 +0100 Subject: [PATCH 5/5] doc --- doc/en/reference.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc/en/reference.rst b/doc/en/reference.rst index 12e0d09a8be..a946660cd84 100644 --- a/doc/en/reference.rst +++ b/doc/en/reference.rst @@ -1103,6 +1103,18 @@ passed multiple times. The expected format is ``name=value``. For example:: [pytest] junit_suite_name = my_suite +.. confval:: load_entrypoint_plugins + + .. versionadded:: 4.4 + + A list of pytest plugins that should be loaded via entrypoints. + + By default all plugins are loaded. An empty list can be used to load none. + + .. code-block:: ini + + [pytest] + load_entrypoint_plugins = pytester,anotherplugin .. confval:: log_cli_date_format