|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
3 | 3 | import contextlib |
| 4 | +import hashlib |
4 | 5 | import io |
5 | 6 | import os |
6 | 7 | import platform |
@@ -95,6 +96,25 @@ def _cache_hit(cachefile: str, response: requests.Response) -> bool: |
95 | 96 | return local_mtime >= remote_mtime |
96 | 97 |
|
97 | 98 |
|
| 99 | +def url_to_cache_filename(ref_url: str) -> str: |
| 100 | + """ |
| 101 | + Given a schema URL, convert it to a filename for caching in a cache dir. |
| 102 | +
|
| 103 | + Rules are as follows: |
| 104 | + - the base filename is an sha256 hash of the URL |
| 105 | + - if the filename ends in an extension (.json, .yaml, etc) that extension |
| 106 | + is appended to the hash |
| 107 | +
|
| 108 | + Preserving file extensions preserves the extension-based logic used for parsing, and |
| 109 | + it also helps a local editor (browsing the cache) identify filetypes. |
| 110 | + """ |
| 111 | + filename = hashlib.sha256(ref_url.encode()).hexdigest() |
| 112 | + if "." in (last_part := ref_url.rpartition("/")[-1]): |
| 113 | + _, _, extension = last_part.rpartition(".") |
| 114 | + filename = f"{filename}.{extension}" |
| 115 | + return filename |
| 116 | + |
| 117 | + |
98 | 118 | class FailedDownloadError(Exception): |
99 | 119 | pass |
100 | 120 |
|
@@ -155,21 +175,21 @@ def bind( |
155 | 175 | validation_callback: t.Callable[[bytes], t.Any] | None = None, |
156 | 176 | ) -> BoundCacheDownloader: |
157 | 177 | return BoundCacheDownloader( |
158 | | - file_url, filename, self, validation_callback=validation_callback |
| 178 | + file_url, self, filename=filename, validation_callback=validation_callback |
159 | 179 | ) |
160 | 180 |
|
161 | 181 |
|
162 | 182 | class BoundCacheDownloader: |
163 | 183 | def __init__( |
164 | 184 | self, |
165 | 185 | file_url: str, |
166 | | - filename: str | None, |
167 | 186 | downloader: CacheDownloader, |
168 | 187 | *, |
| 188 | + filename: str | None = None, |
169 | 189 | validation_callback: t.Callable[[bytes], t.Any] | None = None, |
170 | 190 | ) -> None: |
171 | 191 | self._file_url = file_url |
172 | | - self._filename = filename or file_url.split("/")[-1] |
| 192 | + self._filename = filename or url_to_cache_filename(file_url) |
173 | 193 | self._downloader = downloader |
174 | 194 | self._validation_callback = validation_callback |
175 | 195 |
|
|
0 commit comments