persidict 0.34.2__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.

@@ -1,12 +1,19 @@
1
- """Functions for signing and unsigning SafeStrTuples.
2
-
3
- Two functions from this module allow to add and remove
4
- hash signature suffixes to/from all strings in a SafeStrTuple:
5
- sign_safe_str_tuple() and unsign_safe_str_tuple().
6
-
7
- The suffixes are used to ensure correct work of persistent dictionaries
8
- (which employ SafeStrTuple-s as keys) with case-insensitive filesystems,
9
- e.g. MacOS HFS.
1
+ """Sign and unsign SafeStrTuple elements with deterministic suffixes.
2
+
3
+ This module provides helpers to add or remove short, deterministic hash
4
+ suffixes to every string element of a SafeStrTuple. These suffixes are used
5
+ to avoid collisions on case-insensitive filesystems (e.g., macOS HFS) while
6
+ keeping keys stable and portable.
7
+
8
+ Functions:
9
+ sign_safe_str_tuple(str_seq, digest_len):
10
+ Return a new SafeStrTuple where each element is suffixed with an
11
+ underscore and a base32-encoded MD5 digest fragment of length
12
+ ``digest_len``. If a correct suffix is already present, it is not
13
+ duplicated.
14
+ unsign_safe_str_tuple(str_seq, digest_len):
15
+ Return a new SafeStrTuple where a previously added suffix of length
16
+ ``digest_len`` is removed from each element, when detected.
10
17
  """
11
18
 
12
19
  import base64
@@ -14,12 +21,31 @@ import hashlib
14
21
  from .safe_str_tuple import SafeStrTuple
15
22
 
16
23
 
17
- def _create_signature_suffix(input_str:str, digest_len:int) -> str:
18
- """ Create a hash signature suffix for a string."""
24
+ def _create_signature_suffix(input_str: str, digest_len: int) -> str:
25
+ """Create a short, deterministic hash suffix for a string.
26
+
27
+ The suffix format is ``_<b32(md5(input))[:digest_len].lower()>``. For
28
+ ``digest_len == 0`` an empty string is returned.
29
+
30
+ Args:
31
+ input_str: Input string to sign.
32
+ digest_len: Number of base32 characters from the MD5 digest to include.
33
+ Must be non-negative. A value of 0 disables suffixing.
34
+
35
+ Returns:
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.
41
+ """
19
42
 
20
- assert isinstance(input_str, str)
21
- assert isinstance(digest_len, int)
22
- 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}")
23
49
 
24
50
  if digest_len == 0:
25
51
  return ""
@@ -32,12 +58,31 @@ def _create_signature_suffix(input_str:str, digest_len:int) -> str:
32
58
  return suffix
33
59
 
34
60
 
35
- def _add_signature_suffix_if_absent(input_str:str, digest_len:int) -> str:
36
- """ Add a hash signature suffix to a string if it's not there."""
61
+ def _add_signature_suffix_if_absent(input_str: str, digest_len: int) -> str:
62
+ """Add the hash signature suffix if it's not already present.
37
63
 
38
- assert isinstance(input_str, str)
39
- assert isinstance(digest_len, int)
40
- assert digest_len >= 0
64
+ If the input already ends with the exact suffix calculated from its
65
+ unsuffixed part, it is returned unchanged.
66
+
67
+ Args:
68
+ input_str: The string to sign.
69
+ digest_len: Length of the digest fragment to use; 0 leaves the string
70
+ unchanged.
71
+
72
+ Returns:
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.
78
+ """
79
+
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}")
41
86
 
42
87
  if digest_len == 0:
43
88
  return input_str
@@ -52,10 +97,18 @@ def _add_signature_suffix_if_absent(input_str:str, digest_len:int) -> str:
52
97
 
53
98
 
54
99
  def _add_all_suffixes_if_absent(
55
- str_seq:SafeStrTuple
56
- ,digest_len:int
100
+ str_seq: SafeStrTuple,
101
+ digest_len: int,
57
102
  ) -> SafeStrTuple:
