Making it possible to access the pluginmanager in the pytest_addoption hook

This commit is contained in:
Joshua Storck 2019-10-30 14:18:13 -04:00
parent cefe6bfec3
commit 7a96d94fd4
8 changed files with 99 additions and 7 deletions

View File

@ -0,0 +1,5 @@
Adding the pluginmanager as an option to :py:func:`~hookspec.pytest_addoption`
so that hooks can be invoked when setting up command line options. This is
useful for having one plugin communicate things to another plugin,
such as default values or which set of command line options to add. See
:ref:`Using hooks in pytest_addoption <addoptionhooks>` for more details.

View File

@ -364,7 +364,7 @@ specifies via named environments:
import pytest
def pytest_addoption(parser):
def pytest_addoption(parser, pluginmanager):
parser.addoption(
"-E",
action="store",

View File

@ -36,7 +36,7 @@ Now we add a test configuration like this:
# content of conftest.py
def pytest_addoption(parser):
def pytest_addoption(parser, pluginmanager):
parser.addoption("--all", action="store_true", help="run all combinations")

View File

@ -33,7 +33,7 @@ provide the ``cmdopt`` through a :ref:`fixture function <fixture function>`:
import pytest
def pytest_addoption(parser):
def pytest_addoption(parser, pluginmanager):
parser.addoption(
"--cmdopt", action="store", default="type1", help="my option: type1 or type2"
)
@ -151,7 +151,7 @@ line option to control skipping of ``pytest.mark.slow`` marked tests:
import pytest
def pytest_addoption(parser):
def pytest_addoption(parser, pluginmanager):
parser.addoption(
"--runslow", action="store_true", default=False, help="run slow tests"
)

View File

@ -338,7 +338,7 @@ string value of ``Hello World!`` if we do not supply a value or ``Hello
import pytest
def pytest_addoption(parser):
def pytest_addoption(parser, pluginmanager):
group = parser.getgroup("helloworld")
group.addoption(
"--name",
@ -677,6 +677,56 @@ Example:
print(config.hook)
.. _`addoptionhooks`:
Using hooks in pytest_addoption
-------------------------------
Occasionally, it is necessary to change the way in which command line options
are defined by one plugin based on hooks in another plugin. For example,
a plugin may expose a command line option for which another plugin needs
to define the default value. The pluginmanager can be used to install and
use hooks to accomplish this. The plugin would define and add the hooks
and use pytest_addoption as follows:
.. code-block:: python
# contents of hooks.py
# Use firstresult=True because we only want one plugin to define this
# default value
@hookspec(firstresult=True)
def pytest_config_file_default_value():
""" Return the default value for the config file command line option. """
# contents of myplugin.py
def pytest_addhooks(pluginmanager):
""" This example assumes the hooks are grouped in the 'hooks' module. """
from . import hook
pluginmanager.add_hookspecs(hook)
def pytest_addoption(parser, pluginmanager):
default_value = pluginmanager.hook.pytest_config_file_default_value()
parser.addoption(
"--config-file",
help="Config file to use, defaults to %(default)s",
default=default_value,
)
The conftest.py that is using myplugin would simply define the hook as follows:
.. code-block:: python
def pytest_config_file_default_value():
return "config.yaml"
Optionally using hooks from 3rd party plugins
---------------------------------------------

View File

@ -697,7 +697,9 @@ class Config:
self._cleanup = [] # type: List[Callable[[], None]]
self.pluginmanager.register(self, "pytestconfig")
self._configured = False
self.hook.pytest_addoption.call_historic(kwargs=dict(parser=self._parser))
self.hook.pytest_addoption.call_historic(
kwargs=dict(parser=self._parser, pluginmanager=self.pluginmanager)
)
@property
def invocation_dir(self):

View File

@ -35,7 +35,7 @@ def pytest_plugin_registered(plugin, manager):
@hookspec(historic=True)
def pytest_addoption(parser):
def pytest_addoption(parser, pluginmanager):
"""register argparse-style options and ini-style config values,
called once at the beginning of a test run.
@ -50,6 +50,11 @@ def pytest_addoption(parser):
To add ini-file values call :py:func:`parser.addini(...)
<_pytest.config.Parser.addini>`.
:arg _pytest.config.PytestPluginManager pluginmanager: pytest plugin manager,
which can be used to install :py:func:`hookspec`'s or :py:func:`hookimpl`'s
and allow one plugin to call another plugin's hooks to change how
command line options are added.
Options can later be accessed through the
:py:class:`config <_pytest.config.Config>` object, respectively:

View File

@ -135,6 +135,36 @@ class TestPytestPluginInteractions:
ihook_b = session.gethookproxy(testdir.tmpdir.join("tests"))
assert ihook_a is not ihook_b
def test_hook_with_addoption(self, testdir):
"""Test that hooks can be used in a call to pytest_addoption"""
testdir.makepyfile(
newhooks="""
import pytest
@pytest.hookspec(firstresult=True)
def pytest_default_value():
pass
"""
)
testdir.makepyfile(
myplugin="""
import newhooks
def pytest_addhooks(pluginmanager):
pluginmanager.add_hookspecs(newhooks)
def pytest_addoption(parser, pluginmanager):
default_value = pluginmanager.hook.pytest_default_value()
parser.addoption("--config", help="Config, defaults to %(default)s", default=default_value)
"""
)
testdir.makeconftest(
"""
pytest_plugins=("myplugin",)
def pytest_default_value():
return "default_value"
"""
)
res = testdir.runpytest("--help")
res.stdout.fnmatch_lines(["*--config=CONFIG*default_value*"])
def test_default_markers(testdir):
result = testdir.runpytest("--markers")