persidict 0.34.3__py3-none-any.whl → 0.35.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.

Potentially problematic release.


This version of persidict might be problematic. Click here for more details.

@@ -74,16 +74,17 @@ class FileDirDict(PersiDict):
74
74
 
75
75
  Raises:
76
76
  ValueError: If base_dir points to a file; if file_type is "__etag__";
77
- or if configuration is inconsistent (e.g., non-str values with
78
- unsupported file_type).
79
- AssertionError: If file_type contains unsafe characters.
77
+ if file_type contains unsafe characters; or if configuration is
78
+ inconsistent (e.g., non-str values with unsupported file_type).
79
+ RuntimeError: If base_dir cannot be created or is not a directory.
80
80
  """
81
81
 
82
82
  super().__init__(immutable_items = immutable_items
83
83
  ,digest_len = digest_len
84
84
  ,base_class_for_values = base_class_for_values)
85
85
 
86
- assert file_type == replace_unsafe_chars(file_type, "")
86
+ if file_type != replace_unsafe_chars(file_type, ""):
87
+ raise ValueError("file_type contains unsafe characters")
87
88
  self.file_type = file_type
88
89
  if self.file_type == "__etag__":
89
90
  raise ValueError(
@@ -92,8 +93,8 @@ class FileDirDict(PersiDict):
92
93
 
93
94
  if (base_class_for_values is None or
94
95
  not issubclass(base_class_for_values,str)):
95
- assert file_type in {"json", "pkl"}, ("For non-string values"
96
- + " file_type must be either 'pkl' or 'json'.")
96
+ if file_type not in {"json", "pkl"}:
97
+ raise ValueError("For non-string values file_type must be either 'pkl' or 'json'.")
97
98
 
98
99
  base_dir = str(base_dir)
99
100
 
@@ -101,7 +102,8 @@ class FileDirDict(PersiDict):
101
102
  raise ValueError(f"{base_dir} is a file, not a directory.")
102
103
 
103
104
  os.makedirs(base_dir, exist_ok=True)
104
- assert os.path.isdir(base_dir)
105
+ if not os.path.isdir(base_dir):
106
+ raise RuntimeError(f"Failed to create or access directory: {base_dir}")
105
107
 
106
108
  # self.base_dir_param = _base_dir
107
109
  self._base_dir = os.path.abspath(base_dir)
@@ -539,7 +541,8 @@ class FileDirDict(PersiDict):
539
541
  KeyError: If immutable_items is True or if the key does not exist.
540
542
  """
541
543
  key = SafeStrTuple(key)
542
- assert not self.immutable_items, "Can't delete immutable items"
544
+ if self.immutable_items:
545
+ raise KeyError("Can't delete immutable items")
543
546
  filename = self._build_full_path(key)
544
547
  if not os.path.isfile(filename):
545
548
  raise KeyError(f"File {filename} does not exist")
@@ -563,11 +566,19 @@ class FileDirDict(PersiDict):
563
566
  - Any if result_type == {"values"}
564
567
  - tuple[SafeStrTuple, Any] if result_type == {"keys", "values"}
565
568
  - tuple[..., float] including POSIX timestamp if "timestamps" is requested.
569
+
570
+ Raises:
571
+ TypeError: If result_type is not a set.
572
+ ValueError: If result_type is empty or contains unsupported labels.
566
573
  """
567
- assert isinstance(result_type, set)
568
- assert 1 <= len(result_type) <= 3
569
- assert len(result_type | {"keys", "values", "timestamps"}) == 3
570
- assert 1 <= len(result_type & {"keys", "values", "timestamps"}) <= 3
574
+ if not isinstance(result_type, set):
575
+ raise TypeError("result_type must be a set")
576
+ if not (1 <= len(result_type) <= 3):
577
+ raise ValueError("result_type must be a non-empty subset of {'keys','values','timestamps'}")
578
+ allowed = {"keys", "values", "timestamps"}
579
+ invalid = result_type - allowed
580
+ if invalid:
581
+ raise ValueError(f"Unsupported result_type entries: {sorted(invalid)}; allowed: {sorted(allowed)}")
571
582
 
572
583
  walk_results = os.walk(self._base_dir)
573
584
  ext_len = len(self.file_type) + 1
@@ -46,18 +46,22 @@ class OverlappingMultiDict:
46
46
  resulting dict also receives file_type=<key>.
47
47
 
48
48
  Raises:
49
- AssertionError: If dict_type is not a PersiDict subclass, or if
49
+ TypeError: If dict_type is not a PersiDict subclass, or if
50
50
  shared_subdicts_params is not a dict, or if any individual
51
51
  parameter set is not a dict.
52
52
  """
