persidict 0.18.0__py3-none-any.whl → 0.20.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.

persidict/__init__.py CHANGED
@@ -26,4 +26,5 @@ from .safe_chars import get_safe_chars, replace_unsafe_chars
26
26
  from .safe_str_tuple import SafeStrTuple
27
27
  from .persi_dict import PersiDict
28
28
  from .file_dir_dict import FileDirDict
29
- from .s3_dict import S3Dict
29
+ from .s3_dict import S3Dict
30
+ from .nochange_const import *
@@ -21,6 +21,7 @@ import jsonpickle.ext.numpy as jsonpickle_numpy
21
21
  import jsonpickle.ext.pandas as jsonpickle_pandas
22
22
  import parameterizable
23
23
 
24
+ from .nochange_const import NO_CHANGE
24
25
  from .safe_chars import replace_unsafe_chars
25
26
  from .safe_str_tuple import SafeStrTuple
26
27
  from .safe_str_tuple_signing import sign_safe_str_tuple, unsign_safe_str_tuple
@@ -45,7 +46,7 @@ class FileDirDict(PersiDict):
45
46
  text files (either in jason format or as a plain text).
46
47
  """
47
48
 
48
- base_dir:str
49
+ _base_dir:str
49
50
  file_type:str
50
51
 
51
52
  def __init__(self
@@ -56,7 +57,7 @@ class FileDirDict(PersiDict):
56
57
  , base_class_for_values: Optional[type] = None):
57
58
  """A constructor defines location of the store and file format to use.
58
59
 
59
- base_dir is a directory that will contain all the files in
60
+ _base_dir is a directory that will contain all the files in
60
61
  the FileDirDict. If the directory does not exist, it will be created.
61
62
 
62
63
  base_class_for_values constraints the type of values that can be
@@ -98,14 +99,15 @@ class FileDirDict(PersiDict):
98
99
  os.mkdir(base_dir)
99
100
  assert os.path.isdir(base_dir)
100
101
 
101
- self.base_dir_param = base_dir
102
- self.base_dir = os.path.abspath(base_dir)
102
+ # self.base_dir_param = _base_dir
103
+ self._base_dir = os.path.abspath(base_dir)
104
+
103
105
 
104
106
  def __repr__(self):
105
107
  """Return repr(self)."""
106
108
 
107
109
  repr_str = super().__repr__()
108
- repr_str = repr_str[:-1] + f", base_dir={self.base_dir}"
110
+ repr_str = repr_str[:-1] + f", _base_dir={self._base_dir}"
109
111
  repr_str += f", file_type={self.file_type}"
110
112
  repr_str += " )"
111
113
 
@@ -116,15 +118,22 @@ class FileDirDict(PersiDict):
116
118
  """Return configuration parameters of the dictionary."""
117
119
  params = super().get_params()
118
120
  additional_params = dict(
119
- base_dir=self.base_dir_param
121
+ base_dir=self.base_dir
120
122
  , file_type=self.file_type)
121
123
  params.update(additional_params)
122
124
  return params
123
125
 
126
+
124
127
  @property
125
128
  def base_url(self) -> str:
126
129
  """Return dictionary's URL"""
127
- return f"file://{self.base_dir}"
130
+ return f"file://{self._base_dir}"
131
+
132
+
133
+ @property
134
+ def base_dir(self) -> str:
135
+ """Return dictionary's base directory"""
136
+ return self._base_dir
128
137
 
129
138
 
130
139
  def __len__(self) -> int:
@@ -132,7 +141,7 @@ class FileDirDict(PersiDict):
132
141
 
133
142
  num_files = 0
134
143
  suffix = "." + self.file_type
135
- for subdir_info in os.walk(self.base_dir):
144
+ for subdir_info in os.walk(self._base_dir):
136
145
  files = subdir_info[2]
137
146
  files = [f_name for f_name in files
138
147
  if f_name.endswith(suffix)]
@@ -146,13 +155,13 @@ class FileDirDict(PersiDict):
146
155
  if self.immutable_items:
147
156
  raise KeyError("Can't clear a dict that contains immutable items")
148
157
 
149
- for subdir_info in os.walk(self.base_dir, topdown=False):
158
+ for subdir_info in os.walk(self._base_dir, topdown=False):
150
159
  (subdir_name, _, files) = subdir_info
