|
15 | 15 |
|
16 | 16 | from __future__ import annotations |
17 | 17 | import os |
| 18 | +import sys |
18 | 19 | import base64 |
19 | 20 | import pickle |
20 | 21 | from typing import TypeVar, Any, Mapping, Iterator |
|
33 | 34 |
|
34 | 35 |
|
35 | 36 | __all__ = ( |
| 37 | + "FILE_EXTENSION", |
36 | 38 | "FileCacheSource", |
37 | 39 | "FileCache" |
38 | 40 | ) |
39 | 41 |
|
40 | 42 | S = TypeVar("S", bound=TimestampedCodeSource) |
41 | 43 |
|
| 44 | +# use cache tag in extension to prevent errors with different python versions |
| 45 | +FILE_EXTENSION = f".{sys.implementation.cache_tag}.pickle" |
| 46 | + |
42 | 47 |
|
43 | 48 | class FileCacheSource(CacheSource[S]): |
44 | 49 | """ |
@@ -139,9 +144,10 @@ def __eq__(self, other: object) -> bool: |
139 | 144 |
|
140 | 145 | def __getitem__(self, name: str) -> FileCacheSource[S]: |
141 | 146 | """get FileCacheSource with path as returned by .path()""" |
| 147 | + path = self.path(name) # dont leak sources if self.path fails |
142 | 148 | return FileCacheSource( |
143 | 149 | self.source_container[name], |
144 | | - self.path(name), |
| 150 | + path, |
145 | 151 | self.ttl |
146 | 152 | ) |
147 | 153 |
|
@@ -194,20 +200,20 @@ def clear(self) -> None: |
194 | 200 | pass |
195 | 201 |
|
196 | 202 | def path(self, name: str) -> str: |
197 | | - """return directory_name/<base32 encoded name>.pickle""" |
| 203 | + """return directory_name/<base32 encoded name>FILE_EXTENSION""" |
198 | 204 | return os.path.join( |
199 | 205 | self.directory_name, |
200 | | - base64.b32encode(name.encode("utf8")).decode("utf8") + ".pickle" |
| 206 | + base64.b32encode(name.encode("utf8")).decode("utf8") + FILE_EXTENSION |
201 | 207 | ) # use base32 because of case-insensitive file systems and forbidden characters |
202 | 208 |
|
203 | 209 | def reconstruct_name(self, path: str) -> str: |
204 | 210 | """reconstruct the name from a file cache path""" |
205 | | - name, _, _ = os.path.basename(path).rpartition(".") |
| 211 | + name, _, _ = os.path.basename(path).partition(".") |
206 | 212 | return base64.b32decode(name.encode("utf8"), casefold=True).decode("utf8") |
207 | 213 |
|
208 | 214 | def paths(self) -> Iterator[str]: |
209 | 215 | """return a iterator yielding all paths currently in use (including outdated ones)""" |
210 | 216 | with os.scandir(self.directory_name) as directory: |
211 | 217 | for entry in directory: |
212 | | - if entry.name.endswith(".pickle") and entry.is_file(): |
| 218 | + if entry.name.endswith(FILE_EXTENSION) and entry.is_file(): |
213 | 219 | yield entry.path |
0 commit comments