1
1
Fork 0
mirror of https://github.com/pypa/pip synced 2023-12-13 21:30:23 +01:00

Add json_metadata

This commit is contained in:
Stéphane Bidoul 2022-05-02 09:16:24 +02:00
parent 422719863a
commit aac65f0b86
No known key found for this signature in database
GPG key ID: BCAB2555446B5B92
3 changed files with 116 additions and 1 deletions

View file

@ -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."""

View 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

View file

@ -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"