Skip to content

Commit

Permalink
Support for --no-deps within deps (#2678)
Browse files Browse the repository at this point in the history
Resolves #2674
  • Loading branch information
gaborbernat committed Dec 11, 2022
1 parent 36afe0c commit 7c4f177
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 15 deletions.
1 change: 1 addition & 0 deletions docs/changelog/2674.bugfix.rst
@@ -0,0 +1 @@
Support for ``--no-deps`` flag within the :ref:`deps` - by :user:`gaborbernat`.
5 changes: 2 additions & 3 deletions src/tox/tox_env/python/pip/pip_install.py
Expand Up @@ -10,8 +10,7 @@
from tox.config.main import Config
from tox.config.types import Command
from tox.execute.request import StdinSource
from tox.report import HandledError
from tox.tox_env.errors import Recreate
from tox.tox_env.errors import Fail, Recreate
from tox.tox_env.installer import Installer
from tox.tox_env.package import PathPackage
from tox.tox_env.python.api import Python
Expand Down Expand Up @@ -92,7 +91,7 @@ def _install_requirement_file(self, arguments: PythonDeps, section: str, of_type
try:
new_options, new_reqs = arguments.unroll()
except ValueError as exception:
raise HandledError(f"{exception} for tox env py within deps")
raise Fail(f"{exception} for tox env py within deps")
new_requirements: list[str] = []
new_constraints: list[str] = []
for req in new_reqs:
Expand Down
16 changes: 11 additions & 5 deletions src/tox/tox_env/python/pip/req/file.py
Expand Up @@ -137,6 +137,10 @@ def __init__(self, path: Path, constraint: bool) -> None:
self._as_root_args: list[str] | None = None
self._parser_private: ArgumentParser | None = None

@property
def _req_parser(self) -> RequirementsFile:
return self

def __str__(self) -> str:
return f"{'-c' if self.is_constraint else '-r'} {self.path}"

Expand All @@ -162,8 +166,12 @@ def requirements(self) -> list[ParsedRequirement]:
def _parser(self) -> ArgumentParser:
if self._parser_private is None:
self._parser_private = build_parser()
self._extend_parser(self._parser_private)
return self._parser_private

def _extend_parser(self, parser: ArgumentParser) -> None: # noqa: U100
...

def _ensure_requirements_parsed(self) -> None:
if self._requirements is None:
self._requirements = self._parse_requirements(opt=self._opt, recurse=True)
Expand Down Expand Up @@ -204,7 +212,7 @@ def _parse_and_recurse(self, filename: str, constraint: bool, recurse: bool) ->
# do a join so relative paths work
req_path = os.path.join(os.path.dirname(filename), req_path)
if recurse:
yield from self._parse_and_recurse(req_path, nested_constraint, recurse)
yield from self._req_parser._parse_and_recurse(req_path, nested_constraint, recurse)
else:
line.filename = req_path
yield line
Expand Down Expand Up @@ -278,8 +286,7 @@ def _handle_requirement_line(line: ParsedLine) -> ParsedRequirement:
req_options["hash"] = hash_values
return ParsedRequirement(line.requirement, req_options, line.filename, line.lineno)

@staticmethod
def _merge_option_line(base_opt: Namespace, opt: Namespace, filename: str) -> None: # noqa: C901
def _merge_option_line(self, base_opt: Namespace, opt: Namespace, filename: str) -> None: # noqa: C901
# percolate options upward
if opt.requirements:
if not hasattr(base_opt, "requirements"):
Expand Down Expand Up @@ -428,8 +435,7 @@ def as_root_args(self) -> list[str]:
self._as_root_args = result
return self._as_root_args

@staticmethod
def _option_to_args(opt: Namespace) -> list[str]:
def _option_to_args(self, opt: Namespace) -> list[str]:
result: list[str] = []
for req in getattr(opt, "requirements", []):
result.extend(("-r", req))
Expand Down
34 changes: 27 additions & 7 deletions src/tox/tox_env/python/pip/req_file.py
@@ -1,7 +1,7 @@
from __future__ import annotations

import re
from argparse import Namespace
from argparse import ArgumentParser, Namespace
from pathlib import Path

from .req.file import ParsedRequirement, ReqFileLines, RequirementsFile
Expand All @@ -16,6 +16,27 @@ def __init__(self, raw: str, root: Path):
super().__init__(root / "tox.ini", constraint=False)
self._raw = self._normalize_raw(raw)
self._unroll: tuple[list[str], list[str]] | None = None
self._req_parser_: RequirementsFile | None = None

def _extend_parser(self, parser: ArgumentParser) -> None:
parser.add_argument("--no-deps", action="store_true", default=False)

def _merge_option_line(self, base_opt: Namespace, opt: Namespace, filename: str) -> None:
super()._merge_option_line(base_opt, opt, filename)
if opt.no_deps:
base_opt.no_deps = True

def _option_to_args(self, opt: Namespace) -> list[str]:
result = super()._option_to_args(opt)
if getattr(opt, "no_deps", False):
result.append("--no-deps")
return result

@property
def _req_parser(self) -> RequirementsFile:
if self._req_parser_ is None:
self._req_parser_ = RequirementsFile(path=self._path, constraint=False)
return self._req_parser_

def _get_file_content(self, url: str) -> str:
if self._is_url_self(url):
Expand Down Expand Up @@ -69,14 +90,13 @@ def _parse_requirements(self, opt: Namespace, recurse: bool) -> list[ParsedRequi
# check for any invalid options in the deps list
# (requirements recursively included from other files are not checked)
requirements = super()._parse_requirements(opt, recurse)
for r in requirements:
if r.from_file != str(self.path):
for req in requirements:
if req.from_file != str(self.path):
continue
for illegal_option in self._illegal_options:
if r.options.get(illegal_option):
raise ValueError(
f"Cannot use --{illegal_option} in deps list, it must be in requirements file. ({r})",
)
if req.options.get(illegal_option):
msg = f"Cannot use --{illegal_option} in deps list, it must be in requirements file. ({req})"
raise ValueError(msg)
return requirements

def unroll(self) -> tuple[list[str], list[str]]:
Expand Down
21 changes: 21 additions & 0 deletions tests/tox_env/python/pip/test_req_file.py
Expand Up @@ -37,3 +37,24 @@ def test_deps_with_requirements_with_hash(tmp_path: Path) -> None:
assert str(parsed_req.requirement) == "foo==1"
assert parsed_req.options == {"hash": [exp_hash]}
assert parsed_req.from_file == str(requirements)


def test_deps_with_no_deps(tmp_path: Path) -> None:
"""deps with --hash should raise an exception."""
(tmp_path / "r.txt").write_text("urrlib3")
python_deps = PythonDeps(raw="-rr.txt\n--no-deps", root=tmp_path)

assert len(python_deps.requirements) == 1
parsed_req = python_deps.requirements[0]
assert str(parsed_req.requirement) == "urrlib3"

assert python_deps.options.no_deps is True
assert python_deps.as_root_args == ["-r", "r.txt", "--no-deps"]


def test_req_with_no_deps(tmp_path: Path) -> None:
"""deps with --hash should raise an exception."""
(tmp_path / "r.txt").write_text("--no-deps")
python_deps = PythonDeps(raw="-rr.txt", root=tmp_path)
with pytest.raises(ValueError, match="unrecognized arguments: --no-deps"):
python_deps.requirements

0 comments on commit 7c4f177

Please sign in to comment.