Skip to content

Commit fb7d14a

Browse files
authored
Merge pull request #2087 from macro1/relative-paths
Preserve relative paths for editable dependencies in output
2 parents 6209efd + 322afa7 commit fb7d14a

File tree

3 files changed

+49
-1
lines changed

3 files changed

+49
-1
lines changed

piptools/_compat/pip_compat.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from pip._internal.metadata import BaseDistribution
1111
from pip._internal.metadata.pkg_resources import Distribution as _PkgResourcesDist
1212
from pip._internal.models.direct_url import DirectUrl
13+
from pip._internal.models.link import Link
1314
from pip._internal.network.session import PipSession
1415
from pip._internal.req import InstallRequirement
1516
from pip._internal.req import parse_requirements as _parse_requirements
@@ -63,6 +64,14 @@ def _from_importlib(cls, dist: _ImportLibDist) -> Distribution:
6364
return cls(dist._dist.name, dist._dist.version, requires, dist.direct_url)
6465

6566

67+
class FileLink(Link):
68+
69+
@property
70+
def file_path(self) -> str:
71+
# overriding the actual property to bypass some validation
72+
return self._url
73+
74+
6675
def parse_requirements(
6776
filename: str,
6877
session: PipSession,
@@ -74,7 +83,14 @@ def parse_requirements(
7483
for parsed_req in _parse_requirements(
7584
filename, session, finder=finder, options=options, constraint=constraint
7685
):
77-
yield install_req_from_parsed_requirement(parsed_req, isolated=isolated)
86+
install_req = install_req_from_parsed_requirement(parsed_req, isolated=isolated)
87+
if install_req.editable and not parsed_req.requirement.startswith("file://"):
88+
# ``Link.url`` is what is saved to the output file
89+
# we set the url directly to undo the transformation in pip's Link class
90+
file_link = FileLink(install_req.link.url)
91+
file_link._url = parsed_req.requirement
92+
install_req.link = file_link
93+
yield install_req
7894

7995

8096
def create_wheel_cache(cache_dir: str, format_control: str | None = None) -> WheelCache:

tests/constants.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,6 @@
55
TEST_DATA_PATH = os.path.join(os.path.dirname(__file__), "test_data")
66
MINIMAL_WHEELS_PATH = os.path.join(TEST_DATA_PATH, "minimal_wheels")
77
PACKAGES_PATH = os.path.join(TEST_DATA_PATH, "packages")
8+
PACKAGES_RELATIVE_PATH = os.path.relpath(
9+
PACKAGES_PATH, os.path.commonpath([os.getcwd(), PACKAGES_PATH])
10+
)

tests/test_pip_compat.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from __future__ import annotations
2+
3+
import os
4+
import tempfile
5+
from pathlib import Path, PurePosixPath
6+
7+
import pytest
8+
9+
from piptools._compat.pip_compat import parse_requirements
10+
from piptools.repositories import PyPIRepository
11+
12+
from .constants import PACKAGES_RELATIVE_PATH
13+
14+
15+
def test_parse_requirements_preserve_editable_relative_path(tmp_path, repository):
16+
test_package_path = str(
17+
PurePosixPath(Path(PACKAGES_RELATIVE_PATH)) / "small_fake_a"
18+
)
19+
requirements_in_path = str(tmp_path / "requirements.in")
20+
21+
with open(requirements_in_path, "w") as requirements_in_file:
22+
requirements_in_file.write(f"-e {test_package_path}")
23+
24+
[install_requirement] = parse_requirements(
25+
requirements_in_path, session=repository.session
26+
)
27+
28+
assert install_requirement.link.url == test_package_path
29+
assert install_requirement.link.file_path == test_package_path

0 commit comments

Comments
 (0)