Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions doc/en/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
37 changes: 33 additions & 4 deletions src/_pytest/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,15 +154,17 @@ 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.
pluginmanager.consider_preparse(args)

for spec in default_plugins:
pluginmanager.import_plugin(spec)
return config
return pluginmanager.config


def get_plugin_manager():
Expand Down Expand Up @@ -246,6 +248,20 @@ def __init__(self):
# Used to know when we are importing conftests after the pytest_configure stage
self._configured = False

def is_blocked(self, name):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be a bummer to write one, but I guess we should add a functional test for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, tests are fine.. :D

ret = super(PytestPluginManager, self).is_blocked(name)
if not ret:
try:
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
return ret

def addhooks(self, module_or_class):
"""
.. deprecated:: 2.8
Expand Down Expand Up @@ -334,6 +350,14 @@ 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",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use type="args" here to force this to always be a list.

default=notset,
type="args",
)

def pytest_configure(self, config):
# XXX now that the pluginmanager exposes hookimpl(tryfirst...)
# we should remove tryfirst/trylast as markers
Expand Down Expand Up @@ -792,6 +816,10 @@ 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):
return

if os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"):
# We don't autoload from setuptools entry points, no need to continue.
return
Expand All @@ -810,7 +838,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."""
Expand Down Expand Up @@ -944,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:
Expand Down
1 change: 1 addition & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down