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() |
