Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/pip/_internal/cache.py @ 1:56ad4e20f292 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author | guerler |
---|---|
date | Fri, 31 Jul 2020 00:32:28 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
0:d30785e31577 | 1:56ad4e20f292 |
---|---|
1 """Cache Management | |
2 """ | |
3 | |
4 import errno | |
5 import hashlib | |
6 import logging | |
7 import os | |
8 | |
9 from pip._vendor.packaging.utils import canonicalize_name | |
10 | |
11 from pip._internal.models.link import Link | |
12 from pip._internal.utils.compat import expanduser | |
13 from pip._internal.utils.misc import path_to_url | |
14 from pip._internal.utils.temp_dir import TempDirectory | |
15 from pip._internal.utils.typing import MYPY_CHECK_RUNNING | |
16 from pip._internal.wheel import InvalidWheelFilename, Wheel | |
17 | |
18 if MYPY_CHECK_RUNNING: | |
19 from typing import Optional, Set, List, Any | |
20 from pip._internal.index import FormatControl | |
21 | |
22 logger = logging.getLogger(__name__) | |
23 | |
24 | |
25 class Cache(object): | |
26 """An abstract class - provides cache directories for data from links | |
27 | |
28 | |
29 :param cache_dir: The root of the cache. | |
30 :param format_control: An object of FormatControl class to limit | |
31 binaries being read from the cache. | |
32 :param allowed_formats: which formats of files the cache should store. | |
33 ('binary' and 'source' are the only allowed values) | |
34 """ | |
35 | |
36 def __init__(self, cache_dir, format_control, allowed_formats): | |
37 # type: (str, FormatControl, Set[str]) -> None | |
38 super(Cache, self).__init__() | |
39 self.cache_dir = expanduser(cache_dir) if cache_dir else None | |
40 self.format_control = format_control | |
41 self.allowed_formats = allowed_formats | |
42 | |
43 _valid_formats = {"source", "binary"} | |
44 assert self.allowed_formats.union(_valid_formats) == _valid_formats | |
45 | |
46 def _get_cache_path_parts(self, link): | |
47 # type: (Link) -> List[str] | |
48 """Get parts of part that must be os.path.joined with cache_dir | |
49 """ | |
50 | |
51 # We want to generate an url to use as our cache key, we don't want to | |
52 # just re-use the URL because it might have other items in the fragment | |
53 # and we don't care about those. | |
54 key_parts = [link.url_without_fragment] | |
55 if link.hash_name is not None and link.hash is not None: | |
56 key_parts.append("=".join([link.hash_name, link.hash])) | |
57 key_url = "#".join(key_parts) | |
58 | |
59 # Encode our key url with sha224, we'll use this because it has similar | |
60 # security properties to sha256, but with a shorter total output (and | |
61 # thus less secure). However the differences don't make a lot of | |
62 # difference for our use case here. | |
63 hashed = hashlib.sha224(key_url.encode()).hexdigest() | |
64 | |
65 # We want to nest the directories some to prevent having a ton of top | |
66 # level directories where we might run out of sub directories on some | |
67 # FS. | |
68 parts = [hashed[:2], hashed[2:4], hashed[4:6], hashed[6:]] | |
69 | |
70 return parts | |
71 | |
72 def _get_candidates(self, link, package_name): | |
73 # type: (Link, Optional[str]) -> List[Any] | |
74 can_not_cache = ( | |
75 not self.cache_dir or | |
76 not package_name or | |
77 not link | |
78 ) | |
79 if can_not_cache: | |
80 return [] | |
81 | |
82 canonical_name = canonicalize_name(package_name) | |
83 formats = self.format_control.get_allowed_formats( | |
84 canonical_name | |
85 ) | |
86 if not self.allowed_formats.intersection(formats): | |
87 return [] | |
88 | |
89 root = self.get_path_for_link(link) | |
90 try: | |
91 return os.listdir(root) | |
92 except OSError as err: | |
93 if err.errno in {errno.ENOENT, errno.ENOTDIR}: | |
94 return [] | |
95 raise | |
96 | |
97 def get_path_for_link(self, link): | |
98 # type: (Link) -> str | |
99 """Return a directory to store cached items in for link. | |
100 """ | |
101 raise NotImplementedError() | |
102 | |
103 def get(self, link, package_name): | |
104 # type: (Link, Optional[str]) -> Link | |
105 """Returns a link to a cached item if it exists, otherwise returns the | |
106 passed link. | |
107 """ | |
108 raise NotImplementedError() | |
109 | |
110 def _link_for_candidate(self, link, candidate): | |
111 # type: (Link, str) -> Link | |
112 root = self.get_path_for_link(link) | |
113 path = os.path.join(root, candidate) | |
114 | |
115 return Link(path_to_url(path)) | |
116 | |
117 def cleanup(self): | |
118 # type: () -> None | |
119 pass | |
120 | |
121 | |
122 class SimpleWheelCache(Cache): | |
123 """A cache of wheels for future installs. | |
124 """ | |
125 | |
126 def __init__(self, cache_dir, format_control): | |
127 # type: (str, FormatControl) -> None | |
128 super(SimpleWheelCache, self).__init__( | |
129 cache_dir, format_control, {"binary"} | |
130 ) | |
131 | |
132 def get_path_for_link(self, link): | |
133 # type: (Link) -> str | |
134 """Return a directory to store cached wheels for link | |
135 | |
136 Because there are M wheels for any one sdist, we provide a directory | |
137 to cache them in, and then consult that directory when looking up | |
138 cache hits. | |
139 | |
140 We only insert things into the cache if they have plausible version | |
141 numbers, so that we don't contaminate the cache with things that were | |
142 not unique. E.g. ./package might have dozens of installs done for it | |
143 and build a version of 0.0...and if we built and cached a wheel, we'd | |
144 end up using the same wheel even if the source has been edited. | |
145 | |
146 :param link: The link of the sdist for which this will cache wheels. | |
147 """ | |
148 parts = self._get_cache_path_parts(link) | |
149 | |
150 # Store wheels within the root cache_dir | |
151 return os.path.join(self.cache_dir, "wheels", *parts) | |
152 | |
153 def get(self, link, package_name): | |
154 # type: (Link, Optional[str]) -> Link | |
155 candidates = [] | |
156 | |
157 for wheel_name in self._get_candidates(link, package_name): | |
158 try: | |
159 wheel = Wheel(wheel_name) | |
160 except InvalidWheelFilename: | |
161 continue | |
162 if not wheel.supported(): | |
163 # Built for a different python/arch/etc | |
164 continue | |
165 candidates.append((wheel.support_index_min(), wheel_name)) | |
166 | |
167 if not candidates: | |
168 return link | |
169 | |
170 return self._link_for_candidate(link, min(candidates)[1]) | |
171 | |
172 | |
173 class EphemWheelCache(SimpleWheelCache): | |
174 """A SimpleWheelCache that creates it's own temporary cache directory | |
175 """ | |
176 | |
177 def __init__(self, format_control): | |
178 # type: (FormatControl) -> None | |
179 self._temp_dir = TempDirectory(kind="ephem-wheel-cache") | |
180 self._temp_dir.create() | |
181 | |
182 super(EphemWheelCache, self).__init__( | |
183 self._temp_dir.path, format_control | |
184 ) | |
185 | |
186 def cleanup(self): | |
187 # type: () -> None | |
188 self._temp_dir.cleanup() | |
189 | |
190 | |
191 class WheelCache(Cache): | |
192 """Wraps EphemWheelCache and SimpleWheelCache into a single Cache | |
193 | |
194 This Cache allows for gracefully degradation, using the ephem wheel cache | |
195 when a certain link is not found in the simple wheel cache first. | |
196 """ | |
197 | |
198 def __init__(self, cache_dir, format_control): | |
199 # type: (str, FormatControl) -> None | |
200 super(WheelCache, self).__init__( | |
201 cache_dir, format_control, {'binary'} | |
202 ) | |
203 self._wheel_cache = SimpleWheelCache(cache_dir, format_control) | |
204 self._ephem_cache = EphemWheelCache(format_control) | |
205 | |
206 def get_path_for_link(self, link): | |
207 # type: (Link) -> str | |
208 return self._wheel_cache.get_path_for_link(link) | |
209 | |
210 def get_ephem_path_for_link(self, link): | |
211 # type: (Link) -> str | |
212 return self._ephem_cache.get_path_for_link(link) | |
213 | |
214 def get(self, link, package_name): | |
215 # type: (Link, Optional[str]) -> Link | |
216 retval = self._wheel_cache.get(link, package_name) | |
217 if retval is link: | |
218 retval = self._ephem_cache.get(link, package_name) | |
219 return retval | |
220 | |
221 def cleanup(self): | |
222 # type: () -> None | |
223 self._wheel_cache.cleanup() | |
224 self._ephem_cache.cleanup() |