Skip to content

Commit

Permalink
Allow extending lists with --override foo+=bar
Browse files Browse the repository at this point in the history
Allow appending to a list with += syntax, instead of replacing the
existing value.

Fixes: #3087
  • Loading branch information
stefanor committed Aug 12, 2023
1 parent b40dc3f commit 263c6fa
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 4 deletions.
3 changes: 3 additions & 0 deletions docs/changelog/3087.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
``--override`` can now take options in the form of ``foo+=bar`` which
will append ``bar`` to the end of an existing list, rather than
replacing it.
22 changes: 22 additions & 0 deletions docs/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -958,3 +958,25 @@ Other Substitutions

* ``{}`` - replaced as ``os.pathsep``
* ``{/}`` - replaced as ``os.sep``

Overriding configuration from the command line
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You can override options in the configuration file, from the command
line.

For example, given this config:

.. code-block:: ini
[testenv]
deps = pytest
commands = pytest tests
You could enable ``ignore_errors`` by running::

tox --override testenv.ignore_errors=True

You could add additional dependencies by running::

tox --override testenv.deps+=pytest-xdist,pytest-cov
21 changes: 18 additions & 3 deletions src/tox/config/loader/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ def __init__(self, value: str) -> None:
if not equal:
msg = f"override {value} has no = sign in it"
raise ArgumentTypeError(msg)

self.append = False
if key.endswith("+"): # key += value appends to a list
key = key[:-1]
self.append = True

self.namespace, _, self.key = key.rpartition(".")

def __repr__(self) -> str:
Expand Down Expand Up @@ -117,10 +123,19 @@ def load( # noqa: PLR0913
:param args: the config load arguments
:return: the converted type
"""
if key in self.overrides:
return _STR_CONVERT.to(self.overrides[key].value, of_type, factory)
override = self.overrides.get(key)
if override and not override.append:
return _STR_CONVERT.to(override.value, of_type, factory)
raw = self.load_raw(key, conf, args.env_name)
return self.build(key, of_type, factory, conf, raw, args)
converted = self.build(key, of_type, factory, conf, raw, args)
if override and override.append:
appends = _STR_CONVERT.to(override.value, of_type, factory)
if isinstance(converted, list) and isinstance(appends, list):
converted += appends
else:
msg = "Only able to append to lists"
raise ValueError(msg)
return converted

def build( # noqa: PLR0913
self,
Expand Down
12 changes: 12 additions & 0 deletions tests/config/loader/test_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ def test_override_add(flag: str) -> None:
assert value.key == "magic"
assert value.value == "true"
assert not value.namespace
assert value.append is False


@pytest.mark.parametrize("flag", ["-x", "--override"])
def test_override_append(flag: str) -> None:
parsed, _, __, ___, ____ = get_options(flag, "magic+=true")
assert len(parsed.override) == 1
value = parsed.override[0]
assert value.key == "magic"
assert value.value == "true"
assert not value.namespace
assert value.append is True


def test_override_equals() -> None:
Expand Down
25 changes: 24 additions & 1 deletion tests/config/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import os
from pathlib import Path
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, List

import pytest

from tox.config.loader.api import Override
from tox.config.loader.memory import MemoryLoader
Expand Down Expand Up @@ -64,6 +66,27 @@ def test_config_override_wins_memory_loader(tox_ini_conf: ToxIniCreator) -> None
assert conf["c"] == "ok"


def test_config_override_appends(tox_ini_conf: ToxIniCreator) -> None:
example = """
[testenv]
passenv = foo
"""
conf = tox_ini_conf(example, override=[Override("testenv.passenv+=bar")]).get_env("testenv")
conf.add_config("passenv", of_type=List[str], default=[], desc="desc")
assert conf["passenv"] == ["foo", "bar"]


def test_config_override_cannot_append(tox_ini_conf: ToxIniCreator) -> None:
example = """
[testenv]
foo = 1
"""
conf = tox_ini_conf(example, override=[Override("testenv.foo+=2")]).get_env("testenv")
conf.add_config("foo", of_type=int, default=0, desc="desc")
with pytest.raises(ValueError, match="Only able to append to lists"):
conf["foo"]


def test_args_are_paths_when_disabled(tox_project: ToxProjectCreator) -> None:
ini = "[testenv]\npackage=skip\ncommands={posargs}\nargs_are_paths=False"
project = tox_project({"tox.ini": ini, "w": {"a.txt": "a"}})
Expand Down

0 comments on commit 263c6fa

Please sign in to comment.