58
- """Add hash signature suffixes to all strings in a SafeStrTuple."""
103
+ """Return a new SafeStrTuple with suffixes added to each element.
104
+
105
+ Args:
106
+ str_seq: Input sequence convertible to SafeStrTuple.
107
+ digest_len: Digest fragment length; 0 results in a no-op.
108
+
109
+ Returns:
110
+ SafeStrTuple: The suffixed sequence.
111
+ """
59
112
 
60
113
  str_seq = SafeStrTuple(str_seq)
61
114
 
@@ -68,12 +121,32 @@ def _add_all_suffixes_if_absent(
68
121
  return new_seq
69
122
 
70
123
 
71
- def _remove_signature_suffix_if_present(input_str:str, digest_len:int) -> str:
72
- """ Remove a hash signature suffix from a string if it's detected."""
124
+ def _remove_signature_suffix_if_present(input_str: str, digest_len: int) -> str:
125
+ """Remove the hash signature suffix if it is detected.
73
126
 
74
- assert isinstance(input_str, str)
75
- assert isinstance(digest_len, int)
76
- assert digest_len >= 0
127
+ Detection is performed by recomputing the expected suffix from the
128
+ unsuffixed portion and comparing it to the current ending.
129
+
130
+ Args:
131
+ input_str: The possibly suffixed string.
132
+ digest_len: Digest fragment length used during signing; 0 leaves the
133
+ string unchanged.
134
+
135
+ Returns:
136
+ str: The original string without the suffix if detected; otherwise the
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.
142
+ """
143
+
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}")
77
150
 
78
151
  if digest_len == 0:
79
152
  return input_str
@@ -88,10 +161,19 @@ def _remove_signature_suffix_if_present(input_str:str, digest_len:int) -> str:
88
161
 
89
162
 
90
163
  def _remove_all_signature_suffixes_if_present(
91
- str_seq:SafeStrTuple
92
- , digest_len:int
164
+ str_seq: SafeStrTuple,
165
+ digest_len: int,
93
166
  ) -> SafeStrTuple:
94
- """Remove hash signature suffixes from all strings in a SafeStrTuple."""
167
+ """Return a new SafeStrTuple with detected suffixes removed from elements.
168
+
169
+ Args:
170
+ str_seq: Input sequence convertible to SafeStrTuple.
171
+ digest_len: Digest fragment length used during signing; 0 results in a
172
+ no-op.
173
+
174
+ Returns:
175
+ SafeStrTuple: The unsigned sequence.
176
+ """
95
177
 
96
178
  str_seq = SafeStrTuple(str_seq)
97
179
 
