33from hashlib import sha256
44from io import BytesIO
55from math import log
6- from os import PathLike , getenv
6+ from os import PathLike
77from pathlib import Path
88from shutil import rmtree
99from sys import modules
1515import requests
1616from platformdirs import user_cache_path
1717
18-
19- def _cache_path () -> Path :
20- """
21- Get epymorph's cache directory.
22-
23- Returns
24- -------
25- :
26- The path.
27- """
28- if (path_var := getenv ("EPYMORPH_CACHE_PATH" )) is not None :
29- # Load path from env var
30- path = Path (path_var )
31- else :
32- # fall back to platform-specific default path
33- path = user_cache_path (appname = "epymorph" )
34- # ensure cache directory exists
35- path .mkdir (parents = True , exist_ok = True )
36- return path
37-
38-
39- CACHE_PATH = _cache_path ()
18+ from epymorph .settings import declare_setting , env_flag , env_path , env_path_list
19+
20+ EPYMORPH_CACHE_PATH = declare_setting (
21+ name = "EPYMORPH_CACHE_PATH" ,
22+ description = (
23+ "Optional path to use as the location to store cached files. "
24+ "By default, epymorph uses a path which is appropriate to your OS."
25+ ),
26+ getter = lambda : env_path (
27+ name = "EPYMORPH_CACHE_PATH" ,
28+ default_value = user_cache_path (appname = "epymorph" ),
29+ ensure_exists = True ,
30+ ),
31+ )
32+ """An environment variable for epymorph's cache path."""
33+
34+ EPYMORPH_CACHE_DISABLED = declare_setting (
35+ name = "EPYMORPH_CACHE_DISABLED" ,
36+ description = (
37+ "An optional boolean value; true to disable all cache interactions. "
38+ "Default is false."
39+ ),
40+ getter = lambda : env_flag ("EPYMORPH_CACHE_DISABLED" , False ),
41+ )
42+ """An environment variable to entirely disable caching."""
43+
44+ EPYMORPH_CACHE_DISABLED_PATHS = declare_setting (
45+ name = "EPYMORPH_CACHE_DISABLED_PATHS" ,
46+ description = (
47+ "An optional list of paths (separated by semicolons); "
48+ "when attempting to load or save a file using the cache, "
49+ "epymorph will check if the cache path starts with one of "
50+ "these paths, and if so, interactions with the cache will be "
51+ "skipped entirely."
52+ ),
53+ getter = lambda : env_path_list ("EPYMORPH_CACHE_DISABLED_PATHS" ),
54+ )
55+ """An environment variable for paths which should have caching disabled."""
56+
57+
58+ CACHE_PATH = EPYMORPH_CACHE_PATH .get ()
4059"""The root directory for epymorph's cached files."""
4160
4261
@@ -398,7 +417,11 @@ def load_file_from_cache(from_path: str | PathLike[str]) -> BytesIO:
398417 """
399418 try :
400419 return load_file (_resolve_cache_path (from_path ))
420+ except FileMissingError :
421+ # missing file is a normal cache miss; no extra context needed
422+ raise CacheMissError () from None
401423 except FileError as e :
424+ # any other file error is abnormal and extra context will help debug
402425 raise CacheMissError () from e
403426
404427
@@ -425,23 +448,34 @@ def load_or_fetch(cache_path: Path, fetch: Callable[[], BytesIO]) -> BytesIO:
425448 :
426449 The file bytes.
427450 """
428- try :
451+ cache_disabled = EPYMORPH_CACHE_DISABLED .get () or any (
452+ cache_path .is_relative_to (p ) # is the file's cache path in a disabled path?
453+ for p in EPYMORPH_CACHE_DISABLED_PATHS .get ()
454+ )
455+
456+ if not cache_disabled :
429457 # Try to load from cache.
430- return load_file_from_cache (cache_path )
431- except CacheMissError :
432- # On cache miss, fetch file contents.
433- file = fetch ()
458+ try :
459+ return load_file_from_cache (cache_path )
460+ except CacheMissError :
461+ # passing through the exception context means the cache miss
462+ # doesn't clutter up the exception stack if fetching the file
463+ # from source fails.
464+ pass
465+
466+ # On cache miss, fetch file contents.
467+ file = fetch ()
468+
469+ if not cache_disabled :
434470 # And attempt to save the file to the cache for next time.
435471 try :
436472 save_file_to_cache (cache_path , file )
437473 except FileWriteError as e :
438- # Failure to save to the cache is not worth stopping the program:
439- # raise a warning.
440- warn (
441- f"Unable to save file to the cache ({ cache_path } ). Cause:\n { e } " ,
442- CacheWarning ,
443- )
444- return file
474+ # Failure to save to the cache is not worth stopping the program.
475+ wrn = f"Unable to save file to the cache ({ cache_path } ). Cause:\n { e } "
476+ warn (wrn , CacheWarning )
477+
478+ return file
445479
446480
447481def load_or_fetch_url (url : str , cache_path : Path ) -> BytesIO :
0 commit comments