persidict 0.34.1__py3-none-any.whl → 0.34.2__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/s3_dict.py
CHANGED
|
@@ -197,48 +197,61 @@ class S3Dict(PersiDict):
|
|
|
197
197
|
|
|
198
198
|
obj_name = self._build_full_objectname(key)
|
|
199
199
|
|
|
200
|
-
|
|
201
|
-
try:
|
|
202
|
-
head = self.s3_client.head_object(
|
|
203
|
-
Bucket=self.bucket_name, Key=obj_name)
|
|
204
|
-
s3_etag = head.get("ETag")
|
|
205
|
-
except ClientError as e:
|
|
206
|
-
if e.response['Error']['Code'] == '404':
|
|
207
|
-
raise KeyError(f"Key {key} not found in S3 bucket {self.bucket_name}")
|
|
208
|
-
else:
|
|
209
|
-
# Re-raise other client errors (e.g., permissions, throttling)
|
|
210
|
-
raise
|
|
211
|
-
|
|
200
|
+
cached_etag = None
|
|
212
201
|
etag_file_name = file_name + ".__etag__"
|
|
213
|
-
if not self.immutable_items and os.path.exists(file_name) and os.path.exists(
|
|
202
|
+
if not self.immutable_items and os.path.exists(file_name) and os.path.exists(
|
|
203
|
+
etag_file_name):
|
|
214
204
|
with open(etag_file_name, "r") as f:
|
|
215
205
|
cached_etag = f.read()
|
|
216
|
-
if cached_etag == s3_etag:
|
|
217
|
-
return self.local_cache._read_from_file(file_name)
|
|
218
|
-
|
|
219
|
-
dir_name = os.path.dirname(file_name)
|
|
220
|
-
fd, temp_path = tempfile.mkstemp(dir=dir_name, prefix=".__tmp__")
|
|
221
206
|
|
|
222
207
|
try:
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
208
|
+
get_kwargs = {'Bucket': self.bucket_name, 'Key': obj_name}
|
|
209
|
+
if cached_etag:
|
|
210
|
+
get_kwargs['IfNoneMatch'] = cached_etag
|
|
211
|
+
|
|
212
|
+
response = self.s3_client.get_object(**get_kwargs)
|
|
213
|
+
|
|
214
|
+
# 200 OK: object was downloaded, either because it's new or changed.
|
|
215
|
+
s3_etag = response.get("ETag")
|
|
216
|
+
body = response['Body']
|
|
217
|
+
|
|
218
|
+
dir_name = os.path.dirname(file_name)
|
|
219
|
+
fd, temp_path = tempfile.mkstemp(dir=dir_name, prefix=".__tmp__")
|
|
220
|
+
|
|
228
221
|
try:
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
222
|
+
with os.fdopen(fd, 'wb') as f:
|
|
223
|
+
# Stream body to file to avoid loading all in memory
|
|
224
|
+
for chunk in body.iter_chunks():
|
|
225
|
+
f.write(chunk)
|
|
226
|
+
f.flush()
|
|
227
|
+
os.fsync(f.fileno())
|
|
228
|
+
os.replace(temp_path, file_name)
|
|
229
|
+
try:
|
|
230
|
+
if os.name == 'posix':
|
|
231
|
+
dir_fd = os.open(dir_name, os.O_RDONLY)
|
|
232
|
+
try:
|
|
233
|
+
os.fsync(dir_fd)
|
|
234
|
+
finally:
|
|
235
|
+
os.close(dir_fd)
|
|
236
|
+
except OSError:
|
|
237
|
+
pass
|
|
238
|
+
except:
|
|
239
|
+
os.remove(temp_path) # Clean up temp file on failure
|
|
240
|
+
raise
|
|
241
|
+
|
|
242
|
+
self._write_etag_file(file_name, s3_etag)
|
|
240
243
|
|
|
241
|
-
|
|
244
|
+
except ClientError as e:
|
|
245
|
+
error_code = e.response.get("Error", {}).get("Code")
|
|
246
|
+
if e.response['ResponseMetadata']['HTTPStatusCode'] == 304:
|
|
247
|
+
# 304 Not Modified: our cached version is up-to-date.
|
|
248
|
+
# The file will be read from cache at the end of the function.
|
|
249
|
+
pass
|
|
250
|
+
elif e.response.get("Error", {}).get("Code") == 'NoSuchKey':
|
|
251
|
+
raise KeyError(f"Key {key} not found in S3 bucket {self.bucket_name}")
|
|
252
|
+
else:
|
|
253
|
+
# Re-raise other client errors (e.g., permissions, throttling)
|
|
254
|
+
raise
|
|
242
255
|
|
|
243
256
|
return self.local_cache._read_from_file(file_name)
|
|
244
257
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: persidict
|
|
3
|
-
Version: 0.34.
|
|
3
|
+
Version: 0.34.2
|
|
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,11 +4,11 @@ persidict/file_dir_dict.py,sha256=Dr4gdIC5uqykRgNca1pI_M_jEd_9FGjys0BvzAJR0JU,17
|
|
|
4
4
|
persidict/jokers.py,sha256=kX4bE-jKWTM2ki7JOmm_2uJS8zm8u6InZ_V12xo2ImI,1436
|
|
5
5
|
persidict/overlapping_multi_dict.py,sha256=a-lUbmY15_HrDq6jSIt8F8tJboqbeYiuRQeW4elf_oU,2663
|
|
6
6
|
persidict/persi_dict.py,sha256=SF6aWs6kCeeW-bZ9HJwx0sPX7Xav_aURqeSZ-j5quv0,14266
|
|
7
|
-
persidict/s3_dict.py,sha256=
|
|
7
|
+
persidict/s3_dict.py,sha256=j_Fb73wbkOrsneu3a48VUpDDXqVSTtEa0sRI-c_Kbjk,16104
|
|
8
8
|
persidict/safe_chars.py,sha256=HjK1MwROYy_U9ui-rhg1i3nGkj52K4OFWD-wCCcnJ7Y,536
|
|
9
9
|
persidict/safe_str_tuple.py,sha256=xyIzxlCKmvnNHkFFKWtcBREefxZ0-HLxoH_epYDt8qg,3719
|
|
10
10
|
persidict/safe_str_tuple_signing.py,sha256=5uCjAVZRqOou-KpDZw-Exboc3-3vuayJMqrrt8aZ0ck,3742
|
|
11
11
|
persidict/write_once_dict.py,sha256=-XHQhTEdvPHTKqLXK4WWW0k5cFitkzalVJC1n4BbKGo,6496
|
|
12
|
-
persidict-0.34.
|
|
13
|
-
persidict-0.34.
|
|
14
|
-
persidict-0.34.
|
|
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,,
|
|
File without changes
|