persidict 0.34.1__py3-none-any.whl → 0.34.3__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,8 +21,20 @@ 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
+ """
19
38
 
20
39
  assert isinstance(input_str, str)
21
40
  assert isinstance(digest_len, int)
@@ -32,8 +51,20 @@ def _create_signature_suffix(input_str:str, digest_len:int) -> str:
32
51
  return suffix
33
52
 
34
53
 
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."""
54
+ def _add_signature_suffix_if_absent(input_str: str, digest_len: int) -> str:
55
+ """Add the hash signature suffix if it's not already present.
56
+
57
+ If the input already ends with the exact suffix calculated from its
58
+ unsuffixed part, it is returned unchanged.
59
+
60
+ Args:
61
+ input_str: The string to sign.
62
+ digest_len: Length of the digest fragment to use; 0 leaves the string
63
+ unchanged.
64
+
65
+ Returns:
66
+ str: The original or suffixed string.
67
+ """
37
68
 
38
69
  assert isinstance(input_str, str)
39
70
  assert isinstance(digest_len, int)
@@ -52,10 +83,18 @@ def _add_signature_suffix_if_absent(input_str:str, digest_len:int) -> str:
52
83
 
53
84
 
54
85
  def _add_all_suffixes_if_absent(
55
- str_seq:SafeStrTuple
56
- ,digest_len:int
86
+ str_seq: SafeStrTuple,
87
+ digest_len: int,
57
88
  ) -> SafeStrTuple:
58
- """Add hash signature suffixes to all strings in a SafeStrTuple."""
89
+ """Return a new SafeStrTuple with suffixes added to each element.
90
+
91
+ Args:
92
+ str_seq: Input sequence convertible to SafeStrTuple.
93
+ digest_len: Digest fragment length; 0 results in a no-op.
94
+
95
+ Returns:
96
+ SafeStrTuple: The suffixed sequence.
97
+ """
59
98
 
60
99
  str_seq = SafeStrTuple(str_seq)
61
100
 
@@ -68,8 +107,21 @@ def _add_all_suffixes_if_absent(
68
107
  return new_seq
69
108
 
70
109
 
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."""
110
+ def _remove_signature_suffix_if_present(input_str: str, digest_len: int) -> str:
111
+ """Remove the hash signature suffix if it is detected.
112
+
113
+ Detection is performed by recomputing the expected suffix from the
114
+ unsuffixed portion and comparing it to the current ending.
115
+
116
+ Args:
117
+ input_str: The possibly suffixed string.
118
+ digest_len: Digest fragment length used during signing; 0 leaves the
119
+ string unchanged.
120
+
121
+ Returns:
122
+ str: The original string without the suffix if detected; otherwise the
123
+ original string.
124
+ """
73
125
 
74
126
  assert isinstance(input_str, str)
75
127
  assert isinstance(digest_len, int)
@@ -88,10 +140,19 @@ def _remove_signature_suffix_if_present(input_str:str, digest_len:int) -> str:
88
140
 
89
141
 
90
142
  def _remove_all_signature_suffixes_if_present(
91
- str_seq:SafeStrTuple
92
- , digest_len:int
143
+ str_seq: SafeStrTuple,
144
+ digest_len: int,
93
145
  ) -> SafeStrTuple:
94
- """Remove hash signature suffixes from all strings in a SafeStrTuple."""
146
+ """Return a new SafeStrTuple with detected suffixes removed from elements.
147
+
148
+ Args:
149
+ str_seq: Input sequence convertible to SafeStrTuple.
150
+ digest_len: Digest fragment length used during signing; 0 results in a
151
+ no-op.
152
+
153
+ Returns:
154
+ SafeStrTuple: The unsigned sequence.
155
+ """
95
156
 
96
157
  str_seq = SafeStrTuple(str_seq)
97
158
 
@@ -108,10 +169,22 @@ def _remove_all_signature_suffixes_if_present(
108
169
  return new_seq
109
170
 
110
171
 
111
- def sign_safe_str_tuple(str_seq:SafeStrTuple
112
- , digest_len:int
172
+ def sign_safe_str_tuple(
173
+ str_seq: SafeStrTuple,
174
+ digest_len: int,
113
175
  ) -> SafeStrTuple:
114
- """Add hash signature suffixes to all strings in a SafeStrTuple."""
176
+ """Return a SafeStrTuple with signature suffixes added to all elements.
177
+
178
+ This is the public function for signing keys used by persistent dicts.
179
+
180
+ Args:
181
+ str_seq: Input sequence convertible to SafeStrTuple.
182
+ digest_len: Number of characters from the base32 digest to append. Use
183
+ 0 to disable suffixing.
184
+
185
+ Returns:
186
+ SafeStrTuple: The suffixed sequence.
187
+ """
115
188
 
116
189
  str_seq = SafeStrTuple(str_seq)
117
190
 
@@ -120,10 +193,20 @@ def sign_safe_str_tuple(str_seq:SafeStrTuple
120
193
  return str_seq
121
194
 
122
195
 
123
- def unsign_safe_str_tuple(str_seq:SafeStrTuple
124
- , digest_len:int
196
+ def unsign_safe_str_tuple(
197
+ str_seq: SafeStrTuple,
198
+ digest_len: int,
125
199
  ) -> SafeStrTuple:
126
- """Remove hash signature suffixes from all strings in a SafeStrTuple."""
200
+ """Return a SafeStrTuple with detected signature suffixes removed.
201
+
202
+ Args:
203
+ str_seq: Input sequence convertible to SafeStrTuple.
204
+ digest_len: Number of characters that were appended during signing. Use
205
+ 0 for a no-op.
206
+
207
+ Returns:
208
+ SafeStrTuple: The unsigned sequence.
209
+ """
127
210
 
128
211
  str_seq = SafeStrTuple(str_seq)
129
212
 
@@ -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,31 @@ 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
+ AssertionError: If ``wrapped_dict`` is not a PersiDict or does not
83
+ enforce immutable items.
84
+ """
49
85
  if wrapped_dict is None:
50
- wrapped_dict = FileDirDict(immutable_items = True)
86
+ wrapped_dict = FileDirDict(immutable_items=True)
51
87
  assert isinstance(wrapped_dict, PersiDict)
52
88
  assert wrapped_dict.immutable_items == True
53
89
  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)