151
160
  suffix = "." + self.file_type
152
161
  for f in files:
153
162
  if f.endswith(suffix):
154
163
  os.remove(os.path.join(subdir_name, f))
155
- if (subdir_name != self.base_dir) and (
164
+ if (subdir_name != self._base_dir) and (
156
165
  len(os.listdir(subdir_name)) == 0 ):
157
166
  os.rmdir(subdir_name)
158
167
 
@@ -163,7 +172,7 @@ class FileDirDict(PersiDict):
163
172
  """Convert a key into a filesystem path."""
164
173
 
165
174
  key = sign_safe_str_tuple(key, self.digest_len)
166
- key = [self.base_dir] + list(key.strings)
175
+ key = [self._base_dir] + list(key.strings)
167
176
  dir_names = key[:-1] if is_file_path else key
168
177
 
169
178
  if create_subdirs:
@@ -290,6 +299,9 @@ class FileDirDict(PersiDict):
290
299
  def __setitem__(self, key:PersiDictKey, value:Any):
291
300
  """Set self[key] to value."""
292
301
 
302
+ if value is NO_CHANGE:
303
+ return
304
+
293
305
  if isinstance(value, PersiDict):
294
306
  raise TypeError(
295
307
  f"You are not allowed to store a PersiDict "
@@ -319,7 +331,7 @@ class FileDirDict(PersiDict):
319
331
  def _generic_iter(self, iter_type: str):
320
332
  """Underlying implementation for .items()/.keys()/.values() iterators"""
321
333
  assert iter_type in {"keys", "values", "items"}
322
- walk_results = os.walk(self.base_dir)
334
+ walk_results = os.walk(self._base_dir)
323
335
  ext_len = len(self.file_type) + 1
324
336
 
325
337
  def splitter(dir_path: str):
@@ -341,7 +353,7 @@ class FileDirDict(PersiDict):
341
353
  for f in files:
342
354
  if f.endswith(suffix):
343
355
  prefix_key = os.path.relpath(
344
- dir_name, start=self.base_dir)
356
+ dir_name, start=self._base_dir)
345
357
 
346
358
  result_key = (*splitter(prefix_key), f[:-ext_len])
347
359
  result_key = SafeStrTuple(result_key)
@@ -0,0 +1,17 @@
1
+ """A singleton constant to indicate no change in a value.
2
+
3
+ When updating a val ue in a persistent dictionary,
4
+ use NO_CHANGE as the new value to indicate that
5
+ the existing value should remain unchanged.
6
+ """
7
+
8
+ class NoChange_Class:
9
+ _instance = None
10
+
11
+ def __new__(cls):
12
+ if cls._instance is None:
13
+ cls._instance = super().__new__(cls)
14
+ return cls._instance
15
+
16
+ NoChange = NoChange_Class()
17
+ NO_CHANGE = NoChange_Class()
persidict/persi_dict.py CHANGED
@@ -26,6 +26,7 @@ from parameterizable import ParameterizableClass
26
26
  from typing import Any, Sequence, Optional
27
27
  from collections.abc import MutableMapping
28
28
 
29
+ from .nochange_const import NO_CHANGE
29
30
  from .safe_str_tuple import SafeStrTuple
30
31
 
31
32
  PersiDictKey = SafeStrTuple | Sequence[str] | str
@@ -93,6 +94,7 @@ class PersiDict(MutableMapping, ParameterizableClass):
93
94
  self.immutable_items = bool(immutable_items)
94
95
  self.base_class_for_values = base_class_for_values
95
96
 
97
+
96
98
  def get_params(self):
97
99
  """Return a dictionary of parameters for the PersiDict object."""
98
100
  params = dict(
@@ -102,6 +104,7 @@ class PersiDict(MutableMapping, ParameterizableClass):
102
104
  )
103
105
  return params
104
106
 
107
+
105
108
  @property
106
109
  @abstractmethod
107
110
  def base_url(self):
@@ -109,6 +112,13 @@ class PersiDict(MutableMapping, ParameterizableClass):
109
112
  raise NotImplementedError
110
113
 
111
114
 
115
+ @property
116
+ @abstractmethod
117
+ def base_dir(self):
118
+ """Return dictionary's base directory in the local filesystem"""
119
+ raise NotImplementedError
120
+
121
+
112
122
  def __repr__(self) -> str:
113
123
  """Return repr(self)"""
114
124
  repr_str = self.__class__.__name__ + "("
@@ -139,6 +149,8 @@ class PersiDict(MutableMapping, ParameterizableClass):
139
149
 
140
150
  def __setitem__(self, key:PersiDictKey, value:Any):
141
151
  """Set self[key] to value."""
152
+ if value is NO_CHANGE:
153
+ return
142
154
  if self.immutable_items:
143
155
  if key in self:
144
156
  raise KeyError("Can't modify an immutable key-value pair")
@@ -192,6 +204,7 @@ class PersiDict(MutableMapping, ParameterizableClass):
192
204
  """
193
205
  # TODO: check edge cases to ensure the same semantics as standard dicts
194
206
  key = SafeStrTuple(key)
207
+ assert not default is NO_CHANGE
195
208
  if key in self:
196
209
  return self[key]
197
210
  else:
persidict/s3_dict.py CHANGED
@@ -10,6 +10,7 @@ import parameterizable
10
10
  from .safe_str_tuple import SafeStrTuple
11
11
  from .safe_str_tuple_signing import sign_safe_str_tuple, unsign_safe_str_tuple
12
12
  from .persi_dict import PersiDict
13
+ from .nochange_const import NO_CHANGE
13
14
  from .file_dir_dict import FileDirDict, PersiDictKey
14
15
 
15
16
  S3DICT_DEFAULT_BASE_DIR = "__s3_dict__"
@@ -33,7 +34,7 @@ class S3Dict(PersiDict):
33
34
  bucket_name: str
34
35
  root_prefix: str
35
36
  file_type: str
36
- base_dir: str
37
+ _base_dir: str
37
38
 
38
39
  def __init__(self, bucket_name:str = "my_bucket"
39
40
  , region:str = None
@@ -52,7 +53,7 @@ class S3Dict(PersiDict):
52
53
 
53
54
  root_prefix is a common S3 prefix for all objectnames in a dictionary.
54
55
 
55
- base_dir is a local directory that will be used to store tmp files.
56
+ _base_dir is a local directory that will be used to store tmp files.
56
57
 
57
58
  base_class_for_values constraints the type of values that can be
58
59
  stored in the dictionary. If specified, it will be used to
@@ -99,7 +100,7 @@ class S3Dict(PersiDict):
99
100
  """Return repr(self)."""
100
101
 
101
102
  repr_str = super().__repr__()
102
- repr_str = repr_str[:-1] + f", base_dir={self.local_cache.base_dir}"
103
+ repr_str = repr_str[:-1] + f", _base_dir={self.local_cache._base_dir}"
103
104
  repr_str += f", file_type={self.file_type}"
104
105
  repr_str += f", region={self.region}"
105
106
  repr_str += f", bucket_name={self.bucket_name}"
@@ -124,6 +125,11 @@ class S3Dict(PersiDict):
124
125
  return f"s3://{self.bucket_name}/{self.root_prefix}"
125
126
 
126
127
 
128
+ @property
129
+ def base_dir(self) -> str:
130
+ """Return dictionary's base directory in the local filesystem"""
131
+ return self.local_cache.base_dir
132
+
127
133
 
128
134
  def _build_full_objectname(self, key:PersiDictKey) -> str:
129
135
  """ Convert PersiDictKey into an S3 objectname. """
@@ -174,6 +180,9 @@ class S3Dict(PersiDict):
174
180
  def __setitem__(self, key:PersiDictKey, value:Any):
175
181
  """Set self[key] to value. """
176
182
 
183
+ if value is NO_CHANGE:
184
+ return
185
+
177
186
  if isinstance(value, PersiDict):
178
187
  raise TypeError(
179
188
  f"You are not allowed to store a PersiDict "
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: persidict
3
- Version: 0.18.0
3
+ Version: 0.20.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
@@ -155,12 +155,13 @@ that simultaneously work with the same instance of a dictionary.
155
155
  * Values must be pickleable Python objects.
156
156
  * You can constrain values to be an instance of a specific class.
157
157
  * Insertion order is not preserved.
158
- * You can not assign initial key-value pairs to a dictionary in its constructor.
158
+ * You cannot assign initial key-value pairs to a dictionary in its constructor.
159
159
  * `PersiDict` API has additional methods `delete_if_exists()`, `timestamp()`,
160
160
  `get_subdict()`, `subdicts()`, `random_keys()`, `newest_keys()`,
161
161
  `oldest_keys()`, `newest_values()`, `oldest_values()`,
162
162
  `get_params()`, `get_metaparams()`, and `get_default_metaparams()`,
163
163
  which are not available in native Python dicts.
164
+ * You can use NO_CHANGE constant to avoid actually setting/updating a value.
164
165
 
165
166
  ## Fine Tuning
166
167
 
@@ -0,0 +1,12 @@
1
+ persidict/.DS_Store,sha256=d65165279105ca6773180500688df4bdc69a2c7b771752f0a46ef120b7fd8ec3,6148
2
+ persidict/__init__.py,sha256=34e758e6e566fc18225214b430cf492938d6f3d48b36e678ab947f15c780a699,1185
3
+ persidict/file_dir_dict.py,sha256=438558ea1f06a8e280609a4aaa94a6fea05d82ce2ef79bd401af94b014da82a6,14132
4
+ persidict/nochange_const.py,sha256=d569d9283a684150e380b08ebdeb232783dd0e51938bfbbadf27327952027f3e,443
5
+ persidict/persi_dict.py,sha256=7301a8a6a41bb4c2fbd29d31e0421287d53f6a88c66e9324656db1043e5e2b55,11728
6
+ persidict/s3_dict.py,sha256=acc676f92ae993364f1fa70d74e95c182b2f263f29eae99f3b554f45aededfe5,11834
7
+ persidict/safe_chars.py,sha256=59a20e96205d2e5675d827a911ad42ddbd553f1bd7e2cda1be765a9c2c4ce814,565
8
+ persidict/safe_str_tuple.py,sha256=71393904bdebfb213ad8429fed59e04da52964076c01324f2238821aa4339325,3717
9
+ persidict/safe_str_tuple_signing.py,sha256=e6e0a3015651a8ea2ef8aa43670f84c5ba1cdfedefb9ac8932aaebb7c699d1c9,3742
10
+ persidict-0.20.0.dist-info/WHEEL,sha256=c133ef911c90b05f7e14d8679ba99146f9154fcd271b7398cf8f672283b94e05,79
11
+ persidict-0.20.0.dist-info/METADATA,sha256=f8d494dd74e77144e57c8db653c0b7da471c3d71e313804caee9ad6ae78f3855,9172
12
+ persidict-0.20.0.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- persidict/.DS_Store,sha256=d65165279105ca6773180500688df4bdc69a2c7b771752f0a46ef120b7fd8ec3,6148
2
- persidict/__init__.py,sha256=f589b6292bef0ee7c6f6ef087dacbecc5943a64deed8839b02f8342db3032b25,1155
3
- persidict/file_dir_dict.py,sha256=b978200dd48fa77a7b15bc05327d5aa773cc09974e138a581da0a784d0448903,13906
4
- persidict/persi_dict.py,sha256=cea37db3dcd209e5a4ab382e668fad67ac97b56ef829e8466b60eccc8f771eb6,11431
5
- persidict/s3_dict.py,sha256=93d8a7945c565adf8aa513568a4dde3b06772e2366acbdfc3402c063aade17ef,11581
6
- persidict/safe_chars.py,sha256=59a20e96205d2e5675d827a911ad42ddbd553f1bd7e2cda1be765a9c2c4ce814,565
7
- persidict/safe_str_tuple.py,sha256=71393904bdebfb213ad8429fed59e04da52964076c01324f2238821aa4339325,3717
8
- persidict/safe_str_tuple_signing.py,sha256=e6e0a3015651a8ea2ef8aa43670f84c5ba1cdfedefb9ac8932aaebb7c699d1c9,3742
9
- persidict-0.18.0.dist-info/WHEEL,sha256=c133ef911c90b05f7e14d8679ba99146f9154fcd271b7398cf8f672283b94e05,79
10
- persidict-0.18.0.dist-info/METADATA,sha256=3567ed443b76bb83e481945ef1bfd6b20647db3f178d81022b6234d574242aa9,9096
11
- persidict-0.18.0.dist-info/RECORD,,