diff options
author | Bernát Gábor <bgabor8@bloomberg.net> | 2021-01-16 18:11:42 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-01-16 18:11:42 +0000 |
commit | 22e9e25d59645f14a0062a6e714c9521cbc21a37 (patch) | |
tree | 1b25c2a0ed121f3bac8cb2c380d1355d1d94f112 | |
parent | f19fcd19a7098033b78229ce10a176e29c0ccce0 (diff) | |
download | tox-git-22e9e25d59645f14a0062a6e714c9521cbc21a37.tar.gz |
Support all requirement one arg flags to be specified without space (#1835)
Signed-off-by: Bernát Gábor <bgabor8@bloomberg.net>
-rw-r--r-- | docs/changelog/1834.bugfix.rst | 2 | ||||
-rw-r--r-- | src/tox/tox_env/python/req_file.py | 81 | ||||
-rw-r--r-- | tests/tox_env/python/test_req_file.py | 11 |
3 files changed, 61 insertions, 33 deletions
diff --git a/docs/changelog/1834.bugfix.rst b/docs/changelog/1834.bugfix.rst new file mode 100644 index 00000000..8486fce1 --- /dev/null +++ b/docs/changelog/1834.bugfix.rst @@ -0,0 +1,2 @@ +Support all valid requirement file specification without delimiting space in the ``deps`` of the ``tox.ini`` - +by :user:`gaborbernat`. diff --git a/src/tox/tox_env/python/req_file.py b/src/tox/tox_env/python/req_file.py index cd690646..b704b09c 100644 --- a/src/tox/tox_env/python/req_file.py +++ b/src/tox/tox_env/python/req_file.py @@ -23,6 +23,41 @@ def get_url_scheme(url: str) -> Optional[str]: return None if ":" not in url else url.split(":", 1)[0].lower() +NO_ARG = { + "--no-index", + "--prefer-binary", + "--require-hashes", + "--pre", +} +ONE_ARG = { + "-i", + "--index-url", + "--extra-index-url", + "-e", + "--editable", + "-c", + "--constraint", + "-r", + "--requirement", + "-f", + "--find-links", + "--trusted-host", + "--use-feature", + "--no-binary", + "--only-binary", +} +ONE_ARG_ESCAPE = { + "-c", + "--constraint", + "-r", + "--requirement", + "-f", + "--find-links", + "-e", + "--editable", +} + + class RequirementsFile: """ Specification is defined within pip itself and documented under: @@ -30,43 +65,17 @@ class RequirementsFile: - https://github.com/pypa/pip/blob/master/src/pip/_internal/req/constructors.py#L291 """ - VALID_OPTIONS = { - "no_arg": [ - "--no-index", - "--prefer-binary", - "--require-hashes", - "--pre", - ], - "one_arg": [ - "-i", - "--index-url", - "--extra-index-url", - "-e", - "--editable", - "-c", - "--constraint", - "-r", - "--requirement", - "-f", - "--find-links", - "--trusted-host", - "--use-feature", - "--no-binary", - "--only-binary", - ], - } - def __init__(self, raw: str, within_tox_ini: bool = True, root: Optional[Path] = None) -> None: self._root = Path().cwd() if root is None else root if within_tox_ini: # patch the content coming from tox.ini lines: List[str] = [] for line in raw.splitlines(): # for tox<4 supporting requirement/constraint files via -rreq.txt/-creq.txt - if len(line) >= 3 and (line.startswith("-r") or line.startswith("-c")) and not line[2].isspace(): - line = f"{line[:2]} {line[2:]}" + arg_match = next((o for o in ONE_ARG if line.startswith(o) and not line[len(o)].isspace()), None) + if arg_match is not None: + line = f"{arg_match} {line[len(arg_match):]}" # escape spaces - escape_for = ("-c", "--constraint", "-r", "--requirement", "-f", "--find-links" "-e", "--editable") - escape_match = next((e for e in escape_for if line.startswith(e) and line[len(e)].isspace()), None) + escape_match = next((e for e in ONE_ARG_ESCAPE if line.startswith(e) and line[len(e)].isspace()), None) if escape_match is not None: # escape not already escaped spaces escaped = re.sub(r"(?<!\\)(\s)", r"\\\1", line[len(escape_match) + 1 :]) @@ -96,12 +105,12 @@ class RequirementsFile: if line.startswith("-"): # handle flags words = [i for i in re.split(r"(?<!\\)\s", line) if i] first = words[0] - if first in self.VALID_OPTIONS["no_arg"]: + if first in NO_ARG: if len(words) != 1: raise ValueError(line) else: result.append(" ".join(words)) - elif first in self.VALID_OPTIONS["one_arg"]: + elif first in ONE_ARG: if len(words) != 2: raise ValueError(line) else: @@ -168,3 +177,11 @@ class RequirementsFile: yield Path(path) finally: os.unlink(path) + + +__all__ = ( + "RequirementsFile", + "ONE_ARG", + "ONE_ARG_ESCAPE", + "NO_ARG", +) diff --git a/tests/tox_env/python/test_req_file.py b/tests/tox_env/python/test_req_file.py index cb112178..0e61049f 100644 --- a/tests/tox_env/python/test_req_file.py +++ b/tests/tox_env/python/test_req_file.py @@ -4,7 +4,7 @@ from pathlib import Path import pytest from tox.pytest import MonkeyPatch -from tox.tox_env.python.req_file import RequirementsFile +from tox.tox_env.python.req_file import ONE_ARG, RequirementsFile @pytest.mark.parametrize( @@ -127,6 +127,7 @@ def test_requirements_txt_transitive(tmp_path: Path, flag: str) -> None: [ "--pre something", "--missing", + "--index-url a b", "-k", "magic+https://git.example.com/MyProject#egg=MyProject", ], @@ -167,6 +168,14 @@ def test_constraint_txt_expanded(tmp_path: Path, flag: str) -> None: assert filename.read_text() == f"{flag} other.txt" +@pytest.mark.parametrize("flag", sorted(ONE_ARG - {"-c", "--constraint", "-r", "--requirement"})) +def test_one_arg_expanded(tmp_path: Path, flag: str) -> None: + req = RequirementsFile(f"{flag}argument", root=tmp_path) + assert req.validate_and_expand() == [f"{flag} argument"] + with req.with_file() as filename: + assert filename.read_text() == f"{flag} argument" + + @pytest.mark.parametrize("escape_upfront", [True, False]) def test_req_path_with_space(tmp_path: Path, escape_upfront: bool) -> None: req_file = tmp_path / "a b" |