Skip to content

Commit 67e88f6

Browse files
authored
Merge pull request #521 from AndreLouisCaron/envsubs-independence
Isolate environment variable substitutions
2 parents 1a018af + 16529d2 commit 67e88f6

File tree

3 files changed

+59
-32
lines changed

3 files changed

+59
-32
lines changed

CHANGELOG

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ Not released yet
77
- #517: Forward NUMBER_OF_PROCESSORS by default on Windows to fix
88
`multiprocessor.cpu_count()`.
99
- #518: Forward `USERPROFILE` by default on Windows.
10+
- #515: Don't require environment variables in test environments
11+
where they are not used.
1012

1113
2.7.0
1214
-----

tests/test_config.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,6 +1088,27 @@ def test_substitution_notfound_issue246(tmpdir, newconfig):
10881088
assert 'FOO' in env
10891089
assert 'BAR' in env
10901090

1091+
def test_substitution_notfound_issue515(tmpdir, newconfig):
1092+
config = newconfig("""
1093+
[tox]
1094+
envlist = standard-greeting
1095+
1096+
[testenv:standard-greeting]
1097+
commands =
1098+
python -c 'print("Hello, world!")'
1099+
1100+
[testenv:custom-greeting]
1101+
passenv =
1102+
NAME
1103+
commands =
1104+
python -c 'print("Hello, {env:NAME}!")'
1105+
""")
1106+
conf = config.envconfigs['standard-greeting']
1107+
assert conf.commands == [
1108+
['python', '-c', 'print("Hello, world!")'],
1109+
]
1110+
1111+
@pytest.mark.xfail(raises=AssertionError, reason="issue #301")
10911112
def test_substitution_nested_env_defaults_issue301(tmpdir, newconfig, monkeypatch):
10921113
monkeypatch.setenv("IGNORE_STATIC_DEFAULT", "env")
10931114
monkeypatch.setenv("IGNORE_DYNAMIC_DEFAULT", "env")

tox/config.py

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -792,7 +792,8 @@ def __init__(self, config, inipath):
792792
factors = set(name.split('-'))
793793
if section in self._cfg or factors <= known_factors:
794794
config.envconfigs[name] = \
795-
self.make_envconfig(name, section, reader._subs, config)
795+
self.make_envconfig(name, section, reader._subs, config,
796+
replace=name in config.envlist)
796797

797798
all_develop = all(name in config.envconfigs
798799
and config.envconfigs[name].usedevelop
@@ -808,7 +809,7 @@ def _list_section_factors(self, section):
808809
factors.update(*mapcat(_split_factor_expr, exprs))
809810
return factors
810811

811-
def make_envconfig(self, name, section, subs, config):
812+
def make_envconfig(self, name, section, subs, config, replace=True):
812813
factors = set(name.split('-'))
813814
reader = SectionReader(section, self._cfg, fallbacksections=["testenv"],
814815
factors=factors)
@@ -823,7 +824,7 @@ def make_envconfig(self, name, section, subs, config):
823824
atype = env_attr.type
824825
if atype in ("bool", "path", "string", "dict", "dict_setenv", "argv", "argvlist"):
825826
meth = getattr(reader, "get" + atype)
826-
res = meth(env_attr.name, env_attr.default)
827+
res = meth(env_attr.name, env_attr.default, replace=replace)
827828
elif atype == "space-separated-list":
828829
res = reader.getlist(env_attr.name, sep=" ")
829830
elif atype == "line-list":
@@ -942,9 +943,9 @@ def addsubstitutions(self, _posargs=None, **kw):
942943
if _posargs:
943944
self.posargs = _posargs
944945

945-
def getpath(self, name, defaultpath):
946+
def getpath(self, name, defaultpath, replace=True):
946947
toxinidir = self._subs['toxinidir']
947-
path = self.getstring(name, defaultpath)
948+
path = self.getstring(name, defaultpath, replace=replace)
948949
if path is not None:
949950
return toxinidir.join(path, abs=True)
950951

@@ -954,12 +955,12 @@ def getlist(self, name, sep="\n"):
954955
return []
955956
return [x.strip() for x in s.split(sep) if x.strip()]
956957

957-
def getdict(self, name, default=None, sep="\n"):
958+
def getdict(self, name, default=None, sep="\n", replace=True):
958959
value = self.getstring(name, None)
959960
return self._getdict(value, default=default, sep=sep)
960961

961-
def getdict_setenv(self, name, default=None, sep="\n"):
962-
value = self.getstring(name, None, replace=True, crossonly=True)
962+
def getdict_setenv(self, name, default=None, sep="\n", replace=True):
963+
value = self.getstring(name, None, replace=replace, crossonly=True)
963964
definitions = self._getdict(value, default=default, sep=sep)
964965
self._setenv = SetenvDict(definitions, reader=self)
965966
return self._setenv
@@ -976,8 +977,8 @@ def _getdict(self, value, default, sep):
976977

977978
return d
978979

979-
def getbool(self, name, default=None):
980-
s = self.getstring(name, default)
980+
def getbool(self, name, default=None, replace=True):
981+
s = self.getstring(name, default, replace=replace)
981982
if not s:
982983
s = default
983984
if s is None:
@@ -994,12 +995,12 @@ def getbool(self, name, default=None):
994995
"boolean value %r needs to be 'True' or 'False'")
995996
return s
996997