90
+ PersiDict.__init__(self,
91
+ base_class_for_values=wrapped_dict.base_class_for_values,
92
+ immutable_items=True,
93
+ digest_len=wrapped_dict.digest_len)
58
94
  self._wrapped_dict = wrapped_dict
59
95
  self._consistency_checks_passed = 0
60
96
  self._consistency_checks_attempted = 0
@@ -62,12 +98,27 @@ class WriteOnceDict(PersiDict):
62
98
 
63
99
  @property
64
100
  def p_consistency_checks(self) -> float:
65
- """ Probability of checking the value against the first value set. """
101
+ """Probability of checking a new value against the first value stored.
102
+
103
+ Returns:
104
+ float: Probability in [0, 1].
105
+ """
66
106
  return self._p_consistency_checks
67
107
 
68
108
 
69
109
  @p_consistency_checks.setter
70
- def p_consistency_checks(self, value: float|None|KeepCurrentFlag) -> None:
110
+ def p_consistency_checks(self, value: float | None | KeepCurrentFlag) -> None:
111
+ """Set the probability of performing consistency checks.
112
+
113
+ Args:
114
+ value: Probability in [0, 1]. ``None`` is treated as 0.0.
115
+ ``KEEP_CURRENT`` can be used only after initialization to
116
+ preserve the current value.
117
+
118
+ Raises:
119
+ ValueError: If used with ``KEEP_CURRENT`` during initialization or
120
+ if ``value`` is outside [0, 1].
121
+ """
71
122
  if value is KEEP_CURRENT:
72
123
  if hasattr(self, '_p_consistency_checks'):
73
124
  return
@@ -85,34 +136,65 @@ class WriteOnceDict(PersiDict):
85
136
 
86
137
  @property
87
138
  def consistency_checks_failed(self) -> int:
88
- """ Returns the number of failed consistency checks. """
89
- return self._consistency_checks_attempted - self._consistency_checks_passed
139
+ """Number of failed consistency checks.
140
+
141
+ Returns:
142
+ int: Failed checks (attempted - passed).
143
+ """
144
+ return (self._consistency_checks_attempted
145
+ - self._consistency_checks_passed)
90
146
 
91
147
 
92
148
  @property
93
149
  def consistency_checks_attempted(self) -> int:
94
- """ Returns the number of attempted consistency checks. """
150
+ """Number of attempted consistency checks.
151
+
152
+ Returns:
153
+ int: Attempted checks counter.
154
+ """
95
155
  return self._consistency_checks_attempted
96
156
 
97
157
 
98
158
  @property
99
159
  def consistency_checks_passed(self) -> int:
100
- """ Returns the number of successful consistency checks. """
160
+ """Number of successful consistency checks.
161
+
162
+ Returns:
163
+ int: Passed checks counter.
164
+ """
101
165
  return self._consistency_checks_passed
102
166
 
103
167
 
104
168
  def get_params(self):
169
+ """Return parameterization of this instance.
170
+
171
+ Returns:
172
+ dict: A dictionary with keys 'wrapped_dict' and
173
+ 'p_consistency_checks', sorted by keys for deterministic
174
+ comparison/serialization.
175
+ """
105
176
  params = dict(
106
- wrapped_dict = self._wrapped_dict,
107
- p_consistency_checks = self.p_consistency_checks)
177
+ wrapped_dict=self._wrapped_dict,
178
+ p_consistency_checks=self.p_consistency_checks)
108
179
  sorted_params = sort_dict_by_keys(params)
109
180
  return sorted_params
110
181
 
