Skip to content

Commit 4d8ab35

Browse files
committed
BACKPORT: Introduction of Config.invocation_args
1 parent f606fef commit 4d8ab35

File tree

5 files changed

+82
-9
lines changed

5 files changed

+82
-9
lines changed

AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ Evan Kepner
9292
Fabien Zarifian
9393
Fabio Zadrozny
9494
Feng Ma
95+
Fernando Mezzabotta Rey
9596
Florian Bruhin
9697
Floris Bruynooghe
9798
Gabriel Reis

changelog/6870.feature.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
New ``Config.invocation_args`` attribute containing the unchanged arguments passed to ``pytest.main()``.

doc/en/py27-py34-deprecation.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ are available on PyPI.
1717

1818
While pytest ``5.0`` will be the new mainstream and development version, until **January 2020**
1919
the pytest core team plans to make bug-fix releases of the pytest ``4.6`` series by
20-
back-porting patches to the ``4.6-maintenance`` branch that affect Python 2 users.
20+
back-porting patches to the ``4.6.x`` branch that affect Python 2 users.
2121

22-
**After 2020**, the core team will no longer actively backport patches, but the ``4.6-maintenance``
22+
**After 2020**, the core team will no longer actively backport patches, but the ``4.6.x``
2323
branch will continue to exist so the community itself can contribute patches. The core team will
2424
be happy to accept those patches and make new ``4.6`` releases **until mid-2020**.
2525

src/_pytest/config/__init__.py

+54-7
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import types
1414
import warnings
1515

16+
import attr
1617
import py
1718
import six
1819
from packaging.version import Version
@@ -35,6 +36,7 @@
3536
from _pytest.compat import safe_str
3637
from _pytest.outcomes import fail
3738
from _pytest.outcomes import Skipped
39+
from _pytest.pathlib import Path
3840
from _pytest.warning_types import PytestConfigWarning
3941

4042
hookimpl = HookimplMarker("pytest")
@@ -154,10 +156,15 @@ def directory_arg(path, optname):
154156
builtin_plugins.add("pytester")
155157

156158

157-
def get_config(args=None):
159+
def get_config(args=None, plugins=None):
158160
# subsequent calls to main will create a fresh instance
159161
pluginmanager = PytestPluginManager()
160-
config = Config(pluginmanager)
162+
config = Config(
163+
pluginmanager,
164+
invocation_params=Config.InvocationParams(
165+
args=args, plugins=plugins, dir=Path().resolve()
166+
),
167+
)
161168

162169
if args is not None:
163170
# Handle any "-p no:plugin" args.
@@ -190,7 +197,7 @@ def _prepareconfig(args=None, plugins=None):
190197
msg = "`args` parameter expected to be a list or tuple of strings, got: {!r} (type: {})"
191198
raise TypeError(msg.format(args, type(args)))
192199

193-
config = get_config(args)
200+
config = get_config(args, plugins)
194201
pluginmanager = config.pluginmanager
195202
try:
196203
if plugins:
@@ -686,13 +693,49 @@ def _iter_rewritable_modules(package_files):
686693

687694

688695
class Config(object):
689-
""" access to configuration values, pluginmanager and plugin hooks. """
696+
"""
697+
Access to configuration values, pluginmanager and plugin hooks.
698+
699+
:ivar PytestPluginManager pluginmanager: the plugin manager handles plugin registration and hook invocation.
700+
701+
:ivar argparse.Namespace option: access to command line option as attributes.
702+
703+
:ivar InvocationParams invocation_params:
704+
705+
Object containing the parameters regarding the ``pytest.main``
706+
invocation.
707+
Contains the followinig read-only attributes:
708+
* ``args``: list of command-line arguments as passed to ``pytest.main()``.
709+
* ``plugins``: list of extra plugins, might be None
710+
* ``dir``: directory where ``pytest.main()`` was invoked from.
711+
"""
712+
713+
@attr.s(frozen=True)
714+
class InvocationParams(object):
715+
"""Holds parameters passed during ``pytest.main()``
716+
.. note::
717+
Currently the environment variable PYTEST_ADDOPTS is also handled by
718+
pytest implicitly, not being part of the invocation.
719+
Plugins accessing ``InvocationParams`` must be aware of that.
720+
"""
721+
722+
args = attr.ib()
723+
plugins = attr.ib()
724+
dir = attr.ib()
725+
726+
def __init__(self, pluginmanager, invocation_params=None, *args):
727+
from .argparsing import Parser, FILE_OR_DIR
728+
729+
if invocation_params is None:
730+
invocation_params = self.InvocationParams(
731+
args=(), plugins=None, dir=Path().resolve()
732+
)
690733

691-
def __init__(self, pluginmanager):
692734
#: access to command line option as attributes.
693735
#: (deprecated), use :py:func:`getoption() <_pytest.config.Config.getoption>` instead
694736
self.option = argparse.Namespace()
695-
from .argparsing import Parser, FILE_OR_DIR
737+
738+
self.invocation_params = invocation_params
696739

697740
_a = FILE_OR_DIR
698741
self._parser = Parser(
@@ -709,9 +752,13 @@ def __init__(self, pluginmanager):
709752
self._cleanup = []
710753
self.pluginmanager.register(self, "pytestconfig")
711754
self._configured = False
712-
self.invocation_dir = py.path.local()
713755
self.hook.pytest_addoption.call_historic(kwargs=dict(parser=self._parser))
714756

757+
@property
758+
def invocation_dir(self):
759+
"""Backward compatibility"""
760+
return py.path.local(str(self.invocation_params.dir))
761+
715762
def add_cleanup(self, func):
716763
""" Add a function to be called when the config object gets out of
717764
use (usually coninciding with pytest_unconfigure)."""

testing/test_config.py

+24
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from _pytest.main import EXIT_OK
2020
from _pytest.main import EXIT_TESTSFAILED
2121
from _pytest.main import EXIT_USAGEERROR
22+
from _pytest.pathlib import Path
2223

2324

2425
class TestParseIni(object):
@@ -1222,6 +1223,29 @@ def test_config_does_not_load_blocked_plugin_from_args(testdir):
12221223
assert result.ret == EXIT_USAGEERROR
12231224

12241225

1226+
def test_invocation_args(testdir):
1227+
"""Ensure that Config.invocation_* arguments are correctly defined"""
1228+
1229+
class DummyPlugin:
1230+
pass
1231+
1232+
p = testdir.makepyfile("def test(): pass")
1233+
plugin = DummyPlugin()
1234+
rec = testdir.inline_run(p, "-v", plugins=[plugin])
1235+
calls = rec.getcalls("pytest_runtest_protocol")
1236+
assert len(calls) == 1
1237+
call = calls[0]
1238+
config = call.item.config
1239+
1240+
assert config.invocation_params.args == [p, "-v"]
1241+
assert config.invocation_params.dir == Path(str(testdir.tmpdir))
1242+
1243+
plugins = config.invocation_params.plugins
1244+
assert len(plugins) == 2
1245+
assert plugins[0] is plugin
1246+
assert type(plugins[1]).__name__ == "Collect" # installed by testdir.inline_run()
1247+
1248+
12251249
@pytest.mark.parametrize(
12261250
"plugin",
12271251
[

0 commit comments

Comments
 (0)