rclone-api 1.0.90__py2.py3-none-any.whl → 1.0.93__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/rclone.py +24 -4
- rclone_api/s3/api.py +43 -21
- rclone_api/s3/types.py +3 -2
- rclone_api/util.py +7 -0
- {rclone_api-1.0.90.dist-info → rclone_api-1.0.93.dist-info}/METADATA +1 -1
- {rclone_api-1.0.90.dist-info → rclone_api-1.0.93.dist-info}/RECORD +10 -10
- {rclone_api-1.0.90.dist-info → rclone_api-1.0.93.dist-info}/LICENSE +0 -0
- {rclone_api-1.0.90.dist-info → rclone_api-1.0.93.dist-info}/WHEEL +0 -0
- {rclone_api-1.0.90.dist-info → rclone_api-1.0.93.dist-info}/entry_points.txt +0 -0
- {rclone_api-1.0.90.dist-info → rclone_api-1.0.93.dist-info}/top_level.txt +0 -0
rclone_api/rclone.py
CHANGED
|
@@ -679,11 +679,12 @@ class Rclone:
|
|
|
679
679
|
concurrent_chunks: int = 4, # This setting will scale the performance of the upload
|
|
680
680
|
retries: int = 3,
|
|
681
681
|
max_chunks_before_suspension: int | None = None,
|
|
682
|
+
mount_path: Path | None = None,
|
|
682
683
|
) -> MultiUploadResult:
|
|
683
684
|
"""For massive files that rclone can't handle in one go, this function will copy the file in chunks to an S3 store"""
|
|
684
685
|
from rclone_api.s3.api import S3Client
|
|
685
686
|
from rclone_api.s3.create import S3Credentials
|
|
686
|
-
from rclone_api.util import S3PathInfo, split_s3_path
|
|
687
|
+
from rclone_api.util import S3PathInfo, random_str, split_s3_path
|
|
687
688
|
|
|
688
689
|
other_args: list[str] = [
|
|
689
690
|
"--no-modtime",
|
|
@@ -699,7 +700,7 @@ class Rclone:
|
|
|
699
700
|
str(concurrent_chunks),
|
|
700
701
|
"--vfs-fast-fingerprint",
|
|
701
702
|
]
|
|
702
|
-
mount_path = Path("
|
|
703
|
+
mount_path = mount_path or Path("tmp_mnts") / random_str(12)
|
|
703
704
|
src_path = Path(src)
|
|
704
705
|
name = src_path.name
|
|
705
706
|
|
|
@@ -784,8 +785,7 @@ class Rclone:
|
|
|
784
785
|
)
|
|
785
786
|
|
|
786
787
|
out: MultiUploadResult = client.upload_file_multipart(
|
|
787
|
-
upload_target=upload_target,
|
|
788
|
-
upload_config=upload_config
|
|
788
|
+
upload_target=upload_target, upload_config=upload_config
|
|
789
789
|
)
|
|
790
790
|
return out
|
|
791
791
|
|
|
@@ -877,6 +877,7 @@ class Rclone:
|
|
|
877
877
|
other_args: list[str] | None = None,
|
|
878
878
|
) -> Generator[Process, None, None]:
|
|
879
879
|
"""Like mount, but can be used in a context manager."""
|
|
880
|
+
error_happened = False
|
|
880
881
|
proc = self.mount(
|
|
881
882
|
src,
|
|
882
883
|
outdir,
|
|
@@ -888,6 +889,7 @@ class Rclone:
|
|
|
888
889
|
try:
|
|
889
890
|
yield proc
|
|
890
891
|
except Exception as e:
|
|
892
|
+
error_happened = True
|
|
891
893
|
stack_trace = traceback.format_exc()
|
|
892
894
|
warnings.warn(f"Error in scoped_mount: {e}\n\nStack Trace:\n{stack_trace}")
|
|
893
895
|
raise
|
|
@@ -895,6 +897,24 @@ class Rclone:
|
|
|
895
897
|
if proc.poll() is None:
|
|
896
898
|
proc.terminate()
|
|
897
899
|
proc.wait()
|
|
900
|
+
if not error_happened and outdir.exists():
|
|
901
|
+
time.sleep(2)
|
|
902
|
+
if outdir.exists():
|
|
903
|
+
print(f"{outdir} mount still exists, attempting to remove")
|
|
904
|
+
if not _IS_WINDOWS:
|
|
905
|
+
# attempt
|
|
906
|
+
os.system(f"fusermount -u {outdir}")
|
|
907
|
+
os.system(f"umount {outdir}")
|
|
908
|
+
time.sleep(2)
|
|
909
|
+
if outdir.exists():
|
|
910
|
+
is_empty = not list(outdir.iterdir())
|
|
911
|
+
if not is_empty:
|
|
912
|
+
warnings.warn(f"Failed to unmount {outdir}")
|
|
913
|
+
else:
|
|
914
|
+
try:
|
|
915
|
+
outdir.rmdir()
|
|
916
|
+
except Exception as e:
|
|
917
|
+
warnings.warn(f"Failed to remove {outdir}: {e}")
|
|
898
918
|
|
|
899
919
|
@deprecated("mount")
|
|
900
920
|
def mount_webdav(
|
rclone_api/s3/api.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import json
|
|
1
2
|
import warnings
|
|
2
|
-
from pathlib import Path
|
|
3
3
|
|
|
4
4
|
from botocore.client import BaseClient
|
|
5
5
|
|
|
@@ -46,28 +46,50 @@ class S3Client:
|
|
|
46
46
|
upload_target: S3UploadTarget,
|
|
47
47
|
upload_config: S3MutliPartUploadConfig,
|
|
48
48
|
) -> MultiUploadResult:
|
|
49
|
-
|
|
50
|
-
if filesize < _MIN_THRESHOLD_FOR_CHUNKING:
|
|
51
|
-
warnings.warn(
|
|
52
|
-
f"File size {filesize} is less than the minimum threshold for chunking ({_MIN_THRESHOLD_FOR_CHUNKING}), switching to single threaded upload."
|
|
53
|
-
)
|
|
54
|
-
err = self.upload_file(upload_target)
|
|
55
|
-
if err:
|
|
56
|
-
raise err
|
|
57
|
-
return MultiUploadResult.UPLOADED_FRESH
|
|
49
|
+
|
|
58
50
|
chunk_size = upload_config.chunk_size
|
|
59
51
|
retries = upload_config.retries
|
|
60
52
|
resume_path_json = upload_config.resume_path_json
|
|
61
53
|
max_chunks_before_suspension = upload_config.max_chunks_before_suspension
|
|
62
54
|
bucket_name = upload_target.bucket_name
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
filesize = upload_target.src_file.stat().st_size
|
|
58
|
+
if filesize < _MIN_THRESHOLD_FOR_CHUNKING:
|
|
59
|
+
warnings.warn(
|
|
60
|
+
f"File size {filesize} is less than the minimum threshold for chunking ({_MIN_THRESHOLD_FOR_CHUNKING}), switching to single threaded upload."
|
|
61
|
+
)
|
|
62
|
+
err = self.upload_file(upload_target)
|
|
63
|
+
if err:
|
|
64
|
+
raise err
|
|
65
|
+
return MultiUploadResult.UPLOADED_FRESH
|
|
66
|
+
|
|
67
|
+
out = upload_file_multipart(
|
|
68
|
+
s3_client=self.client,
|
|
69
|
+
bucket_name=bucket_name,
|
|
70
|
+
file_path=upload_target.src_file,
|
|
71
|
+
object_name=upload_target.s3_key,
|
|
72
|
+
resumable_info_path=resume_path_json,
|
|
73
|
+
chunk_size=chunk_size,
|
|
74
|
+
retries=retries,
|
|
75
|
+
max_chunks_before_suspension=max_chunks_before_suspension,
|
|
76
|
+
)
|
|
77
|
+
return out
|
|
78
|
+
except Exception as e:
|
|
79
|
+
key = upload_target.s3_key
|
|
80
|
+
access_key_id = self.credentials.access_key_id[:4] + "..."
|
|
81
|
+
secret = self.credentials.secret_access_key[:4] + "..."
|
|
82
|
+
endpoint_url = self.credentials.endpoint_url
|
|
83
|
+
provider = self.credentials.provider
|
|
84
|
+
region_name = self.credentials.region_name
|
|
85
|
+
info_json = {
|
|
86
|
+
"key": key,
|
|
87
|
+
"access_key_id": access_key_id[:4] + "...",
|
|
88
|
+
"secret": secret[:4] + "...",
|
|
89
|
+
"endpoint_url": endpoint_url,
|
|
90
|
+
"provider": provider,
|
|
91
|
+
"region": region_name,
|
|
92
|
+
}
|
|
93
|
+
info_json_str = json.dumps(info_json, indent=2)
|
|
94
|
+
warnings.warn(f"Error uploading file: {e}\nInfo:\n\n{info_json_str}")
|
|
95
|
+
raise
|
rclone_api/s3/types.py
CHANGED
|
@@ -47,8 +47,9 @@ class S3MutliPartUploadConfig:
|
|
|
47
47
|
retries: int
|
|
48
48
|
resume_path_json: Path
|
|
49
49
|
max_chunks_before_suspension: int | None = None
|
|
50
|
-
mount_path: Path | None =
|
|
51
|
-
|
|
50
|
+
mount_path: Path | None = (
|
|
51
|
+
None # If set this will be used to mount the src file, otherwise it's one is chosen automatically
|
|
52
|
+
)
|
|
52
53
|
|
|
53
54
|
|
|
54
55
|
class MultiUploadResult(Enum):
|
rclone_api/util.py
CHANGED
|
@@ -166,3 +166,10 @@ def split_s3_path(path: str) -> S3PathInfo:
|
|
|
166
166
|
assert bucket
|
|
167
167
|
assert key
|
|
168
168
|
return S3PathInfo(remote=remote, bucket=bucket, key=key)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def random_str(length: int) -> str:
|
|
172
|
+
import random
|
|
173
|
+
import string
|
|
174
|
+
|
|
175
|
+
return "".join(random.choices(string.ascii_lowercase + string.digits, k=length))
|
|
@@ -12,24 +12,24 @@ rclone_api/file.py,sha256=EP5yT2dZ0H2p7CY5n0y5k5pHhIliV25pm8KOwBklUTk,1863
|
|
|
12
12
|
rclone_api/filelist.py,sha256=xbiusvNgaB_b_kQOZoHMJJxn6TWGtPrWd2J042BI28o,767
|
|
13
13
|
rclone_api/group_files.py,sha256=H92xPW9lQnbNw5KbtZCl00bD6iRh9yRbCuxku4j_3dg,8036
|
|
14
14
|
rclone_api/process.py,sha256=RrMfTe0bndmJ6gBK67ioqNvCstJ8aTC8RlGX1XBLlcw,4191
|
|
15
|
-
rclone_api/rclone.py,sha256=
|
|
15
|
+
rclone_api/rclone.py,sha256=tXVZv9N83DVVxVwevOo7FyzHCuUGjWx_KP51VwQ0QIY,42246
|
|
16
16
|
rclone_api/remote.py,sha256=O9WDUFQy9f6oT1HdUbTixK2eg0xtBBm8k4Xl6aa6K00,431
|
|
17
17
|
rclone_api/rpath.py,sha256=8ZA_1wxWtskwcy0I8V2VbjKDmzPkiWd8Q2JQSvh-sYE,2586
|
|
18
18
|
rclone_api/scan_missing_folders.py,sha256=Kulca2Q6WZodt00ATFHkmqqInuoPvBkhTcS9703y6po,4740
|
|
19
19
|
rclone_api/types.py,sha256=NC3e78aXCx-sEQ-FqEaC9KzaJDdJhJrKa4Nwum_-Db0,563
|
|
20
|
-
rclone_api/util.py,sha256=
|
|
20
|
+
rclone_api/util.py,sha256=efck9W0rw5wfeRI35iiEz4dy2cMkNpVXrQ9zzynkBks,5185
|
|
21
21
|
rclone_api/walk.py,sha256=-54NVE8EJcCstwDoaC_UtHm73R2HrZwVwQmsnv55xNU,3369
|
|
22
22
|
rclone_api/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
|
|
23
23
|
rclone_api/cmd/copy_large_s3.py,sha256=FPU0S1Y9pApVLmWcdMT_3llywjEtOtwobXTnMXE-FhY,2690
|
|
24
24
|
rclone_api/cmd/list_files.py,sha256=x8FHODEilwKqwdiU1jdkeJbLwOqUkUQuDWPo2u_zpf0,741
|
|
25
|
-
rclone_api/s3/api.py,sha256=
|
|
25
|
+
rclone_api/s3/api.py,sha256=sks9QLErHsLJ06Af7zJSa5-E05_vHqi9YJQxomtx3D8,3639
|
|
26
26
|
rclone_api/s3/basic_ops.py,sha256=hK3366xhVEzEcjz9Gk_8lFx6MRceAk72cax6mUrr6ko,2104
|
|
27
27
|
rclone_api/s3/chunk_uploader.py,sha256=Wcm4h_A6ORaJvT9P9qSeknbJYch22RKZ4FGo1iePlAw,17385
|
|
28
28
|
rclone_api/s3/create.py,sha256=X4mgjekgAoyHNeXnznHNS63DFViAhI0p7-fXDxml0y4,2701
|
|
29
|
-
rclone_api/s3/types.py,sha256=
|
|
30
|
-
rclone_api-1.0.
|
|
31
|
-
rclone_api-1.0.
|
|
32
|
-
rclone_api-1.0.
|
|
33
|
-
rclone_api-1.0.
|
|
34
|
-
rclone_api-1.0.
|
|
35
|
-
rclone_api-1.0.
|
|
29
|
+
rclone_api/s3/types.py,sha256=81_3jwg6MGIxC-GxL-6zANzKO6au9C0BWvAqRyODxOM,1361
|
|
30
|
+
rclone_api-1.0.93.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
|
|
31
|
+
rclone_api-1.0.93.dist-info/METADATA,sha256=T1_yOU3wV_gKRNH_bNGH9YSvOaj2rwyy8eIpQT1cw0k,4479
|
|
32
|
+
rclone_api-1.0.93.dist-info/WHEEL,sha256=rF4EZyR2XVS6irmOHQIJx2SUqXLZKRMUrjsg8UwN-XQ,109
|
|
33
|
+
rclone_api-1.0.93.dist-info/entry_points.txt,sha256=6eNqTRXKhVf8CpWNjXiOa_0Du9tHiW_HD2iQSXRsUg8,132
|
|
34
|
+
rclone_api-1.0.93.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
|
|
35
|
+
rclone_api-1.0.93.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|