Skip to content

Commit

Permalink
Handle quotes around setting paths
Browse files Browse the repository at this point in the history
  • Loading branch information
jayvdb committed Oct 20, 2020
1 parent 0dc8540 commit 1a9f5ac
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 7 deletions.
28 changes: 25 additions & 3 deletions src/tox/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
_ENVSTR_SPLIT_PATTERN = re.compile(r"((?:\{[^}]+\})+)|,")
_ENVSTR_EXPAND_PATTERN = re.compile(r"\{([^}]+)\}")
_WHITESPACE_PATTERN = re.compile(r"\s+")
_UNESCAPED_DOUBLEQUOTE = re.compile(r"((?<!\{1})'){2}")


def get_plugin_manager(plugins=()):
Expand Down Expand Up @@ -1944,8 +1945,11 @@ def processcommand(cls, reader, command, replace=True):
continue

new_arg = ""
had_dual_quote = re.search(_UNESCAPED_DOUBLEQUOTE, word)
new_word = reader._replace(word, unquote_path=False)
new_word = reader._replace(new_word, unquote_path=False)
if not had_dual_quote:
new_word = re.sub(_UNESCAPED_DOUBLEQUOTE, "'", new_word)
new_word = new_word.replace("\\{", "{").replace("\\}", "}")
new_arg += new_word
newcommand += new_arg
Expand Down Expand Up @@ -1980,8 +1984,14 @@ def word_has_ended():
and ps.word
and ps.word[-1] not in string.whitespace
)
or (cur_char == "{" and ps.depth == 0 and not ps.word.endswith("\\"))
or (ps.depth == 0 and ps.word and ps.word[-1] == "}")
or (
cur_char == "{"
and ps.depth == 0
and not ps.word.endswith("\\")
and ps.word != "'"
)
or (ps.depth == 0 and ps.word and ps.word[-1] == "}" and peek() != "'")
or (ps.depth == 0 and ps.word and ps.word[-2:] == "}'")
or (cur_char not in string.whitespace and ps.word and ps.word.strip() == "")
)

Expand All @@ -1995,6 +2005,12 @@ def yield_if_word_ended():
if word_has_ended():
yield_this_word()

def peek():
try:
return self.command[_i + 1]
except IndexError:
return ""

def accumulate():
ps.word += cur_char

Expand All @@ -2004,7 +2020,7 @@ def push_substitution():
def pop_substitution():
ps.depth -= 1

for cur_char in self.command:
for _i, cur_char in enumerate(self.command):
if cur_char in string.whitespace:
if ps.depth == 0:
yield_if_word_ended()
Expand All @@ -2016,6 +2032,12 @@ def pop_substitution():
elif cur_char == "}":
accumulate()
pop_substitution()
elif cur_char == "'":
if ps.depth == 0 and ps.word[:2] == "'{" and ps.word[-1] == "}":
accumulate()
else:
yield_if_word_ended()
accumulate()
else:
yield_if_word_ended()
accumulate()
Expand Down
62 changes: 58 additions & 4 deletions tests/unit/config/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ def test_command_substitution_pound(self, tmpdir, newconfig):
toxworkdir = {toxinidir}/.tox#dir
[testenv:py27]
commands = {envpython} {toxworkdir}
commands = '{envpython}' '{toxworkdir}'/'foo#bar'
""",
)

Expand All @@ -541,7 +541,7 @@ def test_command_substitution_pound(self, tmpdir, newconfig):

assert envconfig.commands[0] == [
str(envconfig.envbindir.join("python")),
str(config.toxworkdir.realpath()),
str(config.toxworkdir.join("foo#bar")),
]

def test_command_substitution_whitespace(self, tmpdir, newconfig):
Expand All @@ -552,7 +552,7 @@ def test_command_substitution_whitespace(self, tmpdir, newconfig):
toxworkdir = {toxinidir}/.tox dir
[testenv:py27]
commands = {envpython} {toxworkdir}
commands = '{envpython}' '{toxworkdir}'/'foo bar'
""",
)

Expand All @@ -567,7 +567,61 @@ def test_command_substitution_whitespace(self, tmpdir, newconfig):

assert envconfig.commands[0] == [
str(envconfig.envbindir.join("python")),
str(config.toxworkdir.realpath()),
str(config.toxworkdir.join("foo bar").realpath()),
]

def test_command_env_substitution_pound(self, tmpdir, newconfig):
"""Ensure pound in path is kept in commands using setenv."""
config = newconfig(
"""
[tox]
toxworkdir = {toxinidir}/.tox#dir
[testenv:py27]
setenv = VAR = '{toxworkdir}'/'foo#bar'
commands = '{envpython}' '{env:VAR}'
""",
)

assert config.toxworkdir.realpath() == tmpdir.join(".tox#dir").realpath()

envconfig = config.envconfigs["py27"]

assert envconfig.envbindir.realpath() in [
tmpdir.join(".tox#dir", "py27", "bin").realpath(),
tmpdir.join(".tox#dir", "py27", "Scripts").realpath(),
]

assert envconfig.commands[0] == [
str(envconfig.envbindir.join("python")),
str(config.toxworkdir.join("foo#bar").realpath()),
]

def test_command_env_substitution_whitespace(self, tmpdir, newconfig):
"""Ensure spaces in path is kept in commands using setenv."""
config = newconfig(
"""
[tox]
toxworkdir = {toxinidir}/.tox dir
[testenv:py27]
setenv = VAR = '{toxworkdir}'/'foo bar'
commands = '{envpython}' '{env:VAR}'
""",
)

assert config.toxworkdir.realpath() == tmpdir.join(".tox dir").realpath()

envconfig = config.envconfigs["py27"]

assert envconfig.envbindir.realpath() in [
tmpdir.join(".tox dir", "py27", "bin").realpath(),
tmpdir.join(".tox dir", "py27", "Scripts").realpath(),
]

assert envconfig.commands[0] == [
str(envconfig.envbindir.join("python")),
str(config.toxworkdir.join("foo bar").realpath()),
]


Expand Down

0 comments on commit 1a9f5ac

Please sign in to comment.