summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorq0w <43147888+q0w@users.noreply.github.com>2023-01-07 02:26:25 +0300
committerGitHub <noreply@github.com>2023-01-06 15:26:25 -0800
commite9d6e63854c1c0f72ec58b038951b3c9650ad0e7 (patch)
tree50a7e0bb8b02060606816aa424c3060022193b03
parentb9b4d02978b6b66e19986fe894aedf9ad26f54a5 (diff)
downloadtox-git-e9d6e63854c1c0f72ec58b038951b3c9650ad0e7.tar.gz
Handle properly pip --no-binary / --only-binary options in requirements.txt format files. (#2834)
Fixes https://github.com/tox-dev/tox/issues/2814
-rw-r--r--docs/changelog/2814.bugfix.rst1
-rw-r--r--src/tox/tox_env/python/pip/req/args.py4
-rw-r--r--src/tox/tox_env/python/pip/req/file.py13
-rw-r--r--src/tox/tox_env/python/pip/req/util.py20
-rw-r--r--tests/tox_env/python/pip/req/test_file.py39
5 files changed, 66 insertions, 11 deletions
diff --git a/docs/changelog/2814.bugfix.rst b/docs/changelog/2814.bugfix.rst
new file mode 100644
index 00000000..40d2f9c0
--- /dev/null
+++ b/docs/changelog/2814.bugfix.rst
@@ -0,0 +1 @@
+Handle properly pip ``--no-binary`` / ``--only-binary`` options in requirements.txt format files.
diff --git a/src/tox/tox_env/python/pip/req/args.py b/src/tox/tox_env/python/pip/req/args.py
index 23f98ec3..3fd1f677 100644
--- a/src/tox/tox_env/python/pip/req/args.py
+++ b/src/tox/tox_env/python/pip/req/args.py
@@ -33,8 +33,8 @@ def _global_options(parser: ArgumentParser) -> None:
parser.add_argument("-r", "--requirement", action=AddUniqueAction, dest="requirements")
parser.add_argument("-e", "--editable", action=AddUniqueAction, dest="editables")
parser.add_argument("-f", "--find-links", action=AddUniqueAction)
- parser.add_argument("--no-binary", choices=[":all:", ":none:"]) # TODO: colon separated package names
- parser.add_argument("--only-binary", choices=[":all:", ":none:"]) # TODO: colon separated package names
+ parser.add_argument("--no-binary")
+ parser.add_argument("--only-binary")
parser.add_argument("--prefer-binary", action="store_true", default=False)
parser.add_argument("--require-hashes", action="store_true", default=False)
parser.add_argument("--pre", action="store_true", default=False)
diff --git a/src/tox/tox_env/python/pip/req/file.py b/src/tox/tox_env/python/pip/req/file.py
index d8b29558..495ef194 100644
--- a/src/tox/tox_env/python/pip/req/file.py
+++ b/src/tox/tox_env/python/pip/req/file.py
@@ -15,7 +15,7 @@ import chardet
from packaging.requirements import InvalidRequirement, Requirement
from .args import build_parser
-from .util import VCS, get_url_scheme, is_url, url_to_path
+from .util import VCS, get_url_scheme, handle_binary_option, is_url, url_to_path
# Matches environment variable-style values in '${MY_VARIABLE_1}' with the variable name consisting of only uppercase
# letters, digits or the '_' (underscore). This follows the POSIX standard defined in IEEE Std 1003.1, 2013 Edition.
@@ -341,10 +341,17 @@ class RequirementsFile:
base_opt.trusted_hosts = []
if host not in base_opt.trusted_hosts:
base_opt.trusted_hosts.append(host)
+
+ no_binary = base_opt.no_binary if hasattr(base_opt, "no_binary") else set()
+ only_binary = base_opt.only_binary if hasattr(base_opt, "only_binary") else set()
if opt.no_binary:
- base_opt.no_binary = opt.no_binary
+ handle_binary_option(opt.no_binary, no_binary, only_binary)
if opt.only_binary:
- base_opt.only_binary = opt.only_binary
+ handle_binary_option(opt.only_binary, only_binary, no_binary)
+ if no_binary:
+ base_opt.no_binary = no_binary
+ if only_binary:
+ base_opt.only_binary = only_binary
@staticmethod
def _break_args_options(line: str) -> tuple[str, str]:
diff --git a/src/tox/tox_env/python/pip/req/util.py b/src/tox/tox_env/python/pip/req/util.py
index 9914d111..2a110a1d 100644
--- a/src/tox/tox_env/python/pip/req/util.py
+++ b/src/tox/tox_env/python/pip/req/util.py
@@ -4,6 +4,8 @@ from __future__ import annotations
from urllib.parse import urlsplit
from urllib.request import url2pathname
+from packaging.utils import canonicalize_name
+
VCS = ["ftp", "ssh", "git", "hg", "bzr", "sftp", "svn"]
VALID_SCHEMAS = ["http", "https", "file"] + VCS
@@ -26,3 +28,21 @@ def url_to_path(url: str) -> str:
raise ValueError(f"non-local file URIs are not supported on this platform: {url!r}")
path = url2pathname(netloc + path)
return path
+
+
+def handle_binary_option(value: str, target: set[str], other: set[str]) -> None:
+ new = value.split(",")
+ while ":all:" in new:
+ other.clear()
+ target.clear()
+ target.add(":all:")
+ del new[: new.index(":all:") + 1]
+ if ":none:" not in new:
+ return
+ for name in new:
+ if name == ":none:":
+ target.clear()
+ continue
+ name = canonicalize_name(name)
+ other.discard(name)
+ target.add(name)
diff --git a/tests/tox_env/python/pip/req/test_file.py b/tests/tox_env/python/pip/req/test_file.py
index 9e4e51a2..d947510c 100644
--- a/tests/tox_env/python/pip/req/test_file.py
+++ b/tests/tox_env/python/pip/req/test_file.py
@@ -140,16 +140,43 @@ _REQ_FILE_TEST_CASES = [
["--use-feature", "2020-resolver", "--use-feature", "fast-deps"],
id="use-feature multiple duplicate different line",
),
- pytest.param("--no-binary :all:", {"no_binary": ":all:"}, [], ["--no-binary", ":all:"], id="no-binary all"),
- pytest.param("--no-binary :none:", {"no_binary": ":none:"}, [], ["--no-binary", ":none:"], id="no-binary none"),
- pytest.param("--only-binary :all:", {"only_binary": ":all:"}, [], ["--only-binary", ":all:"], id="only-binary all"),
+ pytest.param("--no-binary :all:", {"no_binary": {":all:"}}, [], ["--no-binary", {":all:"}], id="no-binary all"),
+ pytest.param("--no-binary :none:", {"no_binary": {":none:"}}, [], [], id="no-binary none"),
+ pytest.param(
+ "--only-binary :all:",
+ {"only_binary": {":all:"}},
+ [],
+ ["--only-binary", {":all:"}],
+ id="only-binary all",
+ ),
pytest.param(
"--only-binary :none:",
- {"only_binary": ":none:"},
+ {"only_binary": {":none:"}},
+ [],
[],
- ["--only-binary", ":none:"],
id="only-binary none",
),
+ pytest.param(
+ "--no-binary=foo --only-binary=foo",
+ {"only_binary": {"foo"}},
+ [],
+ ["--only-binary", {"foo"}],
+ id="no-binary-and-only-binary",
+ ),
+ pytest.param(
+ "--no-binary=foo --no-binary=:none:",
+ {},
+ [],
+ [],
+ id="no-binary-none-last",
+ ),
+ pytest.param(
+ "--only-binary=:none: --no-binary=foo",
+ {"no_binary": {"foo"}},
+ [],
+ ["--no-binary", {"foo"}],
+ id="no-binary-none-first",
+ ),
pytest.param("####### example-requirements.txt #######", {}, [], [], id="comment"),
pytest.param("\t##### Requirements without Version Specifiers ######", {}, [], [], id="tab and comment"),
pytest.param(" # start", {}, [], [], id="space and comment"),
@@ -289,7 +316,7 @@ def test_req_file(tmp_path: Path, req: str, opts: dict[str, Any], requirements:
req_file = RequirementsFile(requirements_txt, constraint=False)
assert req_file.as_root_args == as_args
assert str(req_file) == f"-r {requirements_txt}"
- assert vars(req_file.options) == opts
+ assert vars(req_file.options) == (opts if {":none:"} not in opts.values() else {})
found = [str(i) for i in req_file.requirements]
assert found == requirements