Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for --no-deps within deps #2678

Merged
merged 1 commit into from Dec 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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