-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathcache.py
More file actions
100 lines (92 loc) · 3.94 KB
/
Copy pathcache.py
File metadata and controls
100 lines (92 loc) · 3.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# pylint: disable=C0114
import os
import hashlib
import json
from csvpath.util.nos import Nos
from csvpath.util.file_writers import DataFileWriter
from csvpath.util.file_readers import DataFileReader
class Cache:
#
# csvpathx can be either CsvPath or CsvPaths
#
def __init__(self, csvpathx):
self.csvpathx = csvpathx
def clear_cache(self) -> None:
cachedir = self.get_cachedir()
Nos(cachedir).remove()
def get_cache_name(self, filename: str) -> str:
if filename is None:
raise ValueError("Filename cannot be None")
try:
#
# we cannot cache non-local files until we implement file_info() in the
# four non-local backends so we can get the file mod time. quite doable,
# but not doing it atm.
#
# filenames may have a root minor token separated from the real path by
# a #. this will usually be an excel tab. os.path.getmtime() needs the
# real path without that token, but the token itself must stay part of
# what we hash below -- otherwise every tab of the same file collapses
# onto the same cache name, and one tab's cached headers/line count get
# served for another tab entirely.
#
i = filename.find("#")
real_path = filename[0:i] if i > -1 else filename
t = os.path.getmtime(real_path)
filename = f"{filename}{t}"
return hashlib.sha256(filename.encode("utf-8")).hexdigest()
except (FileNotFoundError, IsADirectoryError):
self.csvpathx.logger.debug("{filename} is not available or not a file")
"""
if Nos(filename).is_local:
try:
filename = f"{filename}{os.path.getmtime(filename)}"
return hashlib.sha256(filename.encode("utf-8")).hexdigest()
except (FileNotFoundError, IsADirectoryError):
self.csvpathx.logger.debug("{filename} is not available or not a file")
return None
"""
def get_cachedir(self) -> str:
self.csvpathx.config._assure_cache_path()
if not Nos(self.csvpathx.config.cache_dir_path).is_local:
raise ValueError("Cache path must be local")
return self.csvpathx.config.cache_dir_path
def get_keypath(self, filename: str) -> str:
if filename is None:
raise ValueError("Filename cannot be None")
#
# filename is passed through as-is, root minor token (e.g. an excel tab)
# included. get_cache_name() is responsible for stripping it off only
# where it needs the real on-disk path (the mtime lookup), while still
# hashing the full filename so different tabs get different cache names.
#
fn = self.get_cache_name(filename)
if fn is None:
self.csvpathx.logger.debug(
"Unknown cache name for file. Is the file local?"
)
return None
cachedir = self.get_cachedir()
if cachedir is None:
self.csvpathx.logger.debug("No cache path available")
return None
keypath = None
cachepath = os.path.join(cachedir, fn)
keypath = f"{cachepath}.json"
return keypath
def cached_text(self, filename: str) -> list | dict:
if not Nos(filename).is_local:
return None
keypath = self.get_keypath(filename)
if keypath and Nos(keypath).exists():
with DataFileReader(path=keypath, mode="rb") as file:
return json.load(file.source)
return None
def cache_text(self, filename: str, data: list | dict) -> None:
if not Nos(filename).is_local:
return None
keypath = self.get_keypath(filename)
if keypath is None:
raise ValueError(f"Keypath for {filename} cannot be None")
with DataFileWriter(path=keypath) as file:
json.dump(data, file.sink)