111
- def __setitem__(self, key, value):
112
- """ Set the value of a key if it is not already set.
182
+ def __setitem__(self, key:PersiDictKey, value):
183
+ """Set a value for a key, preserving the first assignment.
184
+
185
+ If the key is new, the value is stored. If the key already exists,
186
+ a probabilistic consistency check may be performed to ensure the new
187
+ value matches the originally stored value. If a check is performed and
188
+ the values differ, a ValueError is raised.
113
189
 
114
- If the key is already set, it checks the value
115
- against the value that was first set.
190
+ Args:
191
+ key: key (string or sequence of strings or SafeStrTuple)
192
+ value: Value to store.
193
+
194
+ Raises:
195
+ KeyError: If the wrapped dict failed to set a new key unexpectedly.
196
+ ValueError: If a consistency check is triggered and the new value
197
+ differs from the original value for the key.
116
198
  """
117
199
  check_needed = False
118
200
 
@@ -149,34 +231,91 @@ class WriteOnceDict(PersiDict):
149
231
  + f"which is not allowed. Details here: {diff_dict} ")
150
232
  self._consistency_checks_passed += 1
151
233
 
152
- def __contains__(self, item):
234
+ def __contains__(self, item:PersiDictKey):
235
+ """Check if a key exists in the dictionary.
236
+
237
+ Args:
238
+ item: Key to check.
239
+
240
+ Returns:
241
+ bool: True if the key exists, False otherwise.
242
+ """
153
243
  return item in self._wrapped_dict
154
244
 
155
- def __getitem__(self, key):
245
+ def __getitem__(self, key:PersiDictKey):
246
+ """Retrieve a value by key.
247
+
248
+ Args:
249
+ key: Key to look up.
250
+
251
+ Returns:
252
+ Any: Stored value.
253
+ """
156
254
  return self._wrapped_dict[key]
157
255
 
158
256
  def __len__(self):
257
+ """Return the number of items stored.
258
+
259
+ Returns:
260
+ int: Number of key-value pairs.
261
+ """
159
262
  return len(self._wrapped_dict)
160
263
 
161
- def _generic_iter(self, iter_type: str):
264
+ def _generic_iter(self, iter_type: set[str]):
265
+ """Delegate iteration to the wrapped dict.
266
+
267
+ Args:
268
+ iter_type: tType of iterator: 'items' and/or 'keys' and/or 'timestamps'.
269
+
270
+ Returns:
271
+ Any: Iterator from the wrapped dictionary.
272
+ """
162
273
  return self._wrapped_dict._generic_iter(iter_type)
163
274
 
164
- def timestamp(self, key:PersiDictKey) -> float:
275
+ def timestamp(self, key: PersiDictKey) -> float:
276
+ """Return the timestamp for a given key.
277
+
278
+ Args:
279
+ key: Key for which to retrieve the timestamp.
280
+
281
+ Returns:
282
+ float: POSIX timestamp (seconds since epoch) of the item's last
283
+ modification as tracked by the wrapped dict.
284
+ """
165
285
  return self._wrapped_dict.timestamp(key)
166
286
 
167
287
  def __getattr__(self, name):
168
- # Forward attribute access to the wrapped object
288
+ """Forward attribute access to the wrapped object.
289
+
290
+ Args:
291
+ name: Attribute name.
292
+
293
+ Returns:
294
+ Any: Attribute value from the wrapped dict.
295
+ """
169
296
  return getattr(self._wrapped_dict, name)
170
297
 
171
298
  @property
172
299
  def base_dir(self):
300
+ """Base directory of the wrapped dict (if applicable)."""
173
301
  return self._wrapped_dict.base_dir
174
302
 
175
303
  @property
176
304
  def base_url(self):
305
+ """Base URL of the wrapped dict (if applicable)."""
177
306
  return self._wrapped_dict.base_url
178
307
 
179
- def get_subdict(self, prefix_key:PersiDictKey) -> WriteOnceDict:
308
+ def get_subdict(self, prefix_key: PersiDictKey) -> WriteOnceDict:
309
+ """Return a WriteOnceDict view over a sub-keyspace.
310
+
311
+ Args:
312
+ prefix_key: Prefix identifying the sub-dictionary.
313
+
314
+ Returns:
315
+ WriteOnceDict: A new WriteOnceDict wrapping the corresponding
316
+ sub-dictionary of the underlying store, sharing the same
317
+ p_consistency_checks probability.
318
+ """
180
319
  subdict = self._wrapped_dict.get_subdict(prefix_key)
181
320
  result = WriteOnceDict(subdict, self.p_consistency_checks)
182
321
  return result
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: persidict
3
- Version: 0.34.1
3
+ Version: 0.34.3
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=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,,
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=fzU3GKKNor6WRoIpP_-7d8ckO4eyhBOOo5kbpsmdxQA,15434
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.1.dist-info/WHEEL,sha256=Jb20R3Ili4n9P1fcwuLup21eQ5r9WXhs4_qy7VTrgPI,79
13
- persidict-0.34.1.dist-info/METADATA,sha256=a6o3joH0aBP1HEWHYxJgKn8KkktLA79TLenbkuttITM,9312
14
- persidict-0.34.1.dist-info/RECORD,,