Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suport # character in path of tox project #2146

Merged
merged 2 commits into from
Aug 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/changelog/763.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Support ``#`` character in path for the tox project - by :user:`gaborbernat`.
5 changes: 3 additions & 2 deletions src/tox/config/loader/ini/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,17 @@ def __init__(self, section: str, parser: ConfigParser, overrides: List[Override]
super().__init__(overrides)

def load_raw(self, key: str, conf: Optional["Config"], env_name: Optional[str]) -> str:
value = self._section[key]
return self.process_raw(conf, env_name, self._section[key])

@staticmethod
def process_raw(conf: Optional["Config"], env_name: Optional[str], value: str) -> str:
# strip comments
elements: List[str] = []
for line in value.split("\n"):
if not line.startswith("#"):
part = _COMMENTS.sub("", line)
elements.append(part.replace("\\#", "#"))
strip_comments = "\n".join(elements)

if conf is None: # conf is None when we're loading the global tox configuration file for the CLI
factor_filtered = strip_comments # we don't support factor and replace functionality there
else:
Expand Down
3 changes: 2 additions & 1 deletion src/tox/config/loader/ini/replace.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,10 @@ def replace_reference(
for src in _config_value_sources(settings["env"], settings["section"], current_env, conf, loader):
try:
if isinstance(src, SectionProxy):
return src[key]
return loader.process_raw(conf, current_env, src[key])
value = src.load(key, chain)
as_str, _ = stringify(value)
as_str = as_str.replace("#", r"\#") # escape comment characters as these will be stripped
return as_str
except KeyError as exc: # if fails, keep trying maybe another source can satisfy
exception = exc
Expand Down
2 changes: 2 additions & 0 deletions src/tox/config/loader/str_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,10 @@ def to_dict(value: str, of_type: Tuple[Type[Any], Type[Any]]) -> Iterator[Tuple[
@staticmethod
def to_command(value: str) -> Command:
is_win = sys.platform == "win32"
value = value.replace(r"\#", "#")
splitter = shlex.shlex(value, posix=not is_win)
splitter.whitespace_split = True
splitter.commenters = "" # comments handled earlier, and the shlex does not know escaped comment characters
args: List[str] = []
pos = 0
try:
Expand Down
1 change: 1 addition & 0 deletions src/tox/config/set_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def load(self, item: str, chain: Optional[List[str]] = None) -> str:
return self._materialized[item]
raw = self._raw[item]
result = self.replacer(raw, chain) # apply any replace options
result = result.replace(r"\#", "#") # unroll escaped comment with replacement
self._materialized[item] = result
self._raw.pop(item, None) # if the replace requires the env we may be called again, so allow pop to fail
return result
Expand Down
8 changes: 5 additions & 3 deletions src/tox/pytest.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,17 +370,19 @@ def matches(pattern: str, text: str, flags: int = 0) -> None:


class ToxProjectCreator(Protocol):
def __call__(self, files: Dict[str, Any], base: Optional[Path] = None) -> ToxProject: # noqa: U100
def __call__(
self, files: Dict[str, Any], base: Optional[Path] = None, prj_path: Optional[Path] = None # noqa: U100
) -> ToxProject:
...


@pytest.fixture(name="tox_project")
def init_fixture(
tmp_path: Path, capfd: CaptureFixture, monkeypatch: MonkeyPatch, mocker: MockerFixture
) -> ToxProjectCreator:
def _init(files: Dict[str, Any], base: Optional[Path] = None) -> ToxProject:
def _init(files: Dict[str, Any], base: Optional[Path] = None, prj_path: Optional[Path] = None) -> ToxProject:
"""create tox projects"""
return ToxProject(files, base, tmp_path / "p", capfd, monkeypatch, mocker)
return ToxProject(files, base, prj_path or tmp_path / "p", capfd, monkeypatch, mocker)

return _init # noqa

Expand Down
26 changes: 26 additions & 0 deletions tests/session/cmd/test_show_config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import platform
import sys
from configparser import ConfigParser
from pathlib import Path
from textwrap import dedent
from typing import Callable, Tuple

import pytest
Expand Down Expand Up @@ -155,3 +157,27 @@ def test_show_config_description_normalize(tox_project: ToxProjectCreator) -> No
outcome = tox_project({"tox.ini": tox_ini}).run("c", "-e", "py", "-k", "description")
outcome.assert_success()
assert outcome.out == "[testenv:py]\ndescription = A magical pipe of this\n"


def test_show_config_ini_comment_path(tox_project: ToxProjectCreator, tmp_path: Path) -> None:
prj_path = tmp_path / "#magic"
prj_path.mkdir()
ini = """
[testenv]
package = skip
set_env =
A=1 # comment
# more comment
commands = {envpython} -c 'import os; print(os.linesep.join(f"{k}={v}" for k, v in os.environ.items()))'
[testenv:py]
set_env =
{[testenv]set_env}
B = {tox_root} # just some comment
"""
project = tox_project({"tox.ini": dedent(ini)}, prj_path=prj_path)
result = project.run("r", "-e", "py")
result.assert_success()
a_line = next(i for i in result.out.splitlines() if i.startswith("A=")) # pragma: no branch # not found raises
assert a_line == "A=1"
b_line = next(i for i in result.out.splitlines() if i.startswith("B=")) # pragma: no branch # not found raises
assert b_line == f"B={prj_path}"
1 change: 1 addition & 0 deletions whitelist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ chdir
cmd
codec
colorama
commenters
conf
configs
conftest
Expand Down