diff --git a/AUTHORS b/AUTHORS index 49440194e5c..3b0448371e0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -207,6 +207,7 @@ Tzu-ping Chung Vasily Kuznetsov Victor Uriarte Vidar T. Fauske +Virgil Dupras Vitaly Lashmanov Vlad Dragos Wil Cooley diff --git a/changelog/3784.feature.rst b/changelog/3784.feature.rst new file mode 100644 index 00000000000..87b6cafb6a1 --- /dev/null +++ b/changelog/3784.feature.rst @@ -0,0 +1 @@ +Add option to disable plugin auto-loading. diff --git a/doc/en/reference.rst b/doc/en/reference.rst index 86d92cf07bf..6e235e17634 100644 --- a/doc/en/reference.rst +++ b/doc/en/reference.rst @@ -866,6 +866,11 @@ Contains comma-separated list of modules that should be loaded as plugins: export PYTEST_PLUGINS=mymodule.plugin,xdist +PYTEST_DISABLE_PLUGIN_AUTOLOAD +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When set, disables plugin auto-loading through setuptools entrypoints. Only explicitly specified plugins will be +loaded. PYTEST_CURRENT_TEST ~~~~~~~~~~~~~~~~~~~ diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 421d124e9a9..3b2d622c357 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -701,6 +701,10 @@ def _mark_plugins_for_rewrite(self, hook): self.pluginmanager.rewrite_hook = hook + if os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"): + # We don't autoload from setuptools entry points, no need to continue. + return + # 'RECORD' available for plugins installed normally (pip install) # 'SOURCES.txt' available for plugins installed in dev mode (pip install -e) # for installed plugins 'SOURCES.txt' returns an empty list, and vice-versa @@ -726,7 +730,10 @@ def _preparse(self, args, addopts=True): self._checkversion() self._consider_importhook(args) self.pluginmanager.consider_preparse(args) - self.pluginmanager.load_setuptools_entrypoints("pytest11") + if not os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"): + # Don't autoload from setuptools entry point. Only explicitly specified + # plugins are going to be loaded. + self.pluginmanager.load_setuptools_entrypoints("pytest11") self.pluginmanager.consider_env() self.known_args_namespace = ns = self._parser.parse_known_args( args, namespace=copy.copy(self.option) diff --git a/src/_pytest/helpconfig.py b/src/_pytest/helpconfig.py index 12c3339c647..85f071e9e3e 100644 --- a/src/_pytest/helpconfig.py +++ b/src/_pytest/helpconfig.py @@ -156,6 +156,7 @@ def showhelp(config): vars = [ ("PYTEST_ADDOPTS", "extra command line options"), ("PYTEST_PLUGINS", "comma-separated plugins to load during startup"), + ("PYTEST_DISABLE_PLUGIN_AUTOLOAD", "set to disable plugin auto-loading"), ("PYTEST_DEBUG", "set to enable debug tracing of pytest's internals"), ] for name, help in vars: diff --git a/testing/test_config.py b/testing/test_config.py index b507bb8e823..d58bda255cf 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -605,6 +605,26 @@ def load(self): ) +@pytest.mark.parametrize( + "parse_args,should_load", [(("-p", "mytestplugin"), True), ((), False)] +) +def test_disable_plugin_autoload(testdir, monkeypatch, parse_args, should_load): + pkg_resources = pytest.importorskip("pkg_resources") + + def my_iter(name): + raise AssertionError("Should not be called") + + class PseudoPlugin(object): + x = 42 + + monkeypatch.setenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", "1") + monkeypatch.setattr(pkg_resources, "iter_entry_points", my_iter) + monkeypatch.setitem(sys.modules, "mytestplugin", PseudoPlugin()) + config = testdir.parseconfig(*parse_args) + has_loaded = config.pluginmanager.get_plugin("mytestplugin") is not None + assert has_loaded == should_load + + def test_cmdline_processargs_simple(testdir): testdir.makeconftest( """