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 (2020-07-27) |
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 ) |