From 041677e2c0f78ba065b90f54775f351226034372 Mon Sep 17 00:00:00 2001 From: q0w <43147888+q0w@users.noreply.github.com> Date: Thu, 12 Jan 2023 02:43:29 +0300 Subject: [PATCH] Allow package names with env markers with pip binary options --- docs/changelog/2814.bugfix.rst | 1 + src/tox/tox_env/python/pip/req/args.py | 28 +++++++++++++++++++++-- src/tox/tox_env/python/pip/req/file.py | 13 +++-------- tests/tox_env/python/pip/req/test_file.py | 7 ++++++ 4 files changed, 37 insertions(+), 12 deletions(-) create mode 100644 docs/changelog/2814.bugfix.rst diff --git a/docs/changelog/2814.bugfix.rst b/docs/changelog/2814.bugfix.rst new file mode 100644 index 000000000..76a99c530 --- /dev/null +++ b/docs/changelog/2814.bugfix.rst @@ -0,0 +1 @@ +Allow using package names with env markers for pip's ``--no-binary`` and ``--only-binary`` options - by :user:`q0w`. diff --git a/src/tox/tox_env/python/pip/req/args.py b/src/tox/tox_env/python/pip/req/args.py index 3fd1f6779..b1e01cdf9 100644 --- a/src/tox/tox_env/python/pip/req/args.py +++ b/src/tox/tox_env/python/pip/req/args.py @@ -5,6 +5,8 @@ from argparse import Action, ArgumentParser, ArgumentTypeError, Namespace from typing import IO, Any, NoReturn, Sequence +from tox.tox_env.python.pip.req.util import handle_binary_option + class _OurArgumentParser(ArgumentParser): def print_usage(self, file: IO[str] | None = None) -> None: # noqa: U100 @@ -33,8 +35,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") - parser.add_argument("--only-binary") + parser.add_argument("--no-binary", action=BinaryAction, nargs="+") + parser.add_argument("--only-binary", action=BinaryAction, nargs="+") 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) @@ -90,3 +92,25 @@ def __call__( current = getattr(namespace, self.dest) if values not in current: current.append(values) + + +class BinaryAction(Action): + def __call__( + self, + parser: ArgumentParser, # noqa: U100 + namespace: Namespace, + values: str | Sequence[Any] | None, + option_string: str | None = None, # noqa: U100 + ) -> None: + if getattr(namespace, "no_binary", None) is None: + namespace.no_binary = set() + if getattr(namespace, "only_binary", None) is None: + namespace.only_binary = set() + + args = ( + (namespace.no_binary, namespace.only_binary) + if self.dest == "no_binary" + else (namespace.only_binary, namespace.no_binary) + ) + assert values is not None + handle_binary_option(values[0], *args) diff --git a/src/tox/tox_env/python/pip/req/file.py b/src/tox/tox_env/python/pip/req/file.py index 495ef194f..d8b295589 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 @@ from packaging.requirements import InvalidRequirement, Requirement from .args import build_parser -from .util import VCS, get_url_scheme, handle_binary_option, is_url, url_to_path +from .util import VCS, get_url_scheme, 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,17 +341,10 @@ def _merge_option_line(self, base_opt: Namespace, opt: Namespace, filename: str) 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: - handle_binary_option(opt.no_binary, no_binary, only_binary) + base_opt.no_binary = opt.no_binary if 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 + base_opt.only_binary = opt.only_binary @staticmethod def _break_args_options(line: str) -> tuple[str, str]: diff --git a/tests/tox_env/python/pip/req/test_file.py b/tests/tox_env/python/pip/req/test_file.py index d947510c7..e387a8acd 100644 --- a/tests/tox_env/python/pip/req/test_file.py +++ b/tests/tox_env/python/pip/req/test_file.py @@ -177,6 +177,13 @@ ["--no-binary", {"foo"}], id="no-binary-none-first", ), + pytest.param( + "--only-binary foo; sys_platform == 'aix'", + {"only_binary": {"foo;"}}, + [], + ["--only-binary", {"foo;"}], + id="only-binary-and-env-marker", + ), 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"),