summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruno Oliveira <nicoddemus@gmail.com>2019-11-06 15:18:59 -0300
committerGitHub <noreply@github.com>2019-11-06 15:18:59 -0300
commit74f4ec59869c417b1891f7a97cd184a2093263a7 (patch)
tree1e41d120bb96926460457ab80097c4d7aa8de4c7
parent8dcee39ce93f36acd2f7daf4482546a6ac9f4041 (diff)
parentf4008042069b64111c4ad4ab047029a5e77f01ea (diff)
downloadpytest-74f4ec59869c417b1891f7a97cd184a2093263a7.tar.gz
Making it possible to access the pluginmanager in the pytest_ad… (#6106)
Making it possible to access the pluginmanager in the pytest_addoptio…
-rw-r--r--changelog/6061.feature.rst4
-rw-r--r--doc/en/writing_plugins.rst50
-rw-r--r--src/_pytest/config/__init__.py4
-rw-r--r--src/_pytest/hookspec.py7
-rw-r--r--testing/test_pluginmanager.py30
5 files changed, 93 insertions, 2 deletions
diff --git a/changelog/6061.feature.rst b/changelog/6061.feature.rst
new file mode 100644
index 000000000..11f548625
--- /dev/null
+++ b/changelog/6061.feature.rst
@@ -0,0 +1,4 @@
+Adding the pluginmanager as an option ``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.
diff --git a/doc/en/writing_plugins.rst b/doc/en/writing_plugins.rst
index 5f429c219..8660746bd 100644
--- a/doc/en/writing_plugins.rst
+++ b/doc/en/writing_plugins.rst
@@ -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
---------------------------------------------
diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py
index 2b0f48c07..8e11b56e5 100644
--- a/src/_pytest/config/__init__.py
+++ b/src/_pytest/config/__init__.py
@@ -705,7 +705,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):
diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py
index 7a21837bd..8b45c5f9b 100644
--- a/src/_pytest/hookspec.py
+++ b/src/_pytest/hookspec.py
@@ -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:
diff --git a/testing/test_pluginmanager.py b/testing/test_pluginmanager.py
index 97f220ca5..836b458c6 100644
--- a/testing/test_pluginmanager.py
+++ b/testing/test_pluginmanager.py
@@ -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")