Skip to content

Commit ebc3d1c

Browse files
committed
Refactor to parse_requirements_from_metadata
1 parent 5525e5c commit ebc3d1c

File tree

4 files changed

+52
-42
lines changed

4 files changed

+52
-42
lines changed

piptools/_compat/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from __future__ import annotations
22

3-
from .pip_compat import PIP_VERSION, install_req_from_line, parse_requirements
3+
from .pip_compat import PIP_VERSION, parse_requirements
44

5-
__all__ = ["PIP_VERSION", "install_req_from_line", "parse_requirements"]
5+
__all__ = ["PIP_VERSION", "parse_requirements"]

piptools/_compat/pip_compat.py

+1-24
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@
88
from pip._internal.network.session import PipSession
99
from pip._internal.req import InstallRequirement
1010
from pip._internal.req import parse_requirements as _parse_requirements
11-
from pip._internal.req.constructors import (
12-
install_req_from_parsed_requirement,
13-
parse_req_from_line,
14-
)
11+
from pip._internal.req.constructors import install_req_from_parsed_requirement
1512
from pip._vendor.packaging.version import parse as parse_version
1613
from pip._vendor.pkg_resources import Requirement
1714

@@ -38,26 +35,6 @@ def parse_requirements(
3835
yield install_req_from_parsed_requirement(parsed_req, isolated=isolated)
3936

4037

41-
def install_req_from_line(
42-
name: str,
43-
comes_from: str | InstallRequirement | None = None,
44-
base_package: str | None = None,
45-
base_dir: str | None = None,
46-
) -> InstallRequirement:
47-
parts = parse_req_from_line(name, comes_from)
48-
if base_package and base_dir and parts.requirement.name == base_package:
49-
name = name.replace(base_package, base_dir, 1)
50-
parts = parse_req_from_line(name, comes_from)
51-
52-
return InstallRequirement(
53-
parts.requirement,
54-
comes_from,
55-
link=parts.link,
56-
markers=parts.markers,
57-
extras=parts.extras,
58-
)
59-
60-
6138
# The Distribution interface has changed between pkg_resources and
6239
# importlib.metadata, so this compat layer allows for a consistent access
6340
# pattern. In pip 22.1, importlib.metadata became the default on Python 3.11

piptools/scripts/compile.py

+6-14
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@
1313
from click.utils import LazyFile, safecall
1414
from pip._internal.commands import create_command
1515
from pip._internal.req import InstallRequirement
16+
from pip._internal.req.constructors import install_req_from_line
1617
from pip._internal.utils.misc import redact_auth_from_url
1718

18-
from .._compat import install_req_from_line, parse_requirements
19+
from .._compat import parse_requirements
1920
from ..cache import DependencyCache
2021
from ..exceptions import NoCandidateFound, PipToolsError
2122
from ..locations import CACHE_DIR
@@ -29,6 +30,7 @@
2930
drop_extras,
3031
is_pinned_requirement,
3132
key_from_ireq,
33+
parse_requirements_from_metadata,
3234
)
3335
from ..writer import OutputWriter
3436

@@ -497,19 +499,9 @@ def cli(
497499
log.error(str(e))
498500
log.error(f"Failed to parse {os.path.abspath(src_file)}")
499501
sys.exit(2)
500-
package_name = metadata.get_all("Name")[0]
501-
comes_from = f"{package_name} ({src_file})"
502-
constraints.extend(
503-
[
504-
install_req_from_line(
505-
req,
506-
comes_from,
507-
package_name,
508-
os.path.dirname(os.path.abspath(src_file)),
509-
)
510-
for req in metadata.get_all("Requires-Dist") or []
511-
]
512-
)
502+
503+
constraints.extend(parse_requirements_from_metadata(metadata, src_file))
504+
513505
if all_extras:
514506
if extras:
515507
msg = "--extra has no effect when used with --all-extras"

piptools/utils.py

+43-2
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
import os
88
import re
99
import shlex
10-
from typing import Any, Callable, Iterable, Iterator, TypeVar, cast
10+
from typing import TYPE_CHECKING, Any, Callable, Iterable, Iterator, TypeVar, cast
1111

1212
import click
1313
from click.utils import LazyFile
1414
from pip._internal.req import InstallRequirement
15-
from pip._internal.req.constructors import install_req_from_line
15+
from pip._internal.req.constructors import install_req_from_line, parse_req_from_line
1616
from pip._internal.utils.misc import redact_auth_from_url
1717
from pip._internal.vcs import is_url
1818
from pip._vendor.packaging.markers import Marker
@@ -23,6 +23,12 @@
2323

2424
from piptools.subprocess_utils import run_python_snippet
2525

26+
if TYPE_CHECKING:
27+
from typing import Protocol
28+
else:
29+
Protocol = object
30+
31+
2632
_KT = TypeVar("_KT")
2733
_VT = TypeVar("_VT")
2834
_T = TypeVar("_T")
@@ -482,3 +488,38 @@ def copy_install_requirement(
482488
)
483489

484490
return ireq
491+
492+
493+
class PackageMetadata(Protocol):
494+
def get_all(self, name: str, failobj: _T = ...) -> list[str] | _T:
495+
...
496+
497+
498+
def parse_requirements_from_metadata(
499+
metadata: PackageMetadata, src_file: str
500+
) -> Iterator[InstallRequirement]:
501+
"""
502+
Parse the requirements from the package metadata and returns an iterator
503+
of ``InstallRequirement`` objects.
504+
505+
If the metadata includes a self-referential requirement, this function replaces
506+
the package name in the requirement string with the path to the package directory.
507+
This ensures that pip's resolver can find self-referential requirements.
508+
"""
509+
package_name = metadata.get_all("Name")[0]
510+
comes_from = f"{package_name} ({src_file})"
511+
512+
for req in metadata.get_all("Requires-Dist") or []:
513+
parts = parse_req_from_line(req, comes_from)
514+
if parts.requirement.name == package_name:
515+
package_dir = os.path.dirname(os.path.abspath(src_file))
516+
replaced_package_name = req.replace(package_name, package_dir, 1)
517+
parts = parse_req_from_line(replaced_package_name, comes_from)
518+
519+
yield InstallRequirement(
520+
parts.requirement,
521+
comes_from,
522+
link=parts.link,
523+
markers=parts.markers,
524+
extras=parts.extras,
525+
)

0 commit comments

Comments
 (0)