Skip to content

Commit 30ed588

Browse files
authored
Merge pull request #2260 from sirosen/improve-relpath-handling
Fix pip-compile handling of `-r` paths which are not subpaths of the cwd
2 parents 0aae4ae + d7da20b commit 30ed588

File tree

5 files changed

+81
-3
lines changed

5 files changed

+81
-3
lines changed

changelog.d/2231.bugfix.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fixed `pip-compile` to handle relative path includes which are not subpaths of
2+
the current working directory -- by {user}`sirosen`.

changelog.d/2260.bugfix.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2231.bugfix.md

piptools/_compat/path_compat.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
"""
2+
Compatibility helpers for working with paths and :mod:`pathlib` across platforms
3+
and Python versions.
4+
"""
5+
6+
from __future__ import annotations
7+
8+
import os.path
9+
import pathlib
10+
import sys
11+
12+
__all__ = ("relative_to_walk_up",)
13+
14+
15+
def relative_to_walk_up(path: pathlib.Path, start: pathlib.Path) -> pathlib.Path:
16+
"""
17+
Compute a relative path allowing for the input to not be a subpath of the start.
18+
19+
This is a compatibility helper for ``pathlib.Path.relative_to(..., walk_up=True)``
20+
on all Python versions. (``walk_up: bool`` is Python 3.12+)
21+
"""
22+
# prefer `pathlib.Path.relative_to` where available
23+
if sys.version_info >= (3, 12):
24+
return path.relative_to(start, walk_up=True)
25+
26+
str_result = os.path.relpath(path, start=start)
27+
return pathlib.Path(str_result)

piptools/_compat/pip_compat.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
from pip._internal.req.constructors import install_req_from_parsed_requirement
1919
from pip._vendor.pkg_resources import Requirement
2020

21+
from .path_compat import relative_to_walk_up
22+
2123
# The Distribution interface has changed between pkg_resources and
2224
# importlib.metadata, so this compat layer allows for a consistent access
2325
# pattern. In pip 22.1, importlib.metadata became the default on Python 3.11
@@ -152,7 +154,7 @@ def _relativize_comes_from_location(original_comes_from: str, /) -> str:
152154
return f"{prefix} {file_path.as_posix()}"
153155

154156
# make it relative to the current working dir
155-
suffix = file_path.relative_to(pathlib.Path.cwd()).as_posix()
157+
suffix = relative_to_walk_up(file_path, pathlib.Path.cwd()).as_posix()
156158
return f"{prefix}{space_sep}{suffix}"
157159

158160

tests/test_cli_compile.py

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,11 @@ class TestFilesCollection:
6565
"""
6666

6767
# the name for the collection of files
68-
name: str
68+
name: str = "<unnamed test file collection>"
6969
# static or computed contents
70-
contents: dict[str, str | typing.Callable[[pathlib.Path], str]]
70+
contents: dict[str, str | typing.Callable[[pathlib.Path], str]] = dataclasses.field(
71+
default_factory=dict
72+
)
7173

7274
def __str__(self) -> str:
7375
return self.name
@@ -4047,6 +4049,50 @@ def test_second_order_requirements_relative_path_in_separate_dir(
40474049
)
40484050

40494051

4052+
def test_second_order_requirements_can_be_in_parent_of_cwd(
4053+
pip_conf,
4054+
runner,
4055+
tmp_path,
4056+
monkeypatch,
4057+
pip_produces_absolute_paths,
4058+
):
4059+
"""
4060+
Test handling of ``-r`` includes when the included requirements file is in the
4061+
parent of the current working directory.
4062+
"""
4063+
test_files_collection = TestFilesCollection(
4064+
contents={
4065+
"subdir1/requirements.in": "-r ../requirements2.in\n",
4066+
"requirements2.in": "small-fake-a\n",
4067+
}
4068+
)
4069+
test_files_collection.populate(tmp_path)
4070+
4071+
with monkeypatch.context() as revertable_ctx:
4072+
# cd into the subdir where the initial requirements are
4073+
revertable_ctx.chdir(tmp_path / "subdir1")
4074+
out = runner.invoke(
4075+
cli,
4076+
[
4077+
"--output-file",
4078+
"-",
4079+
"--quiet",
4080+
"--no-header",
4081+
"--no-emit-options",
4082+
"-r",
4083+
"requirements.in",
4084+
],
4085+
)
4086+
4087+
assert out.exit_code == 0
4088+
assert out.stdout == dedent(
4089+
"""\
4090+
small-fake-a==0.2
4091+
# via -r ../requirements2.in
4092+
"""
4093+
)
4094+
4095+
40504096
@pytest.mark.parametrize(
40514097
"input_path_absolute", (True, False), ids=("absolute-input", "relative-input")
40524098
)

0 commit comments

Comments
 (0)