rclone-api 1.3.18__py2.py3-none-any.whl → 1.3.20__py2.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.
- rclone_api/__init__.py +4 -0
- rclone_api/db/db.py +277 -277
- rclone_api/db/models.py +57 -57
- rclone_api/file.py +1 -0
- rclone_api/http_server.py +233 -0
- rclone_api/mount_read_chunker.py +2 -1
- rclone_api/profile/mount_copy_bytes.py +1 -1
- rclone_api/rclone.py +101 -18
- rclone_api/remote.py +3 -0
- rclone_api/s3/chunk_task.py +3 -1
- rclone_api/s3/upload_file_multipart.py +32 -20
- rclone_api/types.py +36 -8
- {rclone_api-1.3.18.dist-info → rclone_api-1.3.20.dist-info}/METADATA +1 -1
- {rclone_api-1.3.18.dist-info → rclone_api-1.3.20.dist-info}/RECORD +18 -17
- {rclone_api-1.3.18.dist-info → rclone_api-1.3.20.dist-info}/LICENSE +0 -0
- {rclone_api-1.3.18.dist-info → rclone_api-1.3.20.dist-info}/WHEEL +0 -0
- {rclone_api-1.3.18.dist-info → rclone_api-1.3.20.dist-info}/entry_points.txt +0 -0
- {rclone_api-1.3.18.dist-info → rclone_api-1.3.20.dist-info}/top_level.txt +0 -0
|
@@ -25,31 +25,39 @@ _MIN_UPLOAD_CHUNK_SIZE = 5 * 1024 * 1024 # 5MB
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
def upload_task(
|
|
28
|
-
info: UploadInfo, chunk:
|
|
28
|
+
info: UploadInfo, chunk: FilePart, part_number: int, retries: int
|
|
29
29
|
) -> FinishedPiece:
|
|
30
|
-
|
|
30
|
+
file_or_err: Path | Exception = chunk.get_file()
|
|
31
|
+
if isinstance(file_or_err, Exception):
|
|
32
|
+
raise file_or_err
|
|
33
|
+
file: Path = file_or_err
|
|
34
|
+
size = os.path.getsize(file)
|
|
31
35
|
retries = retries + 1 # Add one for the initial attempt
|
|
32
36
|
for retry in range(retries):
|
|
33
37
|
try:
|
|
34
38
|
if retry > 0:
|
|
35
39
|
locked_print(f"Retrying part {part_number} for {info.src_file_path}")
|
|
36
40
|
locked_print(
|
|
37
|
-
f"Uploading part {part_number} for {info.src_file_path} of size {
|
|
38
|
-
)
|
|
39
|
-
part = info.s3_client.upload_part(
|
|
40
|
-
Bucket=info.bucket_name,
|
|
41
|
-
Key=info.object_name,
|
|
42
|
-
PartNumber=part_number,
|
|
43
|
-
UploadId=info.upload_id,
|
|
44
|
-
Body=chunk,
|
|
45
|
-
)
|
|
46
|
-
out: FinishedPiece = FinishedPiece(
|
|
47
|
-
etag=part["ETag"], part_number=part_number
|
|
41
|
+
f"Uploading part {part_number} for {info.src_file_path} of size {size}"
|
|
48
42
|
)
|
|
43
|
+
|
|
44
|
+
with open(file, "rb") as f:
|
|
45
|
+
part = info.s3_client.upload_part(
|
|
46
|
+
Bucket=info.bucket_name,
|
|
47
|
+
Key=info.object_name,
|
|
48
|
+
PartNumber=part_number,
|
|
49
|
+
UploadId=info.upload_id,
|
|
50
|
+
Body=f,
|
|
51
|
+
)
|
|
52
|
+
out: FinishedPiece = FinishedPiece(
|
|
53
|
+
etag=part["ETag"], part_number=part_number
|
|
54
|
+
)
|
|
55
|
+
chunk.dispose()
|
|
49
56
|
return out
|
|
50
57
|
except Exception as e:
|
|
51
58
|
if retry == retries - 1:
|
|
52
59
|
locked_print(f"Error uploading part {part_number}: {e}")
|
|
60
|
+
chunk.dispose()
|
|
53
61
|
raise e
|
|
54
62
|
else:
|
|
55
63
|
locked_print(f"Error uploading part {part_number}: {e}, retrying")
|
|
@@ -61,25 +69,29 @@ def handle_upload(
|
|
|
61
69
|
upload_info: UploadInfo, fp: FilePart | EndOfStream
|
|
62
70
|
) -> FinishedPiece | Exception | EndOfStream:
|
|
63
71
|
if isinstance(fp, EndOfStream):
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
part_number = extra.part_number
|
|
68
|
-
print(f"Handling upload for {part_number}, size {fp.size}")
|
|
72
|
+
eos: EndOfStream = fp
|
|
73
|
+
return eos
|
|
74
|
+
part_number: int | None = None
|
|
69
75
|
try:
|
|
76
|
+
assert isinstance(fp.extra, S3FileInfo)
|
|
77
|
+
extra: S3FileInfo = fp.extra
|
|
78
|
+
part_number = extra.part_number
|
|
79
|
+
print(f"Handling upload for {part_number}, size {fp.size}")
|
|
80
|
+
|
|
70
81
|
part: FinishedPiece = upload_task(
|
|
71
82
|
info=upload_info,
|
|
72
|
-
chunk=fp
|
|
83
|
+
chunk=fp,
|
|
73
84
|
part_number=part_number,
|
|
74
85
|
retries=upload_info.retries,
|
|
75
86
|
)
|
|
76
|
-
fp.close()
|
|
77
87
|
return part
|
|
78
88
|
except Exception as e:
|
|
79
89
|
stacktrace = traceback.format_exc()
|
|
80
90
|
msg = f"Error uploading part {part_number}: {e}\n{stacktrace}"
|
|
81
91
|
warnings.warn(msg)
|
|
82
92
|
return e
|
|
93
|
+
finally:
|
|
94
|
+
fp.dispose()
|
|
83
95
|
|
|
84
96
|
|
|
85
97
|
def prepare_upload_file_multipart(
|
rclone_api/types.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import atexit
|
|
1
2
|
import os
|
|
2
3
|
import re
|
|
3
4
|
import time
|
|
@@ -267,8 +268,28 @@ class EndOfStream:
|
|
|
267
268
|
pass
|
|
268
269
|
|
|
269
270
|
|
|
271
|
+
_CLEANUP_LIST: list[Path] = []
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _add_for_cleanup(path: Path) -> None:
|
|
275
|
+
_CLEANUP_LIST.append(path)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def _on_exit_cleanup() -> None:
|
|
279
|
+
paths = list(_CLEANUP_LIST)
|
|
280
|
+
for path in paths:
|
|
281
|
+
try:
|
|
282
|
+
if path.exists():
|
|
283
|
+
path.unlink()
|
|
284
|
+
except Exception as e:
|
|
285
|
+
warnings.warn(f"Cannot cleanup {path}: {e}")
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
atexit.register(_on_exit_cleanup)
|
|
289
|
+
|
|
290
|
+
|
|
270
291
|
class FilePart:
|
|
271
|
-
def __init__(self, payload: bytes | Exception, extra: Any) -> None:
|
|
292
|
+
def __init__(self, payload: Path | bytes | Exception, extra: Any) -> None:
|
|
272
293
|
from rclone_api.util import random_str
|
|
273
294
|
|
|
274
295
|
self.extra = extra
|
|
@@ -277,11 +298,18 @@ class FilePart:
|
|
|
277
298
|
if isinstance(payload, Exception):
|
|
278
299
|
self.payload = payload
|
|
279
300
|
return
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
self.payload.parent.
|
|
284
|
-
|
|
301
|
+
if isinstance(payload, bytes):
|
|
302
|
+
self.payload = get_chunk_tmpdir() / f"{random_str(12)}.chunk"
|
|
303
|
+
with _TMP_DIR_ACCESS_LOCK:
|
|
304
|
+
if not self.payload.parent.exists():
|
|
305
|
+
self.payload.parent.mkdir(parents=True, exist_ok=True)
|
|
306
|
+
self.payload.write_bytes(payload)
|
|
307
|
+
_add_for_cleanup(self.payload)
|
|
308
|
+
if isinstance(payload, Path):
|
|
309
|
+
self.payload = payload
|
|
310
|
+
|
|
311
|
+
def get_file(self) -> Path | Exception:
|
|
312
|
+
return self.payload
|
|
285
313
|
|
|
286
314
|
@property
|
|
287
315
|
def size(self) -> int:
|
|
@@ -315,7 +343,7 @@ class FilePart:
|
|
|
315
343
|
def is_error(self) -> bool:
|
|
316
344
|
return isinstance(self.payload, Exception)
|
|
317
345
|
|
|
318
|
-
def
|
|
346
|
+
def dispose(self) -> None:
|
|
319
347
|
with self._lock:
|
|
320
348
|
if isinstance(self.payload, Exception):
|
|
321
349
|
warnings.warn(
|
|
@@ -330,4 +358,4 @@ class FilePart:
|
|
|
330
358
|
warnings.warn(f"Cannot close file part because of error: {e}")
|
|
331
359
|
|
|
332
360
|
def __del__(self):
|
|
333
|
-
self.
|
|
361
|
+
self.dispose()
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
rclone_api/__init__.py,sha256=
|
|
1
|
+
rclone_api/__init__.py,sha256=gOQJgOs0_oaV_pOwlY00LXRYAHk1_MDwN59od1VpoC0,1334
|
|
2
2
|
rclone_api/cli.py,sha256=dibfAZIh0kXWsBbfp3onKLjyZXo54mTzDjUdzJlDlWo,231
|
|
3
3
|
rclone_api/completed_process.py,sha256=_IZ8IWK7DM1_tsbDEkH6wPZ-bbcrgf7A7smls854pmg,1775
|
|
4
4
|
rclone_api/config.py,sha256=f6jEAxVorGFr31oHfcsu5AJTtOJj2wR5tTSsbGGZuIw,2558
|
|
@@ -8,19 +8,20 @@ rclone_api/diff.py,sha256=tMoJMAGmLSE6Q_7QhPf6PnCzb840djxMZtDmhc2GlGQ,5227
|
|
|
8
8
|
rclone_api/dir.py,sha256=i4h7LX5hB_WmVixxDRWL_l1nifvscrdWct_8Wx7wHZc,3540
|
|
9
9
|
rclone_api/dir_listing.py,sha256=GoziW8Sne6FY90MLNcb2aO3aaa3jphB6H8ExYrV0Ryo,1882
|
|
10
10
|
rclone_api/exec.py,sha256=Bq0gkyZ10mEY0FRyzNZgdN4FaWP9vpeCk1kjpg-gN_8,1083
|
|
11
|
-
rclone_api/file.py,sha256=
|
|
11
|
+
rclone_api/file.py,sha256=cz-7_nJArkVdJ9z2QaC_XZYpihXe3IPBC90Z5_3g2aw,5419
|
|
12
12
|
rclone_api/file_item.py,sha256=cH-AQYsxedhNPp4c8NHY1ad4Z7St4yf_VGbmiGD59no,1770
|
|
13
13
|
rclone_api/filelist.py,sha256=xbiusvNgaB_b_kQOZoHMJJxn6TWGtPrWd2J042BI28o,767
|
|
14
14
|
rclone_api/group_files.py,sha256=H92xPW9lQnbNw5KbtZCl00bD6iRh9yRbCuxku4j_3dg,8036
|
|
15
|
+
rclone_api/http_server.py,sha256=SmeUDDKaMpJGDqRNkHoImHTRNkvHEtGFzu_8jYfoeZU,8113
|
|
15
16
|
rclone_api/log.py,sha256=VZHM7pNSXip2ZLBKMP7M1u-rp_F7zoafFDuR8CPUoKI,1271
|
|
16
17
|
rclone_api/mount.py,sha256=TE_VIBMW7J1UkF_6HRCt8oi_jGdMov4S51bm2OgxFAM,10045
|
|
17
|
-
rclone_api/mount_read_chunker.py,sha256=
|
|
18
|
+
rclone_api/mount_read_chunker.py,sha256=7jaF1Rsjr-kXIZW--Ol1QuG7WArBgdIcpQ0AJMYn7bI,4764
|
|
18
19
|
rclone_api/process.py,sha256=BGXJTZVT__jeaDyjN8_kRycliOhkBErMPdHO1hKRvJE,5271
|
|
19
|
-
rclone_api/rclone.py,sha256=
|
|
20
|
-
rclone_api/remote.py,sha256=
|
|
20
|
+
rclone_api/rclone.py,sha256=lLUHeG11brzhODCTlOhjy6rqcJeC_LhQBGTBcRTkaw8,54099
|
|
21
|
+
rclone_api/remote.py,sha256=jq3dPbAGvYZFW5cTviqxT2w6_jG2LLfS1RIcYSmMsQQ,503
|
|
21
22
|
rclone_api/rpath.py,sha256=8ZA_1wxWtskwcy0I8V2VbjKDmzPkiWd8Q2JQSvh-sYE,2586
|
|
22
23
|
rclone_api/scan_missing_folders.py,sha256=Kulca2Q6WZodt00ATFHkmqqInuoPvBkhTcS9703y6po,4740
|
|
23
|
-
rclone_api/types.py,sha256=
|
|
24
|
+
rclone_api/types.py,sha256=aj3usJrIDKC4MKuObBo9WjlR1isyJColdOQWXo3Repo,10608
|
|
24
25
|
rclone_api/util.py,sha256=F9Q3zbWRsgPF4NG6OWB63cZ7GVq82lsraP47gmmDohU,5416
|
|
25
26
|
rclone_api/walk.py,sha256=-54NVE8EJcCstwDoaC_UtHm73R2HrZwVwQmsnv55xNU,3369
|
|
26
27
|
rclone_api/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
|
|
@@ -29,21 +30,21 @@ rclone_api/cmd/copy_large_s3.py,sha256=nOpAUAQN1mJnf4EIZCh4OVCW7Q4_EXJeLFVe6r_9r
|
|
|
29
30
|
rclone_api/cmd/list_files.py,sha256=x8FHODEilwKqwdiU1jdkeJbLwOqUkUQuDWPo2u_zpf0,741
|
|
30
31
|
rclone_api/cmd/save_to_db.py,sha256=ylvnhg_yzexM-m6Zr7XDiswvoDVSl56ELuFAdb9gqBY,1957
|
|
31
32
|
rclone_api/db/__init__.py,sha256=OSRUdnSWUlDTOHmjdjVmxYTUNpTbtaJ5Ll9sl-PfZg0,40
|
|
32
|
-
rclone_api/db/db.py,sha256=
|
|
33
|
-
rclone_api/db/models.py,sha256=
|
|
33
|
+
rclone_api/db/db.py,sha256=ZpYfeCUe8MKg_fdJucRSe6-fwGY_rWqUn7WkHCNFH_4,10074
|
|
34
|
+
rclone_api/db/models.py,sha256=v7qaXUehvsDvU51uk69JI23fSIs9JFGcOa-Tv1c_wVs,1600
|
|
34
35
|
rclone_api/experimental/flags.py,sha256=qCVD--fSTmzlk9hloRLr0q9elzAOFzPsvVpKM3aB1Mk,2739
|
|
35
36
|
rclone_api/experimental/flags_base.py,sha256=ajU_czkTcAxXYU-SlmiCfHY7aCQGHvpCLqJ-Z8uZLk0,2102
|
|
36
|
-
rclone_api/profile/mount_copy_bytes.py,sha256=
|
|
37
|
+
rclone_api/profile/mount_copy_bytes.py,sha256=M1vZn-Mrga14Ik7MHGZHbnwYli41Ep6Tyll7hQc7Wmo,9071
|
|
37
38
|
rclone_api/s3/api.py,sha256=PafsIEyWDpLWAXsZAjFm9CY14vJpsDr9lOsn0kGRLZ0,4009
|
|
38
39
|
rclone_api/s3/basic_ops.py,sha256=hK3366xhVEzEcjz9Gk_8lFx6MRceAk72cax6mUrr6ko,2104
|
|
39
|
-
rclone_api/s3/chunk_task.py,sha256=
|
|
40
|
+
rclone_api/s3/chunk_task.py,sha256=kA6_5fLNdtT3QdTFrfBY6y8sH9Og8nM2mrjgAz_g1Rc,7196
|
|
40
41
|
rclone_api/s3/chunk_types.py,sha256=oSWv8No9V3BeM7IcGnowyR2a7YrszdAXzEJlxaeZcp0,8852
|
|
41
42
|
rclone_api/s3/create.py,sha256=wgfkapv_j904CfKuWyiBIWJVxfAx_ftemFSUV14aT68,3149
|
|
42
43
|
rclone_api/s3/types.py,sha256=Elmh__gvZJyJyElYwMmvYZIBIunDJiTRAbEg21GmsRU,1604
|
|
43
|
-
rclone_api/s3/upload_file_multipart.py,sha256=
|
|
44
|
-
rclone_api-1.3.
|
|
45
|
-
rclone_api-1.3.
|
|
46
|
-
rclone_api-1.3.
|
|
47
|
-
rclone_api-1.3.
|
|
48
|
-
rclone_api-1.3.
|
|
49
|
-
rclone_api-1.3.
|
|
44
|
+
rclone_api/s3/upload_file_multipart.py,sha256=UlrUl8fB0oK8_r0w8ZwH79jlOCHQrMOKWZeNCmHrT7M,12052
|
|
45
|
+
rclone_api-1.3.20.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
|
|
46
|
+
rclone_api-1.3.20.dist-info/METADATA,sha256=RFYBoioCW6Gs8lnUrpIJKl02vRFXTYdOD3YCIt7zJXw,4598
|
|
47
|
+
rclone_api-1.3.20.dist-info/WHEEL,sha256=rF4EZyR2XVS6irmOHQIJx2SUqXLZKRMUrjsg8UwN-XQ,109
|
|
48
|
+
rclone_api-1.3.20.dist-info/entry_points.txt,sha256=fJteOlYVwgX3UbNuL9jJ0zUTuX2O79JFAeNgK7Sw7EQ,255
|
|
49
|
+
rclone_api-1.3.20.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
|
|
50
|
+
rclone_api-1.3.20.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|