Skip to content

Commit 2c863a7

Browse files
author
Vaskó László
committed
config: expand nested substitutions (fixes #301)
In case of a nested substitution values are expanded starting from the innermost expression and continued until no successful substitutions could be found.
1 parent e374ce6 commit 2c863a7

File tree

4 files changed

+24
-4
lines changed

4 files changed

+24
-4
lines changed

CONTRIBUTORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,4 @@ Selim Belhaouane
4848
Nick Douma
4949
Cyril Roelandt
5050
Bartolome Sanchez Salado
51+
Laszlo Vasko

doc/config.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,14 @@ then the value will be retrieved as ``os.environ['KEY']``
435435
and replace with and empty string if the environment variable does not
436436
exist.
437437

438+
Substitutions can also be nested. In that case they are expanded starting
439+
from the innermost expression::
440+
441+
{env:KEY:{env:DEFAULT_OF_KEY}}
442+
443+
the above example is roughly equivalent to
444+
``os.environ.get('KEY', os.environ['DEFAULT_OF_KEY'])``
445+
438446
.. _`command positional substitution`:
439447
.. _`positional substitution`:
440448

tests/test_config.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,8 +1088,7 @@ def test_substitution_notfound_issue246(tmpdir, newconfig):
10881088
assert 'FOO' in env
10891089
assert 'BAR' in env
10901090

1091-
@pytest.mark.xfail(raises=AssertionError, reason="issue #301")
1092-
def test_substitution_env_defaults_issue301(tmpdir, newconfig, monkeypatch):
1091+
def test_substitution_nested_env_defaults_issue301(tmpdir, newconfig, monkeypatch):
10931092
monkeypatch.setenv("IGNORE_STATIC_DEFAULT", "env")
10941093
monkeypatch.setenv("IGNORE_DYNAMIC_DEFAULT", "env")
10951094
config = newconfig("""

tox/config.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,8 +1059,20 @@ def __init__(self, reader, crossonly=False):
10591059
self.reader = reader
10601060
self.crossonly = crossonly
10611061

1062-
def do_replace(self, x):
1063-
return self.RE_ITEM_REF.sub(self._replace_match, x)
1062+
def do_replace(self, value):
1063+
'''
1064+
Recursively expand substitutions starting from the innermost expression
1065+
'''
1066+
def substitute_once(x):
1067+
return self.RE_ITEM_REF.sub(self._replace_match, x)
1068+
1069+
expanded = substitute_once(value)
1070+
1071+
while expanded != value: # substitution found
1072+
value = expanded
1073+
expanded = substitute_once(value)
1074+
1075+
return expanded
10641076

10651077
def _replace_match(self, match):
10661078
g = match.groupdict()

0 commit comments

Comments
 (0)