Mercurial > repos > guerler > hhblits
comparison lib/python3.8/site-packages/pip/_internal/utils/wheel.py @ 0:9e54283cc701 draft
"planemo upload commit d12c32a45bcd441307e632fca6d9af7d60289d44"
| author | guerler |
|---|---|
| date | Mon, 27 Jul 2020 03:47:31 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:9e54283cc701 |
|---|---|
| 1 """Support functions for working with wheel files. | |
| 2 """ | |
| 3 | |
| 4 from __future__ import absolute_import | |
| 5 | |
| 6 import logging | |
| 7 from email.parser import Parser | |
| 8 from zipfile import ZipFile | |
| 9 | |
| 10 from pip._vendor.packaging.utils import canonicalize_name | |
| 11 from pip._vendor.pkg_resources import DistInfoDistribution | |
| 12 from pip._vendor.six import PY2, ensure_str | |
| 13 | |
| 14 from pip._internal.exceptions import UnsupportedWheel | |
| 15 from pip._internal.utils.pkg_resources import DictMetadata | |
| 16 from pip._internal.utils.typing import MYPY_CHECK_RUNNING | |
| 17 | |
| 18 if MYPY_CHECK_RUNNING: | |
| 19 from email.message import Message | |
| 20 from typing import Dict, Tuple | |
| 21 | |
| 22 from pip._vendor.pkg_resources import Distribution | |
| 23 | |
| 24 if PY2: | |
| 25 from zipfile import BadZipfile as BadZipFile | |
| 26 else: | |
| 27 from zipfile import BadZipFile | |
| 28 | |
| 29 | |
| 30 VERSION_COMPATIBLE = (1, 0) | |
| 31 | |
| 32 | |
| 33 logger = logging.getLogger(__name__) | |
| 34 | |
| 35 | |
| 36 class WheelMetadata(DictMetadata): | |
| 37 """Metadata provider that maps metadata decoding exceptions to our | |
| 38 internal exception type. | |
| 39 """ | |
| 40 def __init__(self, metadata, wheel_name): | |
| 41 # type: (Dict[str, bytes], str) -> None | |
| 42 super(WheelMetadata, self).__init__(metadata) | |
| 43 self._wheel_name = wheel_name | |
| 44 | |
| 45 def get_metadata(self, name): | |
| 46 # type: (str) -> str | |
| 47 try: | |
| 48 return super(WheelMetadata, self).get_metadata(name) | |
| 49 except UnicodeDecodeError as e: | |
| 50 # Augment the default error with the origin of the file. | |
| 51 raise UnsupportedWheel( | |
| 52 "Error decoding metadata for {}: {}".format( | |
| 53 self._wheel_name, e | |
| 54 ) | |
| 55 ) | |
| 56 | |
| 57 | |
| 58 def pkg_resources_distribution_for_wheel(wheel_zip, name, location): | |
| 59 # type: (ZipFile, str, str) -> Distribution | |
| 60 """Get a pkg_resources distribution given a wheel. | |
| 61 | |
| 62 :raises UnsupportedWheel: on any errors | |
| 63 """ | |
| 64 info_dir, _ = parse_wheel(wheel_zip, name) | |
| 65 | |
| 66 metadata_files = [ | |
| 67 p for p in wheel_zip.namelist() if p.startswith("{}/".format(info_dir)) | |
| 68 ] | |
| 69 | |
| 70 metadata_text = {} # type: Dict[str, bytes] | |
| 71 for path in metadata_files: | |
| 72 # If a flag is set, namelist entries may be unicode in Python 2. | |
| 73 # We coerce them to native str type to match the types used in the rest | |
| 74 # of the code. This cannot fail because unicode can always be encoded | |
| 75 # with UTF-8. | |
| 76 full_path = ensure_str(path) | |
| 77 _, metadata_name = full_path.split("/", 1) | |
| 78 | |
| 79 try: | |
| 80 metadata_text[metadata_name] = read_wheel_metadata_file( | |
| 81 wheel_zip, full_path | |
| 82 ) | |
| 83 except UnsupportedWheel as e: | |
| 84 raise UnsupportedWheel( | |
| 85 "{} has an invalid wheel, {}".format(name, str(e)) | |
| 86 ) | |
| 87 | |
| 88 metadata = WheelMetadata(metadata_text, location) | |
| 89 | |
| 90 return DistInfoDistribution( | |
| 91 location=location, metadata=metadata, project_name=name | |
| 92 ) | |
| 93 | |
| 94 | |
| 95 def parse_wheel(wheel_zip, name): | |
| 96 # type: (ZipFile, str) -> Tuple[str, Message] | |
| 97 """Extract information from the provided wheel, ensuring it meets basic | |
| 98 standards. | |
| 99 | |
| 100 Returns the name of the .dist-info directory and the parsed WHEEL metadata. | |
| 101 """ | |
| 102 try: | |
| 103 info_dir = wheel_dist_info_dir(wheel_zip, name) | |
| 104 metadata = wheel_metadata(wheel_zip, info_dir) | |
| 105 version = wheel_version(metadata) | |
| 106 except UnsupportedWheel as e: | |
| 107 raise UnsupportedWheel( | |
| 108 "{} has an invalid wheel, {}".format(name, str(e)) | |
| 109 ) | |
| 110 | |
| 111 check_compatibility(version, name) | |
| 112 | |
| 113 return info_dir, metadata | |
| 114 | |
| 115 | |
| 116 def wheel_dist_info_dir(source, name): | |
| 117 # type: (ZipFile, str) -> str | |
| 118 """Returns the name of the contained .dist-info directory. | |
| 119 | |
| 120 Raises AssertionError or UnsupportedWheel if not found, >1 found, or | |
| 121 it doesn't match the provided name. | |
| 122 """ | |
| 123 # Zip file path separators must be / | |
| 124 subdirs = list(set(p.split("/")[0] for p in source.namelist())) | |
| 125 | |
| 126 info_dirs = [s for s in subdirs if s.endswith('.dist-info')] | |
| 127 | |
| 128 if not info_dirs: | |
| 129 raise UnsupportedWheel(".dist-info directory not found") | |
| 130 | |
| 131 if len(info_dirs) > 1: | |
| 132 raise UnsupportedWheel( | |
| 133 "multiple .dist-info directories found: {}".format( | |
| 134 ", ".join(info_dirs) | |
| 135 ) | |
| 136 ) | |
| 137 | |
| 138 info_dir = info_dirs[0] | |
| 139 | |
| 140 info_dir_name = canonicalize_name(info_dir) | |
| 141 canonical_name = canonicalize_name(name) | |
| 142 if not info_dir_name.startswith(canonical_name): | |
| 143 raise UnsupportedWheel( | |
| 144 ".dist-info directory {!r} does not start with {!r}".format( | |
| 145 info_dir, canonical_name | |
| 146 ) | |
| 147 ) | |
| 148 | |
| 149 # Zip file paths can be unicode or str depending on the zip entry flags, | |
| 150 # so normalize it. | |
| 151 return ensure_str(info_dir) | |
| 152 | |
| 153 | |
| 154 def read_wheel_metadata_file(source, path): | |
| 155 # type: (ZipFile, str) -> bytes | |
| 156 try: | |
| 157 return source.read(path) | |
| 158 # BadZipFile for general corruption, KeyError for missing entry, | |
| 159 # and RuntimeError for password-protected files | |
| 160 except (BadZipFile, KeyError, RuntimeError) as e: | |
| 161 raise UnsupportedWheel( | |
| 162 "could not read {!r} file: {!r}".format(path, e) | |
| 163 ) | |
| 164 | |
| 165 | |
| 166 def wheel_metadata(source, dist_info_dir): | |
| 167 # type: (ZipFile, str) -> Message | |
| 168 """Return the WHEEL metadata of an extracted wheel, if possible. | |
| 169 Otherwise, raise UnsupportedWheel. | |
| 170 """ | |
| 171 path = "{}/WHEEL".format(dist_info_dir) | |
| 172 # Zip file path separators must be / | |
| 173 wheel_contents = read_wheel_metadata_file(source, path) | |
| 174 | |
| 175 try: | |
| 176 wheel_text = ensure_str(wheel_contents) | |
| 177 except UnicodeDecodeError as e: | |
| 178 raise UnsupportedWheel("error decoding {!r}: {!r}".format(path, e)) | |
| 179 | |
| 180 # FeedParser (used by Parser) does not raise any exceptions. The returned | |
| 181 # message may have .defects populated, but for backwards-compatibility we | |
| 182 # currently ignore them. | |
| 183 return Parser().parsestr(wheel_text) | |
| 184 | |
| 185 | |
| 186 def wheel_version(wheel_data): | |
| 187 # type: (Message) -> Tuple[int, ...] | |
| 188 """Given WHEEL metadata, return the parsed Wheel-Version. | |
| 189 Otherwise, raise UnsupportedWheel. | |
| 190 """ | |
| 191 version_text = wheel_data["Wheel-Version"] | |
| 192 if version_text is None: | |
| 193 raise UnsupportedWheel("WHEEL is missing Wheel-Version") | |
| 194 | |
| 195 version = version_text.strip() | |
| 196 | |
| 197 try: | |
| 198 return tuple(map(int, version.split('.'))) | |
| 199 except ValueError: | |
| 200 raise UnsupportedWheel("invalid Wheel-Version: {!r}".format(version)) | |
| 201 | |
| 202 | |
| 203 def check_compatibility(version, name): | |
| 204 # type: (Tuple[int, ...], str) -> None | |
| 205 """Raises errors or warns if called with an incompatible Wheel-Version. | |
| 206 | |
| 207 Pip should refuse to install a Wheel-Version that's a major series | |
| 208 ahead of what it's compatible with (e.g 2.0 > 1.1); and warn when | |
| 209 installing a version only minor version ahead (e.g 1.2 > 1.1). | |
| 210 | |
| 211 version: a 2-tuple representing a Wheel-Version (Major, Minor) | |
| 212 name: name of wheel or package to raise exception about | |
| 213 | |
| 214 :raises UnsupportedWheel: when an incompatible Wheel-Version is given | |
| 215 """ | |
| 216 if version[0] > VERSION_COMPATIBLE[0]: | |
| 217 raise UnsupportedWheel( | |
| 218 "%s's Wheel-Version (%s) is not compatible with this version " | |
| 219 "of pip" % (name, '.'.join(map(str, version))) | |
| 220 ) | |
| 221 elif version > VERSION_COMPATIBLE: | |
| 222 logger.warning( | |
| 223 'Installing from a newer Wheel-Version (%s)', | |
| 224 '.'.join(map(str, version)), | |
| 225 ) |
