mirror of
https://github.com/pypa/pip
synced 2023-12-13 21:30:23 +01:00
Add json_metadata
This commit is contained in:
parent
422719863a
commit
aac65f0b86
|
@ -9,8 +9,10 @@ import zipfile
|
|||
from typing import (
|
||||
IO,
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Collection,
|
||||
Container,
|
||||
Dict,
|
||||
Iterable,
|
||||
Iterator,
|
||||
List,
|
||||
|
@ -38,6 +40,8 @@ from pip._internal.utils.misc import is_local, normalize_path
|
|||
from pip._internal.utils.packaging import safe_extra
|
||||
from pip._internal.utils.urls import url_to_path
|
||||
|
||||
from .json import msg_to_json
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Protocol
|
||||
else:
|
||||
|
@ -379,6 +383,17 @@ class BaseDistribution(Protocol):
|
|||
"""
|
||||
return self._metadata_cached()
|
||||
|
||||
@property
|
||||
def json_metadata(self) -> Dict[str, Any]:
|
||||
"""PEP 566 compliant JSON-serializable representation of METADATA or PKG-INFO.
|
||||
|
||||
This should return an empty dict if the metadata file is unavailable.
|
||||
|
||||
:raises NoneMetadataError: If the metadata file is available, but does
|
||||
not contain valid metadata.
|
||||
"""
|
||||
return msg_to_json(self.metadata)
|
||||
|
||||
@property
|
||||
def metadata_version(self) -> Optional[str]:
|
||||
"""Value of "Metadata-Version:" in distribution metadata, if available."""
|
||||
|
|
80
src/pip/_internal/metadata/json.py
Normal file
80
src/pip/_internal/metadata/json.py
Normal file
|
@ -0,0 +1,80 @@
|
|||
# Extracted from https://github.com/pfmoore/pkg_metadata
|
||||
|
||||
from email.header import Header, decode_header, make_header
|
||||
from email.message import Message
|
||||
from typing import Any, Dict, List, Union
|
||||
|
||||
METADATA_FIELDS = [
|
||||
# Name, Multiple-Use
|
||||
("Metadata-Version", False),
|
||||
("Name", False),
|
||||
("Version", False),
|
||||
("Dynamic", True),
|
||||
("Platform", True),
|
||||
("Supported-Platform", True),
|
||||
("Summary", False),
|
||||
("Description", False),
|
||||
("Description-Content-Type", False),
|
||||
("Keywords", False),
|
||||
("Home-page", False),
|
||||
("Download-URL", False),
|
||||
("Author", False),
|
||||
("Author-email", False),
|
||||
("Maintainer", False),
|
||||
("Maintainer-email", False),
|
||||
("License", False),
|
||||
("Classifier", True),
|
||||
("Requires-Dist", True),
|
||||
("Requires-Python", False),
|
||||
("Requires-External", True),
|
||||
("Project-URL", True),
|
||||
("Provides-Extra", True),
|
||||
("Provides-Dist", True),
|
||||
("Obsoletes-Dist", True),
|
||||
]
|
||||
|
||||
|
||||
def json_name(field: str) -> str:
|
||||
return field.lower().replace("-", "_")
|
||||
|
||||
|
||||
def msg_to_json(msg: Message) -> Dict[str, Any]:
|
||||
def sanitise_header(h: Union[Header, str]) -> str:
|
||||
if isinstance(h, Header):
|
||||
chunks = []
|
||||
for bytes, encoding in decode_header(h):
|
||||
if encoding == "unknown-8bit":
|
||||
try:
|
||||
# See if UTF-8 works
|
||||
bytes.decode("utf-8")
|
||||
encoding = "utf-8"
|
||||
except UnicodeDecodeError:
|
||||
# If not, latin1 at least won't fail
|
||||
encoding = "latin1"
|
||||
chunks.append((bytes, encoding))
|
||||
return str(make_header(chunks))
|
||||
return str(h)
|
||||
|
||||
result = {}
|
||||
for field, multi in METADATA_FIELDS:
|
||||
if field not in msg:
|
||||
continue
|
||||
key = json_name(field)
|
||||
if multi:
|
||||
value: Union[str, List[str]] = [
|
||||
sanitise_header(v) for v in msg.get_all(field)
|
||||
]
|
||||
else:
|
||||
value = sanitise_header(msg.get(field))
|
||||
if key == "keywords":
|
||||
if "," in value:
|
||||
value = [v.strip() for v in value.split(",")]
|
||||
else:
|
||||
value = value.split()
|
||||
result[key] = value
|
||||
|
||||
payload = msg.get_payload()
|
||||
if payload:
|
||||
result["description"] = payload
|
||||
|
||||
return result
|
|
@ -6,8 +6,14 @@ from unittest import mock
|
|||
import pytest
|
||||
from pip._vendor.packaging.utils import NormalizedName
|
||||
|
||||
from pip._internal.metadata import BaseDistribution, get_directory_distribution
|
||||
from pip._internal.metadata import (
|
||||
BaseDistribution,
|
||||
get_directory_distribution,
|
||||
get_wheel_distribution,
|
||||
)
|
||||
from pip._internal.metadata.base import FilesystemWheel
|
||||
from pip._internal.models.direct_url import DIRECT_URL_METADATA_NAME, ArchiveInfo
|
||||
from tests.lib.wheel import make_wheel
|
||||
|
||||
|
||||
@mock.patch.object(BaseDistribution, "read_text", side_effect=FileNotFoundError)
|
||||
|
@ -82,3 +88,17 @@ def test_dist_get_direct_url_valid_metadata(mock_read_text: mock.Mock) -> None:
|
|||
mock_read_text.assert_called_once_with(DIRECT_URL_METADATA_NAME)
|
||||
assert direct_url.url == "https://e.c/p.tgz"
|
||||
assert isinstance(direct_url.info, ArchiveInfo)
|
||||
|
||||
|
||||
def test_json_metadata(tmp_path: Path) -> None:
|
||||
"""Basic test of BaseDistribution json_metadata.
|
||||
|
||||
More tests are available in the original pkg_metadata project where this
|
||||
function comes from, and which we may vendor in the future.
|
||||
"""
|
||||
wheel_path = make_wheel(name="pkga", version="1.0.1").save_to_dir(tmp_path)
|
||||
wheel = FilesystemWheel(wheel_path)
|
||||
dist = get_wheel_distribution(wheel, "pkga")
|
||||
json_metadata = dist.json_metadata
|
||||
assert json_metadata["name"] == "pkga"
|
||||
assert json_metadata["version"] == "1.0.1"
|
||||
|
|
Loading…
Reference in a new issue