persidict 0.37.0__tar.gz → 0.37.1__tar.gz
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.
Potentially problematic release.
This version of persidict might be problematic. Click here for more details.
- {persidict-0.37.0 → persidict-0.37.1}/PKG-INFO +1 -1
- {persidict-0.37.0 → persidict-0.37.1}/pyproject.toml +1 -1
- {persidict-0.37.0 → persidict-0.37.1}/src/persidict/file_dir_dict.py +6 -8
- {persidict-0.37.0 → persidict-0.37.1}/src/persidict/persi_dict.py +27 -3
- {persidict-0.37.0 → persidict-0.37.1}/src/persidict/s3_dict.py +7 -7
- {persidict-0.37.0 → persidict-0.37.1}/src/persidict/safe_str_tuple.py +0 -2
- {persidict-0.37.0 → persidict-0.37.1}/README.md +0 -0
- {persidict-0.37.0 → persidict-0.37.1}/src/persidict/.DS_Store +0 -0
- {persidict-0.37.0 → persidict-0.37.1}/src/persidict/__init__.py +0 -0
- {persidict-0.37.0 → persidict-0.37.1}/src/persidict/jokers.py +0 -0
- {persidict-0.37.0 → persidict-0.37.1}/src/persidict/overlapping_multi_dict.py +0 -0
- {persidict-0.37.0 → persidict-0.37.1}/src/persidict/safe_chars.py +0 -0
- {persidict-0.37.0 → persidict-0.37.1}/src/persidict/safe_str_tuple_signing.py +0 -0
- {persidict-0.37.0 → persidict-0.37.1}/src/persidict/write_once_dict.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: persidict
|
|
3
|
-
Version: 0.37.
|
|
3
|
+
Version: 0.37.1
|
|
4
4
|
Summary: Simple persistent key-value store for Python. Values are stored as files on a disk or as S3 objects on AWS cloud.
|
|
5
5
|
Keywords: persistence,dicts,distributed,parallel
|
|
6
6
|
Author: Vlad (Volodymyr) Pavlov
|
|
@@ -4,7 +4,7 @@ build-backend = "uv_build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "persidict"
|
|
7
|
-
version = "0.37.
|
|
7
|
+
version = "0.37.1"
|
|
8
8
|
description = "Simple persistent key-value store for Python. Values are stored as files on a disk or as S3 objects on AWS cloud."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -27,8 +27,7 @@ from .jokers import KEEP_CURRENT, DELETE_CURRENT, Joker
|
|
|
27
27
|
from .safe_chars import replace_unsafe_chars
|
|
28
28
|
from .safe_str_tuple import SafeStrTuple
|
|
29
29
|
from .safe_str_tuple_signing import sign_safe_str_tuple, unsign_safe_str_tuple
|
|
30
|
-
from .persi_dict import PersiDict, PersiDictKey
|
|
31
|
-
|
|
30
|
+
from .persi_dict import PersiDict, PersiDictKey, non_empty_persidict_key
|
|
32
31
|
|
|
33
32
|
jsonpickle_numpy.register_handlers()
|
|
34
33
|
jsonpickle_pandas.register_handlers()
|
|
@@ -457,7 +456,7 @@ class FileDirDict(PersiDict):
|
|
|
457
456
|
Returns:
|
|
458
457
|
bool: True if a file for the key exists; False otherwise.
|
|
459
458
|
"""
|
|
460
|
-
key =
|
|
459
|
+
key = non_empty_persidict_key(key)
|
|
461
460
|
filename = self._build_full_path(key)
|
|
462
461
|
return os.path.isfile(filename)
|
|
463
462
|
|
|
@@ -479,7 +478,7 @@ class FileDirDict(PersiDict):
|
|
|
479
478
|
TypeError: If the deserialized value does not match base_class_for_values
|
|
480
479
|
when it is set.
|
|
481
480
|
"""
|
|
482
|
-
key =
|
|
481
|
+
key = non_empty_persidict_key(key)
|
|
483
482
|
filename = self._build_full_path(key)
|
|
484
483
|
if not os.path.isfile(filename):
|
|
485
484
|
raise KeyError(f"File {filename} does not exist")
|
|
@@ -510,7 +509,7 @@ class FileDirDict(PersiDict):
|
|
|
510
509
|
base_class_for_values when it is set.
|
|
511
510
|
"""
|
|
512
511
|
|
|
513
|
-
key =
|
|
512
|
+
key = non_empty_persidict_key(key)
|
|
514
513
|
PersiDict.__setitem__(self, key, value)
|
|
515
514
|
if isinstance(value, Joker):
|
|
516
515
|
# processed by base class
|
|
@@ -529,8 +528,7 @@ class FileDirDict(PersiDict):
|
|
|
529
528
|
Raises:
|
|
530
529
|
KeyError: If immutable_items is True or if the key does not exist.
|
|
531
530
|
"""
|
|
532
|
-
key =
|
|
533
|
-
PersiDict.__delitem__(self, key)
|
|
531
|
+
key = non_empty_persidict_key(key)
|
|
534
532
|
filename = self._build_full_path(key)
|
|
535
533
|
if not os.path.isfile(filename):
|
|
536
534
|
raise KeyError(f"File {filename} does not exist")
|
|
@@ -627,7 +625,7 @@ class FileDirDict(PersiDict):
|
|
|
627
625
|
Raises:
|
|
628
626
|
FileNotFoundError: If the key does not exist.
|
|
629
627
|
"""
|
|
630
|
-
key =
|
|
628
|
+
key = non_empty_persidict_key(key)
|
|
631
629
|
filename = self._build_full_path(key)
|
|
632
630
|
return os.path.getmtime(filename)
|
|
633
631
|
|
|
@@ -36,6 +36,24 @@ If a string (or a sequence of strings) is passed to a PersiDict as a key,
|
|
|
36
36
|
it will be automatically converted into SafeStrTuple.
|
|
37
37
|
"""
|
|
38
38
|
|
|
39
|
+
|
|
40
|
+
def non_empty_persidict_key(*args) -> SafeStrTuple:
|
|
41
|
+
"""Create a non-empty SafeStrTuple from the given arguments.
|
|
42
|
+
This is a convenience function that ensures the resulting SafeStrTuple is
|
|
43
|
+
not empty, raising a KeyError if it is.
|
|
44
|
+
Args:
|
|
45
|
+
*args: Arguments to pass to SafeStrTuple constructor.
|
|
46
|
+
Returns:
|
|
47
|
+
SafeStrTuple: A non-empty SafeStrTuple instance.
|
|
48
|
+
Raises:
|
|
49
|
+
KeyError: If the resulting SafeStrTuple is empty.
|
|
50
|
+
"""
|
|
51
|
+
result = SafeStrTuple(*args)
|
|
52
|
+
if len(result) == 0:
|
|
53
|
+
raise KeyError("Key cannot be empty")
|
|
54
|
+
return result
|
|
55
|
+
|
|
56
|
+
|
|
39
57
|
class PersiDict(MutableMapping, ParameterizableClass):
|
|
40
58
|
"""Abstract dict-like interface for durable key-value stores.
|
|
41
59
|
|
|
@@ -209,8 +227,12 @@ class PersiDict(MutableMapping, ParameterizableClass):
|
|
|
209
227
|
elif self.immutable_items:
|
|
210
228
|
if key in self:
|
|
211
229
|
raise KeyError("Can't modify an immutable key-value pair")
|
|
212
|
-
|
|
230
|
+
|
|
231
|
+
key = non_empty_persidict_key(key)
|
|
232
|
+
|
|
233
|
+
if value is DELETE_CURRENT:
|
|
213
234
|
self.delete_if_exists(key)
|
|
235
|
+
return
|
|
214
236
|
|
|
215
237
|
if self.base_class_for_values is not None:
|
|
216
238
|
if not isinstance(value, self.base_class_for_values):
|
|
@@ -237,7 +259,9 @@ class PersiDict(MutableMapping, ParameterizableClass):
|
|
|
237
259
|
if type(self) is PersiDict:
|
|
238
260
|
raise NotImplementedError("PersiDict is an abstract base class"
|
|
239
261
|
" and cannot delete items directly")
|
|
240
|
-
|
|
262
|
+
|
|
263
|
+
key = non_empty_persidict_key(key)
|
|
264
|
+
|
|
241
265
|
if key not in self:
|
|
242
266
|
raise KeyError(f"Key {key} not found")
|
|
243
267
|
|
|
@@ -457,7 +481,7 @@ class PersiDict(MutableMapping, ParameterizableClass):
|
|
|
457
481
|
if self.immutable_items:
|
|
458
482
|
raise KeyError("Can't delete an immutable key-value pair")
|
|
459
483
|
|
|
460
|
-
key =
|
|
484
|
+
key = non_empty_persidict_key(key)
|
|
461
485
|
|
|
462
486
|
if key in self:
|
|
463
487
|
try:
|
|
@@ -16,7 +16,7 @@ from .safe_str_tuple import SafeStrTuple
|
|
|
16
16
|
from .safe_str_tuple_signing import sign_safe_str_tuple, unsign_safe_str_tuple
|
|
17
17
|
from .persi_dict import PersiDict
|
|
18
18
|
from .jokers import KEEP_CURRENT, DELETE_CURRENT, Joker
|
|
19
|
-
from .file_dir_dict import FileDirDict, PersiDictKey
|
|
19
|
+
from .file_dir_dict import FileDirDict, PersiDictKey, non_empty_persidict_key
|
|
20
20
|
from .overlapping_multi_dict import OverlappingMultiDict
|
|
21
21
|
|
|
22
22
|
S3DICT_DEFAULT_BASE_DIR = "__s3_dict__"
|
|
@@ -199,7 +199,7 @@ class S3Dict(PersiDict):
|
|
|
199
199
|
str: The complete S3 object key including root_prefix and file_type
|
|
200
200
|
extension, with digest-based collision prevention applied if enabled.
|
|
201
201
|
"""
|
|
202
|
-
key =
|
|
202
|
+
key = non_empty_persidict_key(key)
|
|
203
203
|
key = sign_safe_str_tuple(key, self.digest_len)
|
|
204
204
|
objectname = self.root_prefix + "/".join(key)+ "." + self.file_type
|
|
205
205
|
return objectname
|
|
@@ -218,7 +218,7 @@ class S3Dict(PersiDict):
|
|
|
218
218
|
bool: True if the key exists in S3 (or local cache for immutable
|
|
219
219
|
items), False otherwise.
|
|
220
220
|
"""
|
|
221
|
-
key =
|
|
221
|
+
key = non_empty_persidict_key(key)
|
|
222
222
|
if self.immutable_items and key in self.main_cache:
|
|
223
223
|
return True
|
|
224
224
|
try:
|
|
@@ -251,7 +251,7 @@ class S3Dict(PersiDict):
|
|
|
251
251
|
KeyError: If the key does not exist in S3.
|
|
252
252
|
"""
|
|
253
253
|
|
|
254
|
-
key =
|
|
254
|
+
key = non_empty_persidict_key(key)
|
|
255
255
|
|
|
256
256
|
if self.immutable_items and key in self.main_cache:
|
|
257
257
|
return self.main_cache[key]
|
|
@@ -317,7 +317,7 @@ class S3Dict(PersiDict):
|
|
|
317
317
|
the required base_class_for_values when specified.
|
|
318
318
|
"""
|
|
319
319
|
|
|
320
|
-
key =
|
|
320
|
+
key = non_empty_persidict_key(key)
|
|
321
321
|
PersiDict.__setitem__(self, key, value)
|
|
322
322
|
if isinstance(value, Joker):
|
|
323
323
|
# Joker values (KEEP_CURRENT, DELETE_CURRENT) are handled by base class
|
|
@@ -351,7 +351,7 @@ class S3Dict(PersiDict):
|
|
|
351
351
|
Raises:
|
|
352
352
|
KeyError: If immutable_items is True, or if the key does not exist.
|
|
353
353
|
"""
|
|
354
|
-
key =
|
|
354
|
+
key = non_empty_persidict_key(key)
|
|
355
355
|
PersiDict.__delitem__(self, key)
|
|
356
356
|
obj_name = self._build_full_objectname(key)
|
|
357
357
|
self.s3_client.delete_object(Bucket = self.bucket_name, Key = obj_name)
|
|
@@ -537,7 +537,7 @@ class S3Dict(PersiDict):
|
|
|
537
537
|
Raises:
|
|
538
538
|
KeyError: If the key does not exist in S3.
|
|
539
539
|
"""
|
|
540
|
-
key =
|
|
540
|
+
key = non_empty_persidict_key(key)
|
|
541
541
|
obj_name = self._build_full_objectname(key)
|
|
542
542
|
response = self.s3_client.head_object(Bucket=self.bucket_name, Key=obj_name)
|
|
543
543
|
return response["LastModified"].timestamp()
|
|
@@ -98,8 +98,6 @@ class SafeStrTuple(Sequence, Hashable):
|
|
|
98
98
|
candidate_strings.extend(SafeStrTuple(*a).strings)
|
|
99
99
|
else:
|
|
100
100
|
raise TypeError(f"Invalid argument type: {type(a)}")
|
|
101
|
-
if len(candidate_strings) == 0:
|
|
102
|
-
raise ValueError("At least one non-empty valid string is required")
|
|
103
101
|
self.strings = tuple(candidate_strings)
|
|
104
102
|
|
|
105
103
|
@property
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|