"""Support functions for working with wheel files. """ from __future__ import absolute_import import logging from email.parser import Parser from zipfile import ZipFile from pip._vendor.packaging.utils import canonicalize_name from pip._vendor.pkg_resources import DistInfoDistribution from pip._vendor.six import PY2, ensure_str from pip._internal.exceptions import UnsupportedWheel from pip._internal.utils.pkg_resources import DictMetadata from pip._internal.utils.typing import MYPY_CHECK_RUNNING if MYPY_CHECK_RUNNING: from email.message import Message from typing import Dict, Tuple from pip._vendor.pkg_resources import Distribution if PY2: from zipfile import BadZipfile as BadZipFile else: from zipfile import BadZipFile VERSION_COMPATIBLE = (1, 0) logger = logging.getLogger(__name__) class WheelMetadata(DictMetadata): """Metadata provider that maps metadata decoding exceptions to our internal exception type. """ def __init__(self, metadata, wheel_name): # type: (Dict[str, bytes], str) -> None super(WheelMetadata, self).__init__(metadata) self._wheel_name = wheel_name def get_metadata(self, name): # type: (str) -> str try: return super(WheelMetadata, self).get_metadata(name) except UnicodeDecodeError as e: # Augment the default error with the origin of the file. raise UnsupportedWheel( "Error decoding metadata for {}: {}".format( self._wheel_name, e ) ) def pkg_resources_distribution_for_wheel(wheel_zip, name, location): # type: (ZipFile, str, str) -> Distribution """Get a pkg_resources distribution given a wheel. :raises UnsupportedWheel: on any errors """ info_dir, _ = parse_wheel(wheel_zip, name) metadata_files = [ p for p in wheel_zip.namelist() if p.startswith("{}/".format(info_dir)) ] metadata_text = {} # type: Dict[str, bytes] for path in metadata_files: # If a flag is set, namelist entries may be unicode in Python 2. # We coerce them to native str type to match the types used in the rest # of the code. This cannot fail because unicode can always be encoded # with UTF-8. full_path = ensure_str(path) _, metadata_name = full_path.split("/", 1) try: metadata_text[metadata_name] = read_wheel_metadata_file( wheel_zip, full_path ) except UnsupportedWheel as e: raise UnsupportedWheel( "{} has an invalid wheel, {}".format(name, str(e)) ) metadata = WheelMetadata(metadata_text, location) return DistInfoDistribution( location=location, metadata=metadata, project_name=name ) def parse_wheel(wheel_zip, name): # type: (ZipFile, str) -> Tuple[str, Message] """Extract information from the provided wheel, ensuring it meets basic standards. Returns the name of the .dist-info directory and the parsed WHEEL metadata. """ try: info_dir = wheel_dist_info_dir(wheel_zip, name) metadata = wheel_metadata(wheel_zip, info_dir) version = wheel_version(metadata) except UnsupportedWheel as e: raise UnsupportedWheel( "{} has an invalid wheel, {}".format(name, str(e)) ) check_compatibility(version, name) return info_dir, metadata def wheel_dist_info_dir(source, name): # type: (ZipFile, str) -> str """Returns the name of the contained .dist-info directory. Raises AssertionError or UnsupportedWheel if not found, >1 found, or it doesn't match the provided name. """ # Zip file path separators must be / subdirs = set(p.split("/", 1)[0] for p in source.namelist()) info_dirs = [s for s in subdirs if s.endswith('.dist-info')] if not info_dirs: raise UnsupportedWheel(".dist-info directory not found") if len(info_dirs) > 1: raise UnsupportedWheel( "multiple .dist-info directories found: {}".format( ", ".join(info_dirs) ) ) info_dir = info_dirs[0] info_dir_name = canonicalize_name(info_dir) canonical_name = canonicalize_name(name) if not info_dir_name.startswith(canonical_name): raise UnsupportedWheel( ".dist-info directory {!r} does not start with {!r}".format( info_dir, canonical_name ) ) # Zip file paths can be unicode or str depending on the zip entry flags, # so normalize it. return ensure_str(info_dir) def read_wheel_metadata_file(source, path): # type: (ZipFile, str) -> bytes try: return source.read(path) # BadZipFile for general corruption, KeyError for missing entry, # and RuntimeError for password-protected files except (BadZipFile, KeyError, RuntimeError) as e: raise UnsupportedWheel( "could not read {!r} file: {!r}".format(path, e) ) def wheel_metadata(source, dist_info_dir): # type: (ZipFile, str) -> Message """Return the WHEEL metadata of an extracted wheel, if possible. Otherwise, raise UnsupportedWheel. """ path = "{}/WHEEL".format(dist_info_dir) # Zip file path separators must be / wheel_contents = read_wheel_metadata_file(source, path) try: wheel_text = ensure_str(wheel_contents) except UnicodeDecodeError as e: raise UnsupportedWheel("error decoding {!r}: {!r}".format(path, e)) # FeedParser (used by Parser) does not raise any exceptions. The returned # message may have .defects populated, but for backwards-compatibility we # currently ignore them. return Parser().parsestr(wheel_text) def wheel_version(wheel_data): # type: (Message) -> Tuple[int, ...] """Given WHEEL metadata, return the parsed Wheel-Version. Otherwise, raise UnsupportedWheel. """ version_text = wheel_data["Wheel-Version"] if version_text is None: raise UnsupportedWheel("WHEEL is missing Wheel-Version") version = version_text.strip() try: return tuple(map(int, version.split('.'))) except ValueError: raise UnsupportedWheel("invalid Wheel-Version: {!r}".format(version)) def check_compatibility(version, name): # type: (Tuple[int, ...], str) -> None """Raises errors or warns if called with an incompatible Wheel-Version. pip should refuse to install a Wheel-Version that's a major series ahead of what it's compatible with (e.g 2.0 > 1.1); and warn when installing a version only minor version ahead (e.g 1.2 > 1.1). version: a 2-tuple representing a Wheel-Version (Major, Minor) name: name of wheel or package to raise exception about :raises UnsupportedWheel: when an incompatible Wheel-Version is given """ if version[0] > VERSION_COMPATIBLE[0]: raise UnsupportedWheel( "{}'s Wheel-Version ({}) is not compatible with this version " "of pip".format(name, '.'.join(map(str, version))) ) elif version > VERSION_COMPATIBLE: logger.warning( 'Installing from a newer Wheel-Version (%s)', '.'.join(map(str, version)), )
Name | Type | Size | Permission | Actions |
---|---|---|---|---|
__pycache__ | Folder | 0755 |
|
|
.__init__.pyo.40009 | File | 164 B | 0644 |
|
.appdirs.pyo.40009 | File | 1.78 KB | 0644 |
|
.compat.pyo.40009 | File | 8.62 KB | 0644 |
|
.compatibility_tags.pyo.40009 | File | 4.45 KB | 0644 |
|
.datetime.pyo.40009 | File | 625 B | 0644 |
|
.deprecation.pyo.40009 | File | 3.44 KB | 0644 |
|
.distutils_args.pyo.40009 | File | 1.6 KB | 0644 |
|
.entrypoints.pyo.40009 | File | 1.47 KB | 0644 |
|
.filetypes.pyo.40009 | File | 792 B | 0644 |
|
.glibc.pyo.40009 | File | 2.19 KB | 0644 |
|
.hashes.pyo.40009 | File | 5.68 KB | 0644 |
|
.inject_securetransport.pyo.40009 | File | 1.07 KB | 0644 |
|
.logging.pyo.40009 | File | 11.6 KB | 0644 |
|
.misc.pyo.40009 | File | 32.2 KB | 0644 |
|
.models.pyo.40009 | File | 2.55 KB | 0644 |
|
.packaging.pyo.40009 | File | 3.27 KB | 0644 |
|
.parallel.pyo.40009 | File | 3.62 KB | 0644 |
|
.pkg_resources.pyo.40009 | File | 2.39 KB | 0644 |
|
.typing.pyo.40009 | File | 1.52 KB | 0644 |
|
.virtualenv.pyo.40009 | File | 4.11 KB | 0644 |
|
.wheel.pyo.40009 | File | 7.27 KB | 0644 |
|
__init__.py | File | 0 B | 0644 |
|
__init__.pyc | File | 164 B | 0644 |
|
__init__.pyo | File | 164 B | 0644 |
|
appdirs.py | File | 1.32 KB | 0644 |
|
appdirs.pyc | File | 1.78 KB | 0644 |
|
appdirs.pyo | File | 1.78 KB | 0644 |
|
compat.py | File | 9.27 KB | 0644 |
|
compat.pyc | File | 8.62 KB | 0644 |
|
compat.pyo | File | 8.62 KB | 0644 |
|
compatibility_tags.py | File | 5.31 KB | 0644 |
|
compatibility_tags.pyc | File | 4.45 KB | 0644 |
|
compatibility_tags.pyo | File | 4.45 KB | 0644 |
|
datetime.py | File | 295 B | 0644 |
|
datetime.pyc | File | 625 B | 0644 |
|
datetime.pyo | File | 625 B | 0644 |
|
deprecation.py | File | 3.24 KB | 0644 |
|
deprecation.pyc | File | 3.44 KB | 0644 |
|
deprecation.pyo | File | 3.44 KB | 0644 |
|
direct_url_helpers.py | File | 4.26 KB | 0644 |
|
direct_url_helpers.pyc | File | 3.37 KB | 0644 |
|
direct_url_helpers.pyo | File | 3.24 KB | 0644 |
|
distutils_args.py | File | 1.32 KB | 0644 |
|
distutils_args.pyc | File | 1.6 KB | 0644 |
|
distutils_args.pyo | File | 1.6 KB | 0644 |
|
encoding.py | File | 1.25 KB | 0644 |
|
encoding.pyc | File | 1.61 KB | 0644 |
|
encoding.pyo | File | 1.56 KB | 0644 |
|
entrypoints.py | File | 1.19 KB | 0644 |
|
entrypoints.pyc | File | 1.47 KB | 0644 |
|
entrypoints.pyo | File | 1.47 KB | 0644 |
|
filesystem.py | File | 6.78 KB | 0644 |
|
filesystem.pyc | File | 7.4 KB | 0644 |
|
filesystem.pyo | File | 7.35 KB | 0644 |
|
filetypes.py | File | 571 B | 0644 |
|
filetypes.pyc | File | 792 B | 0644 |
|
filetypes.pyo | File | 792 B | 0644 |
|
glibc.py | File | 3.22 KB | 0644 |
|
glibc.pyc | File | 2.19 KB | 0644 |
|
glibc.pyo | File | 2.19 KB | 0644 |
|
hashes.py | File | 4.57 KB | 0644 |
|
hashes.pyc | File | 5.68 KB | 0644 |
|
hashes.pyo | File | 5.68 KB | 0644 |
|
inject_securetransport.py | File | 810 B | 0644 |
|
inject_securetransport.pyc | File | 1.07 KB | 0644 |
|
inject_securetransport.pyo | File | 1.07 KB | 0644 |
|
logging.py | File | 12.79 KB | 0644 |
|
logging.pyc | File | 11.6 KB | 0644 |
|
logging.pyo | File | 11.6 KB | 0644 |
|
misc.py | File | 27.67 KB | 0644 |
|
misc.pyc | File | 32.2 KB | 0644 |
|
misc.pyo | File | 32.2 KB | 0644 |
|
models.py | File | 1.17 KB | 0644 |
|
models.pyc | File | 2.55 KB | 0644 |
|
models.pyo | File | 2.55 KB | 0644 |
|
packaging.py | File | 2.96 KB | 0644 |
|
packaging.pyc | File | 3.27 KB | 0644 |
|
packaging.pyo | File | 3.27 KB | 0644 |
|
parallel.py | File | 3.32 KB | 0644 |
|
parallel.pyc | File | 3.62 KB | 0644 |
|
parallel.pyo | File | 3.62 KB | 0644 |
|
pkg_resources.py | File | 1.22 KB | 0644 |
|
pkg_resources.pyc | File | 2.39 KB | 0644 |
|
pkg_resources.pyo | File | 2.39 KB | 0644 |
|
setuptools_build.py | File | 4.94 KB | 0644 |
|
setuptools_build.pyc | File | 3.83 KB | 0644 |
|
setuptools_build.pyo | File | 3.75 KB | 0644 |
|
subprocess.py | File | 9.69 KB | 0644 |
|
subprocess.pyc | File | 6.77 KB | 0644 |
|
subprocess.pyo | File | 6.69 KB | 0644 |
|
temp_dir.py | File | 8.18 KB | 0644 |
|
temp_dir.pyc | File | 8.75 KB | 0644 |
|
temp_dir.pyo | File | 8.62 KB | 0644 |
|
typing.py | File | 1.37 KB | 0644 |
|
typing.pyc | File | 1.52 KB | 0644 |
|
typing.pyo | File | 1.52 KB | 0644 |
|
unpacking.py | File | 9.27 KB | 0644 |
|
unpacking.pyc | File | 7.83 KB | 0644 |
|
unpacking.pyo | File | 7.79 KB | 0644 |
|
urls.py | File | 1.49 KB | 0644 |
|
urls.pyc | File | 1.94 KB | 0644 |
|
urls.pyo | File | 1.8 KB | 0644 |
|
virtualenv.py | File | 3.62 KB | 0644 |
|
virtualenv.pyc | File | 4.11 KB | 0644 |
|
virtualenv.pyo | File | 4.11 KB | 0644 |
|
wheel.py | File | 7.13 KB | 0644 |
|
wheel.pyc | File | 7.27 KB | 0644 |
|
wheel.pyo | File | 7.27 KB | 0644 |
|