Add DictMetadata for adapting zip data to Distribution

pkg_resources.Distribution classes delegate to the IMetadataProvider
implementation provided at construction. This is the one we'll use for
adapting a ZipFile to pkg_resources.DistInfoDistribution.
This commit is contained in:
Chris Hunt 2020-01-01 18:52:58 -05:00
parent c3ab0a0b13
commit 20706eb93f
2 changed files with 101 additions and 0 deletions

View File

@ -0,0 +1,44 @@
from pip._vendor.pkg_resources import yield_lines
from pip._vendor.six import ensure_str
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
if MYPY_CHECK_RUNNING:
from typing import Dict, Iterable, List
class DictMetadata(object):
"""IMetadataProvider that reads metadata files from a dictionary.
"""
def __init__(self, metadata):
# type: (Dict[str, bytes]) -> None
self._metadata = metadata
def has_metadata(self, name):
# type: (str) -> bool
return name in self._metadata
def get_metadata(self, name):
# type: (str) -> str
try:
return ensure_str(self._metadata[name])
except UnicodeDecodeError as e:
# Mirrors handling done in pkg_resources.NullProvider.
e.reason += " in {} file".format(name)
raise
def get_metadata_lines(self, name):
# type: (str) -> Iterable[str]
return yield_lines(self.get_metadata(name))
def metadata_isdir(self, name):
# type: (str) -> bool
return False
def metadata_listdir(self, name):
# type: (str) -> List[str]
return []
def run_script(self, script_name, namespace):
# type: (str, str) -> None
pass

View File

@ -0,0 +1,57 @@
from email.message import Message
import pytest
from pip._vendor.pkg_resources import DistInfoDistribution, Requirement
from pip._vendor.six import ensure_binary
from pip._internal.utils.packaging import get_metadata, get_requires_python
from pip._internal.utils.pkg_resources import DictMetadata
from tests.lib import skip_if_python2
def test_dict_metadata_works():
name = "simple"
version = "0.1.0"
require_a = "a==1.0"
require_b = "b==1.1; extra == 'also_b'"
requires = [require_a, require_b, "c==1.2; extra == 'also_c'"]
extras = ["also_b", "also_c"]
requires_python = ">=3"
metadata = Message()
metadata["Name"] = name
metadata["Version"] = version
for require in requires:
metadata["Requires-Dist"] = require
for extra in extras:
metadata["Provides-Extra"] = extra
metadata["Requires-Python"] = requires_python
inner_metadata = DictMetadata({
"METADATA": ensure_binary(metadata.as_string())
})
dist = DistInfoDistribution(
location="<in-memory>", metadata=inner_metadata, project_name=name
)
assert name == dist.project_name
assert version == dist.version
assert set(extras) == set(dist.extras)
assert [Requirement.parse(require_a)] == dist.requires([])
assert [
Requirement.parse(require_a), Requirement.parse(require_b)
] == dist.requires(["also_b"])
assert metadata.as_string() == get_metadata(dist).as_string()
assert requires_python == get_requires_python(dist)
# Metadata is not decoded on Python 2, so no chance for error.
@skip_if_python2
def test_dict_metadata_throws_on_bad_unicode():
metadata = DictMetadata({
"METADATA": b"\xff"
})
with pytest.raises(UnicodeDecodeError) as e:
metadata.get_metadata("METADATA")
assert "METADATA" in str(e.value)