summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBernát Gábor <bgabor8@bloomberg.net>2021-01-16 18:11:42 +0000
committerGitHub <noreply@github.com>2021-01-16 18:11:42 +0000
commit22e9e25d59645f14a0062a6e714c9521cbc21a37 (patch)
tree1b25c2a0ed121f3bac8cb2c380d1355d1d94f112
parentf19fcd19a7098033b78229ce10a176e29c0ccce0 (diff)
downloadtox-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.rst2
-rw-r--r--src/tox/tox_env/python/req_file.py81
-rw-r--r--tests/tox_env/python/test_req_file.py11
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"