diff --git a/src/fromager/bootstrapper.py b/src/fromager/bootstrapper.py index 77b06546..672d93e0 100644 --- a/src/fromager/bootstrapper.py +++ b/src/fromager/bootstrapper.py @@ -9,9 +9,9 @@ import tempfile import typing import zipfile -from email.parser import BytesParser from urllib.parse import urlparse +from packaging.metadata import Metadata, parse_email from packaging.requirements import Requirement from packaging.utils import NormalizedName, canonicalize_name from packaging.version import Version @@ -784,9 +784,9 @@ def _get_version_from_package_metadata( ) metadata_filename = source_dir.parent / metadata_dir_base / "METADATA" with open(metadata_filename, "rb") as f: - p = BytesParser() - metadata = p.parse(f, headersonly=True) - return Version(metadata["Version"]) + raw_metadata, _ = parse_email(f.read()) + metadata = Metadata.from_raw(raw_metadata) + return metadata.version def _resolve_prebuilt_with_history( self, diff --git a/src/fromager/candidate.py b/src/fromager/candidate.py index e7a824a3..27e4d68b 100644 --- a/src/fromager/candidate.py +++ b/src/fromager/candidate.py @@ -1,23 +1,14 @@ import typing -from email.message import EmailMessage, Message -from email.parser import BytesParser from io import BytesIO -from typing import TYPE_CHECKING from zipfile import ZipFile +from packaging.metadata import Metadata, parse_email from packaging.requirements import Requirement from packaging.utils import BuildTag, canonicalize_name from packaging.version import Version from .request_session import session -# fix for runtime errors caused by inheriting classes that are generic in stubs but not runtime -# https://mypy.readthedocs.io/en/latest/runtime_troubles.html#using-classes-that-are-generic-in-stubs-but-not-at-runtime -if TYPE_CHECKING: - Metadata = Message[str, str] -else: - Metadata = Message - class Candidate: def __init__( @@ -51,11 +42,10 @@ def metadata(self) -> Metadata: return self._metadata def _get_dependencies(self) -> typing.Iterable[Requirement]: - deps = self.metadata.get_all("Requires-Dist", []) + deps = self.metadata.requires_dist or [] extras = self.extras if self.extras else [""] - for d in deps: - r = Requirement(d) + for r in deps: if r.marker is None: yield r else: @@ -71,7 +61,8 @@ def dependencies(self) -> list[Requirement]: @property def requires_python(self) -> str | None: - return self.metadata.get("Requires-Python") + spec = self.metadata.requires_python + return str(spec) if spec is not None else None def get_metadata_for_wheel(url: str) -> Metadata: @@ -79,8 +70,11 @@ def get_metadata_for_wheel(url: str) -> Metadata: with ZipFile(BytesIO(data)) as z: for n in z.namelist(): if n.endswith(".dist-info/METADATA"): - p = BytesParser() - return p.parse(z.open(n), headersonly=True) + metadata_content = z.read(n) + raw_metadata, _ = parse_email(metadata_content) + metadata = Metadata.from_raw(raw_metadata) + return metadata - # If we didn't find the metadata, return an empty dict - return EmailMessage() + # If we didn't find the metadata, return an empty metadata object + raw_metadata, _ = parse_email(b"") + return Metadata.from_raw(raw_metadata) diff --git a/src/fromager/dependencies.py b/src/fromager/dependencies.py index 3313e47c..b8f457d4 100644 --- a/src/fromager/dependencies.py +++ b/src/fromager/dependencies.py @@ -10,7 +10,7 @@ import pkginfo import pyproject_hooks import tomlkit -from packaging.metadata import Metadata +from packaging.metadata import Metadata, parse_email from packaging.requirements import Requirement from . import build_environment, external_commands, overrides, requirements_file @@ -265,7 +265,8 @@ def parse_metadata(metadata_file: pathlib.Path, *, validate: bool = True) -> Met and core metadata version, e.g. a package with metadata 2.2 and license-expression field (added in 2.4). """ - return Metadata.from_email(metadata_file.read_bytes(), validate=validate) + raw_metadata, _ = parse_email(metadata_file.read_bytes()) + return Metadata.from_raw(raw_metadata, validate=validate) def get_install_dependencies_of_wheel(