53
- assert issubclass(dict_type, PersiDict)
54
- assert isinstance(shared_subdicts_params, dict)
53
+ if not issubclass(dict_type, PersiDict):
54
+ raise TypeError("dict_type must be a subclass of PersiDict")
55
+ if not isinstance(shared_subdicts_params, dict):
56
+ raise TypeError("shared_subdicts_params must be a dict")
55
57
  self.dict_type = dict_type
56
58
  self.shared_subdicts_params = shared_subdicts_params
57
59
  self.individual_subdicts_params = individual_subdicts_params
58
60
  self.subdicts_names = list(individual_subdicts_params.keys())
59
61
  for subdict_name in individual_subdicts_params:
60
- assert isinstance(individual_subdicts_params[subdict_name], dict)
62
+ if not isinstance(individual_subdicts_params[subdict_name], dict):
63
+ raise TypeError(
64
+ f"Params for subdict {subdict_name!r} must be a dict")
61
65
  self.__dict__[subdict_name] = dict_type(
62
66
  **{**shared_subdicts_params
63
67
  ,**individual_subdicts_params[subdict_name]
persidict/persi_dict.py CHANGED
@@ -240,11 +240,21 @@ class PersiDict(MutableMapping, ParameterizableClass):
240
240
  Returns:
241
241
  Any: An iterator yielding keys, values, and/or timestamps based on
242
242
  result_type.
243
- """
244
- assert isinstance(result_type, set)
245
- assert 1 <= len(result_type) <= 3
246
- assert len(result_type | {"keys", "values", "timestamps"}) == 3
247
- assert 1 <= len(result_type & {"keys", "values", "timestamps"}) <= 3
243
+
244
+ Raises:
245
+ TypeError: If result_type is not a set.
246
+ ValueError: If result_type contains invalid entries or an invalid number of items.
247
+ NotImplementedError: Subclasses must implement the concrete iterator.
248
+ """
249
+ if not isinstance(result_type, set):
250
+ raise TypeError("result_type must be a set of strings")
251
+ if not (1 <= len(result_type) <= 3):
252
+ raise ValueError("result_type must contain between 1 and 3 elements")
253
+ allowed = {"keys", "values", "timestamps"}
254
+ if (result_type | allowed) != allowed:
255
+ raise ValueError("result_type can only contain 'keys', 'values', 'timestamps'")
256
+ if not (1 <= len(result_type & allowed) <= 3):
257
+ raise ValueError("result_type must include at least one of 'keys', 'values', 'timestamps'")
248
258
  raise NotImplementedError
249
259
 
250
260
 
@@ -322,11 +332,12 @@ class PersiDict(MutableMapping, ParameterizableClass):
322
332
  Any: Existing value if present; otherwise the provided default.
323
333
 
324
334
  Raises:
325
- AssertionError: If default is a Joker command.
335
+ TypeError: If default is a Joker command (KEEP_CURRENT/DELETE_CURRENT).
326
336
  """
327
337
  # TODO: check edge cases to ensure the same semantics as standard dicts
328
338
  key = SafeStrTuple(key)
329
- assert not isinstance(default, Joker)
339
+ if isinstance(default, Joker):
340
+ raise TypeError("default must be a regular value, not a Joker command")
330
341
  if key in self:
331
342
  return self[key]
332
343
  else:
@@ -383,7 +394,7 @@ class PersiDict(MutableMapping, ParameterizableClass):
383
394
  Raises:
384
395
  KeyError: If items are immutable (immutable_items is True).
385
396
  """
386
- if self.immutable_items: # TODO: change to exceptions
397
+ if self.immutable_items:
387
398
  raise KeyError("Can't delete an immutable key-value pair")
388
399
 
389
400
  for k in self.keys():
@@ -408,7 +419,7 @@ class PersiDict(MutableMapping, ParameterizableClass):
408
419
  KeyError: If items are immutable (immutable_items is True).
409
420
  """
410
421
 
411
- if self.immutable_items: # TODO: change to exceptions
422
+ if self.immutable_items:
412
423
  raise KeyError("Can't delete an immutable key-value pair")
413
424
 
414
425
  key = SafeStrTuple(key)
persidict/s3_dict.py CHANGED
@@ -433,12 +433,23 @@ class S3Dict(PersiDict):
433
433
  - Any if result_type == {"values"}
434
434
  - tuple[SafeStrTuple, Any] if result_type == {"keys", "values"}
435
435
  - tuple[..., float] including POSIX timestamp if "timestamps" is requested.
436
+
437
+ Raises:
438
+ ValueError: If result_type is not a set or contains entries other than
439
+ "keys", "values", and/or "timestamps", or if it is empty.
436
440
  """
437
441
 
438
- assert isinstance(result_type, set)
439
- assert 1 <= len(result_type) <= 3
440
- assert len(result_type | {"keys", "values", "timestamps"}) == 3
441
- assert 1 <= len(result_type & {"keys", "values", "timestamps"}) <= 3
442
+ if not isinstance(result_type, set):
443
+ raise ValueError(
444
+ "result_type must be a set containing one to three of: 'keys', 'values', 'timestamps'"
445
+ )
446
+ if not (1 <= len(result_type) <= 3):
447
+ raise ValueError("result_type must be a non-empty set with at most three elements")
448
+ allowed = {"keys", "values", "timestamps"}
449
+ if not result_type.issubset(allowed):
450
+ invalid = ", ".join(sorted(result_type - allowed))
451
+ raise ValueError(f"result_type contains invalid entries: {invalid}. Allowed: {sorted(allowed)}")
452
+ # Intersections/length checks are implied by the above conditions.
442
453
 
443
454
  suffix = "." + self.file_type
444
455
  ext_len = len(self.file_type) + 1
@@ -452,8 +463,14 @@ class S3Dict(PersiDict):
452
463
 
453
464
  Returns:
454
465
  SafeStrTuple: The parsed key parts, still signed.
466
+
467
+ Raises:
468
+ ValueError: If the provided key does not start with this dictionary's root_prefix.
455
469
  """
456
- assert full_name.startswith(self.root_prefix)
470
+ if not full_name.startswith(self.root_prefix):
471
+ raise ValueError(
472
+ f"S3 object key '{full_name}' is outside of root_prefix '{self.root_prefix}'"
473
+ )
457
474
  result = full_name[prefix_len:-ext_len].split(sep="/")
458
475
  return SafeStrTuple(result)
459
476
 
@@ -71,26 +71,33 @@ class SafeStrTuple(Sequence, Hashable):
71
71
  **kwargs: Not supported.
72
72
 
73
73
  Raises:
74
- AssertionError: If kwargs are provided; if no args are provided; if
75
- any string is empty, too long, or contains disallowed chars; or
76
- if an argument has an invalid type.
74
+ TypeError: If unexpected keyword arguments are provided, if no args
75
+ are provided, or if an argument has an invalid type.
76
+ ValueError: If a string is empty, too long, or contains disallowed
77
+ characters.
77
78
  """
78
- assert len(kwargs) == 0
79
- assert len(args) > 0
79
+ if len(kwargs) != 0:
80
+ raise TypeError(f"Unexpected keyword arguments: {list(kwargs.keys())}")
81
+ if len(args) == 0:
82
+ raise TypeError("At least one argument is required")
80
83
  candidate_strings = []
81
84
  for a in args:
82
85
  if isinstance(a, SafeStrTuple):
83
86
  candidate_strings.extend(a.strings)
84
87
  elif isinstance(a, str):
85
- assert len(a) > 0
86
- assert len(a) < SAFE_STRING_MAX_LENGTH
87
- assert all(c in SAFE_CHARS_SET for c in a)
88
+ if len(a) == 0:
89
+ raise ValueError("Strings must be non-empty")
90
+ if len(a) >= SAFE_STRING_MAX_LENGTH:
91
+ raise ValueError(
92
+ f"String length must be < {SAFE_STRING_MAX_LENGTH}, got {len(a)}")
93
+ if not all(c in SAFE_CHARS_SET for c in a):
94
+ raise ValueError("String contains disallowed characters")
88
95
  candidate_strings.append(a)
89
96
  elif _is_sequence_not_mapping(a):
90
97
  if len(a) > 0:
91
98
  candidate_strings.extend(SafeStrTuple(*a).strings)
92
99
  else:
93
- assert False, f"Invalid argument type: {type(a)}"
100
+ raise TypeError(f"Invalid argument type: {type(a)}")
94
101
  self.strings = tuple(candidate_strings)
95
102
 
96
103
  @property
@@ -34,11 +34,18 @@ def _create_signature_suffix(input_str: str, digest_len: int) -> str:
34
34
 
35
35
  Returns:
36
36
  str: The computed suffix to append (may be an empty string).
37
+
38
+ Raises:
39
+ TypeError: If input_str is not a str or digest_len is not an int.
40
+ ValueError: If digest_len is negative.
37
41
  """
38
42
 
39
- assert isinstance(input_str, str)
40
- assert isinstance(digest_len, int)
41
- assert digest_len >= 0
43
+ if not isinstance(input_str, str):
44
+ raise TypeError(f"input_str must be str, got {type(input_str)!r}")
45
+ if not isinstance(digest_len, int):
46
+ raise TypeError(f"digest_len must be int, got {type(digest_len)!r}")
47
+ if digest_len < 0:
48
+ raise ValueError(f"digest_len must be >= 0, got {digest_len}")
42
49
 
43
50
  if digest_len == 0:
44
51
  return ""
@@ -64,11 +71,18 @@ def _add_signature_suffix_if_absent(input_str: str, digest_len: int) -> str:
64
71
 
65
72
  Returns:
66
73
  str: The original or suffixed string.
74
+
75
+ Raises:
76
+ TypeError: If input_str is not a str or digest_len is not an int.
77
+ ValueError: If digest_len is negative.
67
78
  """
68
79
 
69
- assert isinstance(input_str, str)
70
- assert isinstance(digest_len, int)
71
- assert digest_len >= 0
80
+ if not isinstance(input_str, str):
81
+ raise TypeError(f"input_str must be str, got {type(input_str)!r}")
82
+ if not isinstance(digest_len, int):
83
+ raise TypeError(f"digest_len must be int, got {type(digest_len)!r}")
84
+ if digest_len < 0:
85
+ raise ValueError(f"digest_len must be >= 0, got {digest_len}")
72
86
 
73
87
  if digest_len == 0:
74
88
  return input_str
@@ -121,11 +135,18 @@ def _remove_signature_suffix_if_present(input_str: str, digest_len: int) -> str:
121
135
  Returns:
122
136
  str: The original string without the suffix if detected; otherwise the
123
137
  original string.
138
+
139
+ Raises:
140
+ TypeError: If input_str is not a str or digest_len is not an int.
141
+ ValueError: If digest_len is negative.
124
142
  """
125
143
 
126
- assert isinstance(input_str, str)
127
- assert isinstance(digest_len, int)
128
- assert digest_len >= 0
144
+ if not isinstance(input_str, str):
145
+ raise TypeError(f"input_str must be str, got {type(input_str)!r}")
146
+ if not isinstance(digest_len, int):
147
+ raise TypeError(f"digest_len must be int, got {type(digest_len)!r}")
148
+ if digest_len < 0:
149
+ raise ValueError(f"digest_len must be >= 0, got {digest_len}")
129
150
 
130
151
  if digest_len == 0:
131
152
  return input_str
@@ -79,13 +79,16 @@ class WriteOnceDict(PersiDict):
79
79
  (disabled).
80
80
 
81
81
  Raises:
82
- AssertionError: If ``wrapped_dict`` is not a PersiDict or does not
83
- enforce immutable items.
82
+ TypeError: If ``wrapped_dict`` is not a PersiDict instance.
83
+ ValueError: If ``wrapped_dict`` does not enforce immutable items.
84
84
  """
85
85
  if wrapped_dict is None:
86
86
  wrapped_dict = FileDirDict(immutable_items=True)
87
- assert isinstance(wrapped_dict, PersiDict)
88
- assert wrapped_dict.immutable_items == True
87
+ if not isinstance(wrapped_dict, PersiDict):
88
+ raise TypeError("wrapped_dict must be a PersiDict instance")
89
+ if wrapped_dict.immutable_items is not True:
90
+ raise ValueError("wrapped_dict must be append-only "
91
+ "(immutable_items==True)")
89
92
  self.p_consistency_checks = p_consistency_checks
90
93
  PersiDict.__init__(self,
91
94
  base_class_for_values=wrapped_dict.base_class_for_values,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: persidict
3
- Version: 0.34.3
3
+ Version: 0.35.0
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
@@ -0,0 +1,13 @@
1
+ persidict/__init__.py,sha256=CDOSJGgCnyRTkGUTzaeg3Cqsxwx0-0EFieOtldXwAls,1380
2
+ persidict/file_dir_dict.py,sha256=gvCyk_kp_3AC-zkHuSj-0lM4hf_fBK6iz3ffGQ7jtvU,25757
3
+ persidict/jokers.py,sha256=H2MzKllvgm7t2sjX3GaRNLngOiG2ohE-lv0B3g3J1SQ,2710
4
+ persidict/overlapping_multi_dict.py,sha256=v1c2kf3Bhm2Dh6SGEsRV58hQI11YX9ZPxyrDV_1d5s8,5360
5
+ persidict/persi_dict.py,sha256=DIMQaY4gE8NSYTlHlk9rfOJJEYUuLV8kmQ-gc474py4,20052
6
+ persidict/s3_dict.py,sha256=VKDqY9sASffeXtfbavVWk8-umrioIG5Xq57Qqg1wPH4,21522
7
+ persidict/safe_chars.py,sha256=9Qy24fu2dmiJOdmCF8mKZULfQaRp7H4oxfgDXeLgogI,1160
8
+ persidict/safe_str_tuple.py,sha256=YBTcYjUKIffznOawXb9xKjz4HaKdklrgyVtegJFmr5w,7202
9
+ persidict/safe_str_tuple_signing.py,sha256=RQAj4fnpRVaOe0KpwLler1UTaeNOgXCQpU3t80ixtxg,7493
10
+ persidict/write_once_dict.py,sha256=-lPQ_yuU62pczHT0BYO6SFbiZBKFq8Tj9ln3jCzNDzA,11443
11
+ persidict-0.35.0.dist-info/WHEEL,sha256=F3mArEuDT3LDFEqo9fCiUx6ISLN64aIhcGSiIwtu4r8,79
12
+ persidict-0.35.0.dist-info/METADATA,sha256=oo_YL1_W4ux3lTJZvREQPcin1ILJmvxtMDlWuITUwCw,9262
13
+ persidict-0.35.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.8.15
2
+ Generator: uv 0.8.16
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,13 +0,0 @@
1
- persidict/__init__.py,sha256=CDOSJGgCnyRTkGUTzaeg3Cqsxwx0-0EFieOtldXwAls,1380
2
- persidict/file_dir_dict.py,sha256=Lf65HqI00J7Pi2jELWEXo5DUk8cW1GdKdgkVyFgFHGo,25135
3
- persidict/jokers.py,sha256=H2MzKllvgm7t2sjX3GaRNLngOiG2ohE-lv0B3g3J1SQ,2710
4
- persidict/overlapping_multi_dict.py,sha256=ZUbkNConqUDTP1LFxKv0WFvsAPcCiHUjhNKe8912wNs,5112
5
- persidict/persi_dict.py,sha256=mwHng2pjXf_durHKeiTZRDOVJbpHjANkdVR1_y2Bs4E,19408
6
- persidict/s3_dict.py,sha256=Xl-ZOukY7dl-LkujaPiPNVZo9sK9t5bFhG2lCYLAbjs,20635
7
- persidict/safe_chars.py,sha256=9Qy24fu2dmiJOdmCF8mKZULfQaRp7H4oxfgDXeLgogI,1160
8
- persidict/safe_str_tuple.py,sha256=4wt3Jfd7efKczTP1o--XkOSic5_7riVDIgnK19Bzfbk,6741
9
- persidict/safe_str_tuple_signing.py,sha256=ihV1hLx24N9f64nFbqAvbnsPZDon-kUAVncs1628Q6M,6428
10
- persidict/write_once_dict.py,sha256=5tGqm51EIEcjnAhP8oOY-eQ1AaQhteQxSd0SPIp934c,11217
11
- persidict-0.34.3.dist-info/WHEEL,sha256=Jb20R3Ili4n9P1fcwuLup21eQ5r9WXhs4_qy7VTrgPI,79
12
- persidict-0.34.3.dist-info/METADATA,sha256=nXLxLxK2mZ7NmVfRu6H--W8hVNJOQZl1lAs1mbkALXQ,9262
13
- persidict-0.34.3.dist-info/RECORD,,