@@ -108,10 +190,22 @@ def _remove_all_signature_suffixes_if_present(
108
190
  return new_seq
109
191
 
110
192
 
111
- def sign_safe_str_tuple(str_seq:SafeStrTuple
112
- , digest_len:int
193
+ def sign_safe_str_tuple(
194
+ str_seq: SafeStrTuple,
195
+ digest_len: int,
113
196
  ) -> SafeStrTuple:
114
- """Add hash signature suffixes to all strings in a SafeStrTuple."""
197
+ """Return a SafeStrTuple with signature suffixes added to all elements.
198
+
199
+ This is the public function for signing keys used by persistent dicts.
200
+
201
+ Args:
202
+ str_seq: Input sequence convertible to SafeStrTuple.
203
+ digest_len: Number of characters from the base32 digest to append. Use
204
+ 0 to disable suffixing.
205
+
206
+ Returns:
207
+ SafeStrTuple: The suffixed sequence.
208
+ """
115
209
 
116
210
  str_seq = SafeStrTuple(str_seq)
117
211
 
@@ -120,10 +214,20 @@ def sign_safe_str_tuple(str_seq:SafeStrTuple
120
214
  return str_seq
121
215
 
122
216
 
123
- def unsign_safe_str_tuple(str_seq:SafeStrTuple
124
- , digest_len:int
217
+ def unsign_safe_str_tuple(
218
+ str_seq: SafeStrTuple,
219
+ digest_len: int,
125
220
  ) -> SafeStrTuple:
126
- """Remove hash signature suffixes from all strings in a SafeStrTuple."""
221
+ """Return a SafeStrTuple with detected signature suffixes removed.
222
+
223
+ Args:
224
+ str_seq: Input sequence convertible to SafeStrTuple.
225
+ digest_len: Number of characters that were appended during signing. Use
226
+ 0 for a no-op.
227
+
228
+ Returns:
229
+ SafeStrTuple: The unsigned sequence.
230
+ """
127
231
 
128
232
  str_seq = SafeStrTuple(str_seq)
129
233
 
@@ -16,11 +16,20 @@ from typing import Any
16
16
  import joblib.hashing
17
17
  from .persi_dict import PersiDictKey
18
18
 
19
- def _get_md5_signature(x:Any) -> str:
20
- """Return base16 MD5 hash signature of an object.
19
+ def _get_md5_signature(x: Any) -> str:
20
+ """Compute an MD5 signature for an arbitrary Python object.
21
21
 
22
- Uses joblib's Hasher (or NumpyHasher). It uses Pickle for serialization,
23
- except for NumPy arrays, which use optimized custom routines.
22
+ Uses joblib's Hasher (or NumpyHasher when NumPy is available). joblib
23
+ relies on Pickle for serialization, except for NumPy arrays which are
24
+ handled by optimized routines. The resulting digest is returned as a
25
+ lower-case base16 string.
26
+
27
+ Args:
28
+ x: Any serializable Python object. NumPy arrays are supported with
29
+ specialized hashing.
30
+
31
+ Returns:
32
+ str: The base16 MD5 hash of the object.
24
33
  """
25
34
  if 'numpy' in sys.modules:
26
35
  hasher = joblib.hashing.NumpyHasher(hash_name='md5')
@@ -30,12 +39,26 @@ def _get_md5_signature(x:Any) -> str:
30
39
  return str(hash_signature)
31
40
 
32
41
  class WriteOnceDict(PersiDict):
33
- """ A dictionary that always keeps the first value assigned to a key.
34
-
35
- If a key is already set, it randomly checks the value against the value
36
- that was first set. If the new value is different, it raises a
37
- ValueError exception. Once can control the frequency of these checks
38
- or even completely disable them by setting `p_consistency_checks` to 0.
42
+ """Dictionary wrapper that preserves the first value written for each key.
43
+
44
+ Subsequent writes to an existing key are allowed but ignored as they are
45
+ expected to have exactly the same value. They are randomly checked
46
+ against the original value to ensure consistency. If a randomly triggered
47
+ check finds a difference, a ValueError is raised. The probability of
48
+ performing a check is controlled by ``p_consistency_checks``.
49
+
50
+ This is useful in concurrent or distributed settings where the same value
51
+ is assumed to be assigned repeatedly to the same key,
52
+ and you want to check this assumption (detect divergent values)
53
+ without paying the full cost of always comparing values.
54
+
55
+ Attributes:
56
+ p_consistency_checks (float): Probability in [0, 1] of performing a
57
+ consistency check for a key that has been previously set.
58
+ consistency_checks_attempted (int): Number of checks that were
59
+ attempted.
60
+ consistency_checks_passed (int): Number of checks that succeeded.
61
+ consistency_checks_failed (int): Derived as attempted - passed.
39
62
 
40
63
  """
41
64
  _wrapped_dict: PersiDict
@@ -43,18 +66,34 @@ class WriteOnceDict(PersiDict):
43
66
  _consistency_checks_attempted: int
44
67
  _consistency_checks_passed: int
45
68
 
46
- def __init__(self
47
- , wrapped_dict:PersiDict | None = None
48
- , p_consistency_checks: float | None=None):
69
+ def __init__(self,
70
+ wrapped_dict: PersiDict | None = None,
71
+ p_consistency_checks: float | None = None):
72
+ """Initialize a WriteOnceDict.
73
+
74
+ Args:
75
+ wrapped_dict: The underlying persistent dictionary to wrap. If not
76
+ provided, a FileDirDict with immutable_items=True is created.
77
+ p_consistency_checks: Probability in [0, 1] to perform a
78
+ consistency check when a key already exists. ``None`` means 0.0
79
+ (disabled).
80
+
81
+ Raises:
82
+ TypeError: If ``wrapped_dict`` is not a PersiDict instance.
83
+ ValueError: If ``wrapped_dict`` does not enforce immutable items.
84
+ """
49
85
  if wrapped_dict is None:
50
- wrapped_dict = FileDirDict(immutable_items = True)
51
- assert isinstance(wrapped_dict, PersiDict)
52
- assert wrapped_dict.immutable_items == True
86
+ wrapped_dict = FileDirDict(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)")
53
92
  self.p_consistency_checks = p_consistency_checks
54
- PersiDict.__init__(self
55
- , base_class_for_values=wrapped_dict.base_class_for_values
56
- , immutable_items=True
57
- , digest_len=wrapped_dict.digest_len)
93
+ PersiDict.__init__(self,
94
+ base_class_for_values=wrapped_dict.base_class_for_values,
95
+ immutable_items=True,
96
+ digest_len=wrapped_dict.digest_len)
58
97
  self._wrapped_dict = wrapped_dict
59
98
  self._consistency_checks_passed = 0
60
99
  self._consistency_checks_attempted = 0
@@ -62,12 +101,27 @@ class WriteOnceDict(PersiDict):
62
101
 
63
102
  @property
64
103
  def p_consistency_checks(self) -> float:
65
- """ Probability of checking the value against the first value set. """
104
+ """Probability of checking a new value against the first value stored.
105
+
106
+ Returns:
107
+ float: Probability in [0, 1].
108
+ """
66
109
  return self._p_consistency_checks
67
110
 
68
111
 
69
112
  @p_consistency_checks.setter
70
- def p_consistency_checks(self, value: float|None|KeepCurrentFlag) -> None:
113
+ def p_consistency_checks(self, value: float | None | KeepCurrentFlag) -> None:
114
+ """Set the probability of performing consistency checks.
115
+
116
+ Args:
117
+ value: Probability in [0, 1]. ``None`` is treated as 0.0.
118
+ ``KEEP_CURRENT`` can be used only after initialization to
119
+ preserve the current value.
120
+
121
+ Raises:
122
+ ValueError: If used with ``KEEP_CURRENT`` during initialization or
123
+ if ``value`` is outside [0, 1].
124
+ """
71
125
  if value is KEEP_CURRENT:
72
126
  if hasattr(self, '_p_consistency_checks'):
73
127
  return
@@ -85,34 +139,65 @@ class WriteOnceDict(PersiDict):
85
139
 
86
140
  @property
87
141
  def consistency_checks_failed(self) -> int:
88
- """ Returns the number of failed consistency checks. """
89
- return self._consistency_checks_attempted - self._consistency_checks_passed
142
+ """Number of failed consistency checks.
143
+
144
+ Returns:
145
+ int: Failed checks (attempted - passed).
146
+ """
147
+ return (self._consistency_checks_attempted
148
+ - self._consistency_checks_passed)
90
149
 
91
150
 
92
151
  @property
93
152
  def consistency_checks_attempted(self) -> int:
94
- """ Returns the number of attempted consistency checks. """
153
+ """Number of attempted consistency checks.
154
+
155
+ Returns:
156
+ int: Attempted checks counter.
157
+ """
95
158
  return self._consistency_checks_attempted
96
159
 
97
160
 
98
161
  @property
99
162
  def consistency_checks_passed(self) -> int:
100
- """ Returns the number of successful consistency checks. """
163
+ """Number of successful consistency checks.
164
+
165
+ Returns:
166
+ int: Passed checks counter.
167
+ """
101
168
  return self._consistency_checks_passed
102
169
 
103
170
 
104
171
  def get_params(self):
172
+ """Return parameterization of this instance.
173
+
174
+ Returns:
175
+ dict: A dictionary with keys 'wrapped_dict' and
176
+ 'p_consistency_checks', sorted by keys for deterministic
177
+ comparison/serialization.
178
+ """
105
179
  params = dict(
106
- wrapped_dict = self._wrapped_dict,
107
- p_consistency_checks = self.p_consistency_checks)
180
+ wrapped_dict=self._wrapped_dict,
181
+ p_consistency_checks=self.p_consistency_checks)
108
182
  sorted_params = sort_dict_by_keys(params)
109
183
  return sorted_params
110
184
 
111
- def __setitem__(self, key, value):
112
- """ Set the value of a key if it is not already set.
185
+ def __setitem__(self, key:PersiDictKey, value):
186
+ """Set a value for a key, preserving the first assignment.
187
+
188
+ If the key is new, the value is stored. If the key already exists,
189
+ a probabilistic consistency check may be performed to ensure the new
190
+ value matches the originally stored value. If a check is performed and
191
+ the values differ, a ValueError is raised.
113
192
 
114
- If the key is already set, it checks the value
115
- against the value that was first set.
193
+ Args:
194
+ key: key (string or sequence of strings or SafeStrTuple)
195
+ value: Value to store.
196
+
197
+ Raises:
198
+ KeyError: If the wrapped dict failed to set a new key unexpectedly.
199
+ ValueError: If a consistency check is triggered and the new value
200
+ differs from the original value for the key.
116
201
  """
117
202
  check_needed = False
118
203
 
@@ -149,34 +234,91 @@ class WriteOnceDict(PersiDict):
149
234
  + f"which is not allowed. Details here: {diff_dict} ")
150
235
  self._consistency_checks_passed += 1
151
236
 
152
- def __contains__(self, item):
237
+ def __contains__(self, item:PersiDictKey):
238
+ """Check if a key exists in the dictionary.
239
+
240
+ Args:
241
+ item: Key to check.
242
+
243
+ Returns:
244
+ bool: True if the key exists, False otherwise.
245
+ """
153
246
  return item in self._wrapped_dict
154
247
 
155
- def __getitem__(self, key):
248
+ def __getitem__(self, key:PersiDictKey):
249
+ """Retrieve a value by key.
250
+
251
+ Args:
252
+ key: Key to look up.
253
+
254
+ Returns:
255
+ Any: Stored value.
256
+ """
156
257
  return self._wrapped_dict[key]
157
258
 
158
259
  def __len__(self):
260
+ """Return the number of items stored.
261
+
262
+ Returns:
263
+ int: Number of key-value pairs.
264
+ """
159
265
  return len(self._wrapped_dict)
160
266
 
161
- def _generic_iter(self, iter_type: str):
267
+ def _generic_iter(self, iter_type: set[str]):
268
+ """Delegate iteration to the wrapped dict.
269
+
270
+ Args:
271
+ iter_type: tType of iterator: 'items' and/or 'keys' and/or 'timestamps'.
272
+
273
+ Returns:
274
+ Any: Iterator from the wrapped dictionary.
275
+ """
162
276
  return self._wrapped_dict._generic_iter(iter_type)
163
277
 
164
- def timestamp(self, key:PersiDictKey) -> float:
278
+ def timestamp(self, key: PersiDictKey) -> float:
279
+ """Return the timestamp for a given key.
280
+
281
+ Args:
282
+ key: Key for which to retrieve the timestamp.
283
+
284
+ Returns:
285
+ float: POSIX timestamp (seconds since epoch) of the item's last
286
+ modification as tracked by the wrapped dict.
287
+ """
165
288
  return self._wrapped_dict.timestamp(key)
166
289
 
167
290
  def __getattr__(self, name):
168
- # Forward attribute access to the wrapped object
291
+ """Forward attribute access to the wrapped object.
292
+
293
+ Args:
294
+ name: Attribute name.
295
+
296
+ Returns:
297
+ Any: Attribute value from the wrapped dict.
298
+ """
169
299
  return getattr(self._wrapped_dict, name)
170
300
 
171
301
  @property
172
302
  def base_dir(self):
303
+ """Base directory of the wrapped dict (if applicable)."""
173
304
  return self._wrapped_dict.base_dir
174
305
 
175
306
  @property
176
307
  def base_url(self):
308
+ """Base URL of the wrapped dict (if applicable)."""
177
309
  return self._wrapped_dict.base_url
178
310
 
179
- def get_subdict(self, prefix_key:PersiDictKey) -> WriteOnceDict:
311
+ def get_subdict(self, prefix_key: PersiDictKey) -> WriteOnceDict:
312
+ """Return a WriteOnceDict view over a sub-keyspace.
313
+
314
+ Args:
315
+ prefix_key: Prefix identifying the sub-dictionary.
316
+
317
+ Returns:
318
+ WriteOnceDict: A new WriteOnceDict wrapping the corresponding
319
+ sub-dictionary of the underlying store, sharing the same
320
+ p_consistency_checks probability.
321
+ """
180
322
  subdict = self._wrapped_dict.get_subdict(prefix_key)
181
323
  result = WriteOnceDict(subdict, self.p_consistency_checks)
182
324
  return result
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: persidict
3
- Version: 0.34.2
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
@@ -158,10 +158,9 @@ that simultaneously work with the same instance of a dictionary.
158
158
  * Insertion order is not preserved.
159
159
  * You cannot assign initial key-value pairs to a dictionary in its constructor.
160
160
  * `PersiDict` API has additional methods `delete_if_exists()`, `timestamp()`,
161
- `get_subdict()`, `subdicts()`, `random_keys()`, `newest_keys()`,
162
- `oldest_keys()`, `newest_values()`, `oldest_values()`,
163
- `get_params()`, `get_metaparams()`, and `get_default_metaparams()`,
164
- which are not available in native Python dicts.
161
+ `get_subdict()`, `subdicts()`, `random_key()`, `newest_keys()`,
162
+ `oldest_keys()`, `newest_values()`, `oldest_values()`, and
163
+ `get_params()`, which are not available in native Python dicts.
165
164
  * You can use KEEP_CURRENT constant as a fake new value
166
165
  to avoid actually setting/updating a value. Or DELETE_CURRENT as
167
166
  a fake new value to delete the previous value from a dictionary.
@@ -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
persidict/.DS_Store DELETED
Binary file
@@ -1,14 +0,0 @@
1
- persidict/.DS_Store,sha256=1lFlJ5EFymdzGAUAaI30vcaaLHt3F1LwpG7xILf9jsM,6148
2
- persidict/__init__.py,sha256=CDOSJGgCnyRTkGUTzaeg3Cqsxwx0-0EFieOtldXwAls,1380
3
- persidict/file_dir_dict.py,sha256=Dr4gdIC5uqykRgNca1pI_M_jEd_9FGjys0BvzAJR0JU,17804
4
- persidict/jokers.py,sha256=kX4bE-jKWTM2ki7JOmm_2uJS8zm8u6InZ_V12xo2ImI,1436
5
- persidict/overlapping_multi_dict.py,sha256=a-lUbmY15_HrDq6jSIt8F8tJboqbeYiuRQeW4elf_oU,2663
6
- persidict/persi_dict.py,sha256=SF6aWs6kCeeW-bZ9HJwx0sPX7Xav_aURqeSZ-j5quv0,14266
7
- persidict/s3_dict.py,sha256=j_Fb73wbkOrsneu3a48VUpDDXqVSTtEa0sRI-c_Kbjk,16104
8
- persidict/safe_chars.py,sha256=HjK1MwROYy_U9ui-rhg1i3nGkj52K4OFWD-wCCcnJ7Y,536
9
- persidict/safe_str_tuple.py,sha256=xyIzxlCKmvnNHkFFKWtcBREefxZ0-HLxoH_epYDt8qg,3719
10
- persidict/safe_str_tuple_signing.py,sha256=5uCjAVZRqOou-KpDZw-Exboc3-3vuayJMqrrt8aZ0ck,3742
11
- persidict/write_once_dict.py,sha256=-XHQhTEdvPHTKqLXK4WWW0k5cFitkzalVJC1n4BbKGo,6496
12
- persidict-0.34.2.dist-info/WHEEL,sha256=Jb20R3Ili4n9P1fcwuLup21eQ5r9WXhs4_qy7VTrgPI,79
13
- persidict-0.34.2.dist-info/METADATA,sha256=ihAySv6w1tA9-sHA6ONLvM0aDrRkn5RA4a1-G8dFxeE,9312
14
- persidict-0.34.2.dist-info/RECORD,,