Merge pull request #7330 from gnikonorov/issue_7305

This commit is contained in:
Bruno Oliveira 2020-06-12 19:35:38 -03:00 committed by GitHub
commit aaa6f1c3fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 106 additions and 7 deletions

View File

@ -0,0 +1 @@
New ``required_plugins`` configuration option allows the user to specify a list of plugins required for pytest to run. An error is raised if any required plugins are not found when running pytest.

View File

@ -1559,6 +1559,17 @@ passed multiple times. The expected format is ``name=value``. For example::
See :ref:`change naming conventions` for more detailed examples.
.. confval:: required_plugins
A space separated list of plugins that must be present for pytest to run.
If any one of the plugins is not found, emit an error.
.. code-block:: ini
[pytest]
required_plugins = pytest-html pytest-xdist
.. confval:: testpaths

View File

@ -951,6 +951,12 @@ class Config:
self._parser.extra_info["inifile"] = self.inifile
self._parser.addini("addopts", "extra command line options", "args")
self._parser.addini("minversion", "minimally required pytest version")
self._parser.addini(
"required_plugins",
"plugins that must be present for pytest to run",
type="args",
default=[],
)
self._override_ini = ns.override_ini or ()
def _consider_importhook(self, args: Sequence[str]) -> None:
@ -1034,7 +1040,8 @@ class Config:
self.known_args_namespace = ns = self._parser.parse_known_args(
args, namespace=copy.copy(self.option)
)
self._validatekeys()
self._validate_plugins()
self._validate_keys()
if self.known_args_namespace.confcutdir is None and self.inifile:
confcutdir = py.path.local(self.inifile).dirname
self.known_args_namespace.confcutdir = confcutdir
@ -1072,9 +1079,30 @@ class Config:
% (self.inifile, minver, pytest.__version__,)
)
def _validatekeys(self):
def _validate_keys(self) -> None:
for key in sorted(self._get_unknown_ini_keys()):
message = "Unknown config ini key: {}\n".format(key)
self._warn_or_fail_if_strict("Unknown config ini key: {}\n".format(key))
def _validate_plugins(self) -> None:
required_plugins = sorted(self.getini("required_plugins"))
if not required_plugins:
return
plugin_info = self.pluginmanager.list_plugin_distinfo()
plugin_dist_names = [dist.project_name for _, dist in plugin_info]
missing_plugins = []
for plugin in required_plugins:
if plugin not in plugin_dist_names:
missing_plugins.append(plugin)
if missing_plugins:
fail(
"Missing required plugins: {}".format(", ".join(missing_plugins)),
pytrace=False,
)
def _warn_or_fail_if_strict(self, message: str) -> None:
if self.known_args_namespace.strict_config:
fail(message, pytrace=False)
sys.stderr.write("WARNING: {}".format(message))

View File

@ -80,7 +80,7 @@ def pytest_addoption(parser: Parser) -> None:
group._addoption(
"--strict-config",
action="store_true",
help="invalid ini keys for the `pytest` section of the configuration file raise errors.",
help="any warnings encountered while parsing the `pytest` section of the configuration file raise errors.",
)
group._addoption(
"--strict-markers",

View File

@ -224,6 +224,60 @@ class TestParseIni:
with pytest.raises(pytest.fail.Exception, match=exception_text):
testdir.runpytest("--strict-config")
@pytest.mark.parametrize(
"ini_file_text, exception_text",
[
(
"""
[pytest]
required_plugins = fakePlugin1 fakePlugin2
""",
"Missing required plugins: fakePlugin1, fakePlugin2",
),
(
"""
[pytest]
required_plugins = a pytest-xdist z
""",
"Missing required plugins: a, z",
),
(
"""
[pytest]
required_plugins = a q j b c z
""",
"Missing required plugins: a, b, c, j, q, z",
),
(
"""
[some_other_header]
required_plugins = wont be triggered
[pytest]
minversion = 5.0.0
""",
"",
),
(
"""
[pytest]
minversion = 5.0.0
""",
"",
),
],
)
def test_missing_required_plugins(self, testdir, ini_file_text, exception_text):
pytest.importorskip("xdist")
testdir.tmpdir.join("pytest.ini").write(textwrap.dedent(ini_file_text))
testdir.monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD")
if exception_text:
with pytest.raises(pytest.fail.Exception, match=exception_text):
testdir.parseconfig()
else:
testdir.parseconfig()
class TestConfigCmdlineParsing:
def test_parsing_again_fails(self, testdir):
@ -681,6 +735,7 @@ def test_preparse_ordering_with_setuptools(testdir, monkeypatch):
class Dist:
files = ()
metadata = {"name": "foo"}
entry_points = (EntryPoint(),)
def my_dists():
@ -711,6 +766,7 @@ def test_setuptools_importerror_issue1479(testdir, monkeypatch):
class Distribution:
version = "1.0"
files = ("foo.txt",)
metadata = {"name": "foo"}
entry_points = (DummyEntryPoint(),)
def distributions():
@ -735,6 +791,7 @@ def test_importlib_metadata_broken_distribution(testdir, monkeypatch):
class Distribution:
version = "1.0"
files = None
metadata = {"name": "foo"}
entry_points = (DummyEntryPoint(),)
def distributions():
@ -760,6 +817,7 @@ def test_plugin_preparse_prevents_setuptools_loading(testdir, monkeypatch, block
class Distribution:
version = "1.0"
files = ("foo.txt",)
metadata = {"name": "foo"}
entry_points = (DummyEntryPoint(),)
def distributions():
@ -791,6 +849,7 @@ def test_disable_plugin_autoload(testdir, monkeypatch, parse_args, should_load):
return sys.modules[self.name]
class Distribution:
metadata = {"name": "foo"}
entry_points = (DummyEntryPoint(),)
files = ()