You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
56 lines
1.8 KiB
Python
56 lines
1.8 KiB
Python
![]()
2 months ago
|
import importlib.metadata
|
||
|
from typing import Any, Optional, Protocol, cast
|
||
|
|
||
|
|
||
|
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):
|
||
|
"""A protocol that various path objects conform.
|
||
|
|
||
|
This exists because importlib.metadata uses both ``pathlib.Path`` and
|
||
|
``zipfile.Path``, and we need a common base for type hints (Union does not
|
||
|
work well since ``zipfile.Path`` is too new for our linter setup).
|
||
|
|
||
|
This does not mean to be exhaustive, but only contains things that present
|
||
|
in both classes *that we need*.
|
||
|
"""
|
||
|
|
||
|
@property
|
||
|
def name(self) -> str:
|
||
|
raise NotImplementedError()
|
||
|
|
||
|
@property
|
||
|
def parent(self) -> "BasePath":
|
||
|
raise NotImplementedError()
|
||
|
|
||
|
|
||
|
def get_info_location(d: importlib.metadata.Distribution) -> Optional[BasePath]:
|
||
|
"""Find the path to the distribution's metadata directory.
|
||
|
|
||
|
HACK: This relies on importlib.metadata's private ``_path`` attribute. Not
|
||
|
all distributions exist on disk, so importlib.metadata is correct to not
|
||
|
expose the attribute as public. But pip's code base is old and not as clean,
|
||
|
so we do this to avoid having to rewrite too many things. Hopefully we can
|
||
|
eliminate this some day.
|
||
|
"""
|
||
|
return getattr(d, "_path", None)
|
||
|
|
||
|
|
||
|
def get_dist_name(dist: importlib.metadata.Distribution) -> str:
|
||
|
"""Get the distribution's project name.
|
||
|
|
||
|
The ``name`` attribute is only available in Python 3.10 or later. We are
|
||
|
targeting exactly that, but Mypy does not know this.
|
||
|
"""
|
||
|
name = cast(Any, dist).name
|
||
|
if not isinstance(name, str):
|
||
|
raise BadMetadata(dist, reason="invalid metadata entry 'name'")
|
||
|
return name
|