Make InvocationParams.args a tuple

This avoids mutating the original list to reflect on InvocationParams,
which is supposed to be an immutable snapshot of the state of pytest.main()
at the moment of invocation (see pytest-dev/pytest-xdist#478).
This commit is contained in:
Bruno Oliveira 2019-10-19 15:54:54 -03:00
parent 119bf66d7a
commit d12cdd3127
3 changed files with 14 additions and 8 deletions

View File

@ -0,0 +1,2 @@
``Config.InvocationParams.args`` is now always a ``tuple`` to better convey that it should be
immutable and avoid accidental modifications.

View File

@ -169,7 +169,7 @@ def get_config(args=None, plugins=None):
config = Config(
pluginmanager,
invocation_params=Config.InvocationParams(
args=args, plugins=plugins, dir=Path().resolve()
args=args or (), plugins=plugins, dir=Path().resolve()
),
)
@ -654,7 +654,7 @@ class Config:
Contains the following read-only attributes:
* ``args``: list of command-line arguments as passed to ``pytest.main()``.
* ``args``: tuple of command-line arguments as passed to ``pytest.main()``.
* ``plugins``: list of extra plugins, might be None.
* ``dir``: directory where ``pytest.main()`` was invoked from.
"""
@ -667,13 +667,13 @@ class Config:
.. note::
Currently the environment variable PYTEST_ADDOPTS is also handled by
pytest implicitly, not being part of the invocation.
Note that the environment variable ``PYTEST_ADDOPTS`` and the ``addopts``
ini option are handled by pytest, not being included in the ``args`` attribute.
Plugins accessing ``InvocationParams`` must be aware of that.
"""
args = attr.ib()
args = attr.ib(converter=tuple)
plugins = attr.ib()
dir = attr.ib(type=Path)
@ -938,7 +938,6 @@ class Config:
assert not hasattr(
self, "args"
), "can only parse cmdline args at most once per Config object"
assert self.invocation_params.args == args
self.hook.pytest_addhooks.call_historic(
kwargs=dict(pluginmanager=self.pluginmanager)
)

View File

@ -7,6 +7,7 @@ import _pytest._code
import pytest
from _pytest.compat import importlib_metadata
from _pytest.config import _iter_rewritable_modules
from _pytest.config import Config
from _pytest.config.exceptions import UsageError
from _pytest.config.findpaths import determine_setup
from _pytest.config.findpaths import get_common_ancestor
@ -456,7 +457,7 @@ class TestConfigFromdictargs:
config = Config.fromdictargs(option_dict, args)
assert config.args == ["a", "b"]
assert config.invocation_params.args == args
assert config.invocation_params.args == tuple(args)
assert config.option.verbose == 4
assert config.option.capture == "no"
@ -1235,7 +1236,7 @@ def test_invocation_args(testdir):
call = calls[0]
config = call.item.config
assert config.invocation_params.args == [p, "-v"]
assert config.invocation_params.args == (p, "-v")
assert config.invocation_params.dir == Path(str(testdir.tmpdir))
plugins = config.invocation_params.plugins
@ -1243,6 +1244,10 @@ def test_invocation_args(testdir):
assert plugins[0] is plugin
assert type(plugins[1]).__name__ == "Collect" # installed by testdir.inline_run()
# args cannot be None
with pytest.raises(TypeError):
Config.InvocationParams(args=None, plugins=None, dir=Path())
@pytest.mark.parametrize(
"plugin",