Воркед

This commit is contained in:
kromskii2 committed 2025-03-15 09:36:28 +07:00
1 parent ac32c55ab9
commit ee94d8e529
1138 files changed
+55771 -77901

No files matched your search

@@ -1,4 +1,6 @@
from ._dists import Distribution
from ._envs import Environment
__all__ = ["Distribution", "Environment"]
__all__ = ["NAME", "Distribution", "Environment"]
NAME = "importlib"
@@ -1,5 +1,17 @@
import importlib.metadata
from typing import Any, Optional, Protocol, cast
import os
from typing import Any, Optional, Protocol, Tuple, cast
from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
class BadMetadata(ValueError):
def __init__(self, dist: importlib.metadata.Distribution, *, reason: str) -> None:
self.dist = dist
self.reason = reason
def __str__(self) -> str:
return f"Bad metadata in {self.dist} ({self.reason})"
class BasePath(Protocol):
@@ -34,10 +46,40 @@ def get_info_location(d: importlib.metadata.Distribution) -> Optional[BasePath]:
return getattr(d, "_path", None)
def get_dist_name(dist: importlib.metadata.Distribution) -> str:
"""Get the distribution's project name.
def parse_name_and_version_from_info_directory(
dist: importlib.metadata.Distribution,
) -> Tuple[Optional[str], Optional[str]]:
"""Get a name and version from the metadata directory name.
This is much faster than reading distribution metadata.
"""
info_location = get_info_location(dist)
if info_location is None:
return None, None
stem, suffix = os.path.splitext(info_location.name)
if suffix == ".dist-info":
name, sep, version = stem.partition("-")
if sep:
return name, version
if suffix == ".egg-info":
name = stem.split("-", 1)[0]
return name, None
return None, None
def get_dist_canonical_name(dist: importlib.metadata.Distribution) -> NormalizedName:
"""Get the distribution's normalized name.
The ``name`` attribute is only available in Python 3.10 or later. We are
targeting exactly that, but Mypy does not know this.
"""
return cast(Any, dist).name
if name := parse_name_and_version_from_info_directory(dist)[0]:
return canonicalize_name(name)
name = cast(Any, dist).name
if not isinstance(name, str):
raise BadMetadata(dist, reason="invalid metadata entry 'name'")
return canonicalize_name(name)
@@ -1,8 +1,8 @@
import email.message
import importlib.metadata
import os
import pathlib
import zipfile
from os import PathLike
from typing import (
Collection,
Dict,
@@ -11,26 +11,32 @@ from typing import (
Mapping,
Optional,
Sequence,
Union,
cast,
)
from pip._vendor.packaging.requirements import Requirement
from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
from pip._vendor.packaging.version import Version
from pip._vendor.packaging.version import parse as parse_version
from pip._internal.exceptions import InvalidWheel, UnsupportedWheel
from pip._internal.metadata.base import (
BaseDistribution,
BaseEntryPoint,
DistributionVersion,
InfoPath,
Wheel,
)
from pip._internal.utils.misc import normalize_path
from pip._internal.utils.packaging import safe_extra
from pip._internal.utils.packaging import get_requirement
from pip._internal.utils.temp_dir import TempDirectory
from pip._internal.utils.wheel import parse_wheel, read_wheel_metadata_file
from ._compat import BasePath, get_dist_name
from ._compat import (
BasePath,
get_dist_canonical_name,
parse_name_and_version_from_info_directory,
)
class WheelDistribution(importlib.metadata.Distribution):
@@ -91,6 +97,11 @@ class WheelDistribution(importlib.metadata.Distribution):
raise UnsupportedWheel(error)
return text
def locate_file(self, path: Union[str, "PathLike[str]"]) -> pathlib.Path:
# This method doesn't make sense for our in-memory wheel, but the API
# requires us to define it.
raise NotImplementedError
class Distribution(BaseDistribution):
def __init__(
@@ -109,6 +120,23 @@ class Distribution(BaseDistribution):
dist = importlib.metadata.Distribution.at(info_location)
return cls(dist, info_location, info_location.parent)
@classmethod
def from_metadata_file_contents(
cls,
metadata_contents: bytes,
filename: str,
project_name: str,
) -> BaseDistribution:
# Generate temp dir to contain the metadata file, and write the file contents.
temp_dir = pathlib.Path(
TempDirectory(kind="metadata", globally_managed=True).path
)
metadata_path = temp_dir / "METADATA"
metadata_path.write_bytes(metadata_contents)
# Construct dist pointing to the newly created directory.
dist = importlib.metadata.Distribution.at(metadata_path.parent)
return cls(dist, metadata_path.parent, None)
@classmethod
def from_wheel(cls, wheel: Wheel, name: str) -> BaseDistribution:
try:
@@ -116,8 +144,6 @@ class Distribution(BaseDistribution):
dist = WheelDistribution.from_zipfile(zf, name, wheel.location)
except zipfile.BadZipFile as e:
raise InvalidWheel(wheel.location, name) from e
except UnsupportedWheel as e:
raise UnsupportedWheel(f"{name} has an invalid wheel, {e}")
return cls(dist, dist.info_location, pathlib.PurePosixPath(wheel.location))
@property
@@ -138,27 +164,20 @@ class Distribution(BaseDistribution):
return None
return normalize_path(str(self._installed_location))
def _get_dist_name_from_location(self) -> Optional[str]:
"""Try to get the name from the metadata directory name.
This is much faster than reading metadata.
"""
if self._info_location is None:
return None
stem, suffix = os.path.splitext(self._info_location.name)
if suffix not in (".dist-info", ".egg-info"):
return None
return stem.split("-", 1)[0]
@property
def canonical_name(self) -> NormalizedName:
name = self._get_dist_name_from_location() or get_dist_name(self._dist)
return canonicalize_name(name)
return get_dist_canonical_name(self._dist)
@property
def version(self) -> DistributionVersion:
def version(self) -> Version:
if version := parse_name_and_version_from_info_directory(self._dist)[1]:
return parse_version(version)
return parse_version(self._dist.version)
@property
def raw_version(self) -> str:
return self._dist.version
def is_file(self, path: InfoPath) -> bool:
return self._dist.read_text(str(path)) is not None
@@ -178,7 +197,7 @@ class Distribution(BaseDistribution):
return content
def iter_entry_points(self) -> Iterable[BaseEntryPoint]:
# importlib.metadata's EntryPoint structure sasitfies BaseEntryPoint.
# importlib.metadata's EntryPoint structure satisfies BaseEntryPoint.
return self._dist.entry_points
def _metadata_impl(self) -> email.message.Message:
@@ -189,15 +208,18 @@ class Distribution(BaseDistribution):
# until upstream can improve the protocol. (python/cpython#94952)
return cast(email.message.Message, self._dist.metadata)
def iter_provided_extras(self) -> Iterable[str]:
return (
safe_extra(extra) for extra in self.metadata.get_all("Provides-Extra", [])
)
def iter_provided_extras(self) -> Iterable[NormalizedName]:
return [
canonicalize_name(extra)
for extra in self.metadata.get_all("Provides-Extra", [])
]
def iter_dependencies(self, extras: Collection[str] = ()) -> Iterable[Requirement]:
contexts: Sequence[Dict[str, str]] = [{"extra": safe_extra(e)} for e in extras]
contexts: Sequence[Dict[str, str]] = [{"extra": e} for e in extras]
for req_string in self.metadata.get_all("Requires-Dist", []):
req = Requirement(req_string)
# strip() because email.message.Message.get_all() may return a leading \n
# in case a long header was wrapped.
req = get_requirement(req_string.strip())
if not req.marker:
yield req
elif not extras and req.marker.evaluate({"extra": ""}):
@@ -1,5 +1,6 @@
import functools
import importlib.metadata
import logging
import os
import pathlib
import sys
@@ -14,9 +15,11 @@ from pip._internal.models.wheel import Wheel
from pip._internal.utils.deprecation import deprecated
from pip._internal.utils.filetypes import WHEEL_EXTENSION
from ._compat import BasePath, get_dist_name, get_info_location
from ._compat import BadMetadata, BasePath, get_dist_canonical_name, get_info_location
from ._dists import Distribution
logger = logging.getLogger(__name__)
def _looks_like_wheel(location: str) -> bool:
if not location.endswith(WHEEL_EXTENSION):
@@ -56,11 +59,15 @@ class _DistributionFinder:
# To know exactly where we find a distribution, we have to feed in the
# paths one by one, instead of dumping the list to importlib.metadata.
for dist in importlib.metadata.distributions(path=[location]):
normalized_name = canonicalize_name(get_dist_name(dist))
if normalized_name in self._found_names:
continue
self._found_names.add(normalized_name)
info_location = get_info_location(dist)
try:
name = get_dist_canonical_name(dist)
except BadMetadata as e:
logger.warning("Skipping %s due to %s", info_location, e.reason)
continue
if name in self._found_names:
continue
self._found_names.add(name)
yield dist, info_location
def find(self, location: str) -> Iterator[BaseDistribution]:
@@ -142,8 +149,9 @@ class _DistributionFinder:
def _emit_egg_deprecation(location: Optional[str]) -> None:
deprecated(
reason=f"Loading egg at {location} is deprecated.",
replacement="to use pip for package installation.",
gone_in=None,
replacement="to use pip for package installation",
gone_in="25.1",
issue=12330,
)
@@ -166,15 +174,16 @@ class Environment(BaseEnvironment):
for location in self._paths:
yield from finder.find(location)
for dist in finder.find_eggs(location):
# _emit_egg_deprecation(dist.location) # TODO: Enable this.
_emit_egg_deprecation(dist.location)
yield dist
# This must go last because that's how pkg_resources tie-breaks.
yield from finder.find_linked(location)
def get_distribution(self, name: str) -> Optional[BaseDistribution]:
canonical_name = canonicalize_name(name)
matches = (
distribution
for distribution in self.iter_all_distributions()
if distribution.canonical_name == canonicalize_name(name)
if distribution.canonical_name == canonical_name
)
return next(matches, None)