997-
def getargvlist(self, name, default=""):
998+
def getargvlist(self, name, default="", replace=True):
998999
s = self.getstring(name, default, replace=False)
999-
return _ArgvlistReader.getargvlist(self, s)
1000+
return _ArgvlistReader.getargvlist(self, s, replace=replace)
10001001

1001-
def getargv(self, name, default=""):
1002-
return self.getargvlist(name, default)[0]
1002+
def getargv(self, name, default="", replace=True):
1003+
return self.getargvlist(name, default, replace=replace)[0]
10031004

10041005
def getstring(self, name, default=None, replace=True, crossonly=False):
10051006
x = None
@@ -1153,7 +1154,7 @@ def _replace_substitution(self, match):
11531154

11541155
class _ArgvlistReader:
11551156
@classmethod
1156-
def getargvlist(cls, reader, value):
1157+
def getargvlist(cls, reader, value, replace=True):
11571158
"""Parse ``commands`` argvlist multiline string.
11581159
11591160
:param str name: Key name in a section.
@@ -1178,7 +1179,7 @@ def getargvlist(cls, reader, value):
11781179
replaced = reader._replace(current_command, crossonly=True)
11791180
commands.extend(cls.getargvlist(reader, replaced))
11801181
else:
1181-
commands.append(cls.processcommand(reader, current_command))
1182+
commands.append(cls.processcommand(reader, current_command, replace))
11821183
current_command = ""
11831184
else:
11841185
if current_command:
@@ -1188,31 +1189,34 @@ def getargvlist(cls, reader, value):
11881189
return commands
11891190

11901191
@classmethod
1191-
def processcommand(cls, reader, command):
1192+
def processcommand(cls, reader, command, replace=True):
11921193
posargs = getattr(reader, "posargs", "")
11931194
posargs_string = list2cmdline([x for x in posargs if x])
11941195

11951196
# Iterate through each word of the command substituting as
11961197
# appropriate to construct the new command string. This
11971198
# string is then broken up into exec argv components using
11981199
# shlex.
1199-
newcommand = ""
1200-
for word in CommandParser(command).words():
1201-
if word == "{posargs}" or word == "[]":
1202-
newcommand += posargs_string
1203-
continue
1204-
elif word.startswith("{posargs:") and word.endswith("}"):
1205-
if posargs:
1200+
if replace:
1201+
newcommand = ""
1202+
for word in CommandParser(command).words():
1203+
if word == "{posargs}" or word == "[]":
12061204
newcommand += posargs_string
12071205
continue
1208-
else:
1209-
word = word[9:-1]
1210-
new_arg = ""
1211-
new_word = reader._replace(word)
1212-
new_word = reader._replace(new_word)
1213-
new_word = new_word.replace('\\{', '{').replace('\\}', '}')
1214-
new_arg += new_word
1215-
newcommand += new_arg
1206+
elif word.startswith("{posargs:") and word.endswith("}"):
1207+
if posargs:
1208+
newcommand += posargs_string
1209+
continue
1210+
else:
1211+
word = word[9:-1]
1212+
new_arg = ""
1213+
new_word = reader._replace(word)
1214+
new_word = reader._replace(new_word)
1215+
new_word = new_word.replace('\\{', '{').replace('\\}', '}')
1216+
new_arg += new_word
1217+
newcommand += new_arg
1218+
else:
1219+
newcommand = command
12161220

12171221
# Construct shlex object that will not escape any values,
12181222
# use all values as is in argv.

0 commit comments

Comments
 (0)