Skip to content

Commit d3ef0b4

Browse files
committed
Allow {posargs} in setenv
Fixes #1695
1 parent 4e1e0ed commit d3ef0b4

File tree

4 files changed

+46
-15
lines changed

4 files changed

+46
-15
lines changed

CONTRIBUTORS

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ Jake Windle
4848
Jannis Leidel
4949
Joachim Brandon LeBlanc
5050
Johannes Christ
51+
John Mark Vandenberg
5152
Jon Dufresne
5253
Josh Smeaton
5354
Josh Snyder

docs/changelog/1695.feature.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Allow {posargs} in setenv. - by :user:`jayvdb`

src/tox/config/__init__.py

+29-15
Original file line numberDiff line numberDiff line change
@@ -1559,7 +1559,15 @@ def __repr__(self):
15591559

15601560

15611561
class SectionReader:
1562-
def __init__(self, section_name, cfgparser, fallbacksections=None, factors=(), prefix=None):
1562+
def __init__(
1563+
self,
1564+
section_name,
1565+
cfgparser,
1566+
fallbacksections=None,
1567+
factors=(),
1568+
prefix=None,
1569+
posargs="",
1570+
):
15631571
if prefix is None:
15641572
self.section_name = section_name
15651573
else:
@@ -1570,6 +1578,7 @@ def __init__(self, section_name, cfgparser, fallbacksections=None, factors=(), p
15701578
self._subs = {}
15711579
self._subststack = []
15721580
self._setenv = None
1581+
self.posargs = posargs
15731582

15741583
def get_environ_value(self, name):
15751584
if self._setenv is None:
@@ -1695,6 +1704,17 @@ def getstring(self, name, default=None, replace=True, crossonly=False, no_fallba
16951704
x = self._replace_if_needed(x, name, replace, crossonly)
16961705
return x
16971706

1707+
def getposargs(self, default=None):
1708+
if self.posargs:
1709+
posargs = self.posargs
1710+
if sys.platform.startswith("win"):
1711+
posargs_string = list2cmdline([x for x in posargs if x])
1712+
else:
1713+
posargs_string = " ".join([shlex_quote(x) for x in posargs if x])
1714+
return posargs_string
1715+
else:
1716+
return default or ""
1717+
16981718
def _replace_if_needed(self, x, name, replace, crossonly):
16991719
if replace and x and hasattr(x, "replace"):
17001720
x = self._replace(x, name=name, crossonly=crossonly)
@@ -1781,6 +1801,9 @@ def _replace_match(self, match):
17811801
if not any(g.values()):
17821802
return os.pathsep
17831803

1804+
if sub_value == "posargs":
1805+
return self.reader.getposargs(match.group("default_value"))
1806+
17841807
try:
17851808
sub_type = g["sub_type"]
17861809
except KeyError:
@@ -1794,6 +1817,8 @@ def _replace_match(self, match):
17941817
if is_interactive():
17951818
return match.group("substitution_value")
17961819
return match.group("default_value")
1820+
if sub_type == "posargs":
1821+
return self.reader.getposargs(match.group("substitution_value"))
17971822
if sub_type is not None:
17981823
raise tox.exception.ConfigError(
17991824
"No support for the {} substitution type".format(sub_type),
@@ -1887,28 +1912,17 @@ def getargvlist(cls, reader, value, replace=True):
18871912

18881913
@classmethod
18891914
def processcommand(cls, reader, command, replace=True):
1890-
posargs = getattr(reader, "posargs", "")
1891-
if sys.platform.startswith("win"):
1892-
posargs_string = list2cmdline([x for x in posargs if x])
1893-
else:
1894-
posargs_string = " ".join([shlex_quote(x) for x in posargs if x])
1895-
18961915
# Iterate through each word of the command substituting as
18971916
# appropriate to construct the new command string. This
18981917
# string is then broken up into exec argv components using
18991918
# shlex.
19001919
if replace:
19011920
newcommand = ""
19021921
for word in CommandParser(command).words():
1903-
if word == "{posargs}" or word == "[]":
1904-
newcommand += posargs_string
1922+
if word == "[]":
1923+
newcommand += reader.getposargs()
19051924
continue
1906-
elif word.startswith("{posargs:") and word.endswith("}"):
1907-
if posargs:
1908-
newcommand += posargs_string
1909-
continue
1910-
else:
1911-
word = word[9:-1]
1925+
19121926
new_arg = ""
19131927
new_word = reader._replace(word)
19141928
new_word = reader._replace(new_word)

tests/unit/config/test_config.py

+15
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,21 @@ def test_command_env_substitution(self, newconfig):
441441
assert envconfig.commands == [["ls", "testvalue"]]
442442
assert envconfig.setenv["TEST"] == "testvalue"
443443

444+
def test_command_env_substitution_posargs(self, newconfig):
445+
"""Ensure {posargs} values are substituted correctly."""
446+
config = newconfig(
447+
"""
448+
[testenv:py27]
449+
setenv =
450+
TEST={posargs:default}
451+
commands =
452+
ls {env:TEST}
453+
""",
454+
)
455+
envconfig = config.envconfigs["py27"]
456+
assert envconfig.commands == [["ls", "default"]]
457+
assert envconfig.setenv["TEST"] == "default"
458+
444459
def test_command_env_substitution_global(self, newconfig):
445460
"""Ensure referenced {env:key:default} values are substituted correctly."""
446461
config = newconfig(

0 commit comments

Comments
 (0)