bynat 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bynat/__init__.py +0 -0
- bynat/caching/Caching.py +216 -0
- bynat/caching/__init__.py +1 -0
- bynat/cleaning/Cleaning.py +2780 -0
- bynat/cleaning/__init__.py +1 -0
- bynat-0.1.0.dist-info/METADATA +14 -0
- bynat-0.1.0.dist-info/RECORD +10 -0
- bynat-0.1.0.dist-info/WHEEL +5 -0
- bynat-0.1.0.dist-info/licenses/LICENSE +21 -0
- bynat-0.1.0.dist-info/top_level.txt +1 -0
bynat/__init__.py
ADDED
|
File without changes
|
bynat/caching/Caching.py
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
from os import PathLike
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
__all__ = ['CacheManager']
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CacheManager:
|
|
12
|
+
"""
|
|
13
|
+
Manage lightweight JSON cache files for function-level and column-level
|
|
14
|
+
operations.
|
|
15
|
+
|
|
16
|
+
Notes:
|
|
17
|
+
Each function gets its own JSON file inside the configured cache
|
|
18
|
+
directory. Individual methods can then store nested objects keyed by
|
|
19
|
+
column name or another logical cache bucket.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, cache_dir: str | PathLike = "Settings/Cache Data/Automated"):
|
|
23
|
+
"""
|
|
24
|
+
Initialize the cache manager and resolve its storage directory.
|
|
25
|
+
|
|
26
|
+
Relative paths are resolved from the current working directory. Absolute
|
|
27
|
+
paths are used as provided. User home markers (`~`) and environment
|
|
28
|
+
variables are expanded before the path is normalized.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
cache_dir (str | os.PathLike): Directory where JSON cache files are
|
|
32
|
+
stored. Defaults to `"Settings/Cache Data/Automated"`.
|
|
33
|
+
|
|
34
|
+
Raises:
|
|
35
|
+
TypeError: If `cache_dir` is not a string or path-like object.
|
|
36
|
+
ValueError: If `cache_dir` is empty after normalization.
|
|
37
|
+
"""
|
|
38
|
+
if not isinstance(cache_dir, (str, PathLike)):
|
|
39
|
+
raise TypeError("cache_dir must be a string or path-like object")
|
|
40
|
+
|
|
41
|
+
cache_dir = os.fspath(cache_dir)
|
|
42
|
+
|
|
43
|
+
if not isinstance(cache_dir, str):
|
|
44
|
+
raise TypeError("cache_dir must resolve to a string path")
|
|
45
|
+
|
|
46
|
+
cache_dir = cache_dir.strip()
|
|
47
|
+
|
|
48
|
+
if not cache_dir:
|
|
49
|
+
raise ValueError("cache_dir cannot be empty")
|
|
50
|
+
|
|
51
|
+
expanded_cache_dir = os.path.expandvars(os.path.expanduser(cache_dir))
|
|
52
|
+
|
|
53
|
+
self.cache_dir_path = os.path.abspath(expanded_cache_dir)
|
|
54
|
+
self.cache_dir = os.path.normpath(expanded_cache_dir)
|
|
55
|
+
|
|
56
|
+
self.caches = {}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _validate_cache_key(self, key: str, key_name: str) -> str:
|
|
60
|
+
"""
|
|
61
|
+
Validate a cache key before using it in file paths or dictionaries.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
key (str): Cache key value to validate.
|
|
65
|
+
key_name (str): Human-readable argument name for error messages.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
str: Normalized cache key.
|
|
69
|
+
|
|
70
|
+
Raises:
|
|
71
|
+
TypeError: If `key` is not a string.
|
|
72
|
+
ValueError: If `key` is empty or contains path separators.
|
|
73
|
+
"""
|
|
74
|
+
if not isinstance(key, str):
|
|
75
|
+
raise TypeError(f"{key_name} must be a string")
|
|
76
|
+
|
|
77
|
+
key = key.strip()
|
|
78
|
+
|
|
79
|
+
if not key:
|
|
80
|
+
raise ValueError(f"{key_name} cannot be empty")
|
|
81
|
+
|
|
82
|
+
if os.path.basename(key) != key or os.path.sep in key or (os.path.altsep and os.path.altsep in key):
|
|
83
|
+
raise ValueError(f"{key_name} cannot contain path separators")
|
|
84
|
+
|
|
85
|
+
return key
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _get_file_path(self, func_name: str) -> str:
|
|
89
|
+
"""
|
|
90
|
+
Build the cache-file path for a specific function name.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
func_name (str): The logical function identifier used as the cache
|
|
94
|
+
filename prefix.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
str: Absolute path to the JSON cache file for the requested
|
|
98
|
+
function.
|
|
99
|
+
|
|
100
|
+
Raises:
|
|
101
|
+
TypeError: If `func_name` is not a string.
|
|
102
|
+
ValueError: If `func_name` is empty or contains path separators.
|
|
103
|
+
"""
|
|
104
|
+
func_name = self._validate_cache_key(func_name, "func_name")
|
|
105
|
+
|
|
106
|
+
return os.path.join(self.cache_dir_path, f"{func_name}_cache.json")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def load_function_cache(self, func_name: str):
|
|
110
|
+
"""
|
|
111
|
+
Load a function cache from disk into memory on first access.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
func_name (str): Function identifier whose cache file should be
|
|
115
|
+
loaded.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
dict: The in-memory cache object for the requested function. If no
|
|
119
|
+
cache file exists yet, an empty dictionary is created and returned.
|
|
120
|
+
|
|
121
|
+
Raises:
|
|
122
|
+
TypeError: If `func_name` is not a string.
|
|
123
|
+
ValueError: If `func_name` is empty or contains path separators.
|
|
124
|
+
"""
|
|
125
|
+
func_name = self._validate_cache_key(func_name, "func_name")
|
|
126
|
+
|
|
127
|
+
if func_name not in self.caches:
|
|
128
|
+
file_path = self._get_file_path(func_name)
|
|
129
|
+
|
|
130
|
+
if os.path.exists(file_path):
|
|
131
|
+
try:
|
|
132
|
+
with open(file_path, 'r', encoding = 'utf-8') as cache:
|
|
133
|
+
loaded_cache = json.load(cache)
|
|
134
|
+
|
|
135
|
+
self.caches[func_name] = loaded_cache if isinstance(loaded_cache, dict) else {}
|
|
136
|
+
|
|
137
|
+
except (json.JSONDecodeError, OSError) as e:
|
|
138
|
+
print(f"Error: Cache file '{file_path}' is corrupted or unreadable. Starting with an empty cache.\n Details: {e}")
|
|
139
|
+
self.caches[func_name] = {}
|
|
140
|
+
|
|
141
|
+
else:
|
|
142
|
+
self.caches[func_name] = {}
|
|
143
|
+
|
|
144
|
+
return self.caches[func_name]
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def get_column_cache(self, func_name: str, col_name: str):
|
|
148
|
+
"""
|
|
149
|
+
Return a nested cache bucket for a specific function and column.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
func_name (str): Parent function cache name.
|
|
153
|
+
col_name (str): Nested cache key, commonly a column name.
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
dict: The cache dictionary stored under the requested nested key.
|
|
157
|
+
The dictionary is created automatically if it does not yet exist.
|
|
158
|
+
"""
|
|
159
|
+
col_name = self._validate_cache_key(col_name, "col_name")
|
|
160
|
+
func_cache = self.load_function_cache(func_name)
|
|
161
|
+
|
|
162
|
+
if col_name not in func_cache or not isinstance(func_cache[col_name], dict):
|
|
163
|
+
func_cache[col_name] = {}
|
|
164
|
+
|
|
165
|
+
return func_cache[col_name]
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def save_function_cache(self, func_name: str):
|
|
169
|
+
"""
|
|
170
|
+
Persist the in-memory cache for a function to disk as JSON.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
func_name (str): Function identifier whose cache should be written.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
None
|
|
177
|
+
"""
|
|
178
|
+
func_name = self._validate_cache_key(func_name, "func_name")
|
|
179
|
+
|
|
180
|
+
if func_name not in self.caches:
|
|
181
|
+
self.caches[func_name] = {}
|
|
182
|
+
|
|
183
|
+
os.makedirs(self.cache_dir_path, exist_ok = True)
|
|
184
|
+
|
|
185
|
+
file_path = self._get_file_path(func_name)
|
|
186
|
+
|
|
187
|
+
try:
|
|
188
|
+
with open(file_path, 'w', encoding = 'utf-8') as f:
|
|
189
|
+
json.dump(self.caches[func_name], f, ensure_ascii = False, indent = 4)
|
|
190
|
+
|
|
191
|
+
except (TypeError, OSError) as e:
|
|
192
|
+
print(f"Error: Could not save cache file '{file_path}'.\n Details: {e}")
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def delete_column_cache(self, func_name: str, col_name: str):
|
|
196
|
+
"""
|
|
197
|
+
Remove a nested cache bucket from a function cache and save the result.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
func_name (str): Parent function cache name.
|
|
201
|
+
col_name (str): Nested cache key to remove.
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
None
|
|
205
|
+
"""
|
|
206
|
+
col_name = self._validate_cache_key(col_name, "col_name")
|
|
207
|
+
func_cache = self.load_function_cache(func_name)
|
|
208
|
+
|
|
209
|
+
if col_name in func_cache:
|
|
210
|
+
del func_cache[col_name]
|
|
211
|
+
self.save_function_cache(func_name)
|
|
212
|
+
|
|
213
|
+
print(f"Cache for column '{col_name}' deleted from '{func_name}_cache.json'")
|
|
214
|
+
|
|
215
|
+
else:
|
|
216
|
+
print(f"No cache found for column '{col_name}' in '{func_name}_cache.json'")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .Caching import *
|