rclone-api 1.1.11__tar.gz → 1.1.13__tar.gz
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-1.1.11 → rclone_api-1.1.13}/PKG-INFO +1 -1
- {rclone_api-1.1.11 → rclone_api-1.1.13}/pyproject.toml +1 -1
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/cmd/copy_large_s3.py +11 -2
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/rclone.py +6 -3
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/s3/chunk_types.py +26 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/s3/chunk_uploader.py +2 -2
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/s3/types.py +1 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api.egg-info/PKG-INFO +1 -1
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api.egg-info/SOURCES.txt +0 -1
- {rclone_api-1.1.11 → rclone_api-1.1.13}/tests/test_s3.py +2 -0
- rclone_api-1.1.11/rclone-mounted-ranged-download.conf.old3393025366 +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/.aiderignore +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/.github/workflows/lint.yml +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/.github/workflows/push_macos.yml +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/.github/workflows/push_ubuntu.yml +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/.github/workflows/push_win.yml +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/.gitignore +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/.pylintrc +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/.vscode/launch.json +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/.vscode/settings.json +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/.vscode/tasks.json +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/LICENSE +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/MANIFEST.in +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/README.md +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/clean +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/install +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/lint +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/requirements.testing.txt +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/setup.cfg +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/setup.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/__init__.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/assets/example.txt +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/cli.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/cmd/list_files.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/completed_process.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/config.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/convert.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/deprecated.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/diff.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/dir.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/dir_listing.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/exec.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/experimental/flags.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/experimental/flags_base.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/file.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/filelist.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/group_files.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/mount.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/process.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/remote.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/rpath.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/s3/api.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/s3/basic_ops.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/s3/chunk_file.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/s3/create.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/scan_missing_folders.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/types.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/util.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api/walk.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api.egg-info/dependency_links.txt +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api.egg-info/entry_points.txt +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api.egg-info/requires.txt +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/src/rclone_api.egg-info/top_level.txt +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/test +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/tests/archive/test_paramiko.py.disabled +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/tests/test_cmd_list_files.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/tests/test_copy.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/tests/test_copy_files.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/tests/test_diff.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/tests/test_group_files.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/tests/test_is_synced.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/tests/test_ls.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/tests/test_mount.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/tests/test_mount_s3.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/tests/test_mounted_ranged_download.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/tests/test_obscure.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/tests/test_rclone_config.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/tests/test_remote_control.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/tests/test_remotes.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/tests/test_scan_missing_folders.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/tests/test_size_files.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/tests/test_size_suffix.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/tests/test_walk.py +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/tox.ini +0 -0
- {rclone_api-1.1.11 → rclone_api-1.1.13}/upload_package.sh +0 -0
|
@@ -14,6 +14,7 @@ class Args:
|
|
|
14
14
|
dst: str
|
|
15
15
|
chunk_size: SizeSuffix
|
|
16
16
|
threads: int
|
|
17
|
+
write_threads: int
|
|
17
18
|
retries: int
|
|
18
19
|
save_state_json: Path
|
|
19
20
|
verbose: bool
|
|
@@ -38,7 +39,7 @@ def _parse_args() -> Args:
|
|
|
38
39
|
"--chunk-size",
|
|
39
40
|
help="Chunk size that will be read and uploaded in in SizeSuffix (i.e. 128M = 128 megabytes) form",
|
|
40
41
|
type=str,
|
|
41
|
-
default="
|
|
42
|
+
default="512MB",
|
|
42
43
|
)
|
|
43
44
|
parser.add_argument(
|
|
44
45
|
"--threads",
|
|
@@ -46,6 +47,12 @@ def _parse_args() -> Args:
|
|
|
46
47
|
type=int,
|
|
47
48
|
default=64,
|
|
48
49
|
)
|
|
50
|
+
parser.add_argument(
|
|
51
|
+
"--write-threads",
|
|
52
|
+
help="Max number of chunks to upload in parallel to the destination",
|
|
53
|
+
type=int,
|
|
54
|
+
default=64,
|
|
55
|
+
)
|
|
49
56
|
parser.add_argument("--retries", help="Number of retries", type=int, default=3)
|
|
50
57
|
parser.add_argument(
|
|
51
58
|
"--resume-json",
|
|
@@ -61,6 +68,7 @@ def _parse_args() -> Args:
|
|
|
61
68
|
dst=args.dst,
|
|
62
69
|
chunk_size=SizeSuffix(args.chunk_size),
|
|
63
70
|
threads=args.threads,
|
|
71
|
+
write_threads=args.write_threads,
|
|
64
72
|
retries=args.retries,
|
|
65
73
|
save_state_json=args.resume_json,
|
|
66
74
|
verbose=args.verbose,
|
|
@@ -77,7 +85,8 @@ def main() -> int:
|
|
|
77
85
|
src=args.src,
|
|
78
86
|
dst=args.dst,
|
|
79
87
|
chunk_size=args.chunk_size,
|
|
80
|
-
|
|
88
|
+
read_threads=args.threads,
|
|
89
|
+
write_threads=args.write_threads,
|
|
81
90
|
# vfs_read_chunk_size=unit_chunk,
|
|
82
91
|
# vfs_read_chunk_size_limit=args.chunk_size,
|
|
83
92
|
# vfs_read_chunk_streams=args.threads,
|
|
@@ -678,7 +678,8 @@ class Rclone:
|
|
|
678
678
|
dst: str,
|
|
679
679
|
save_state_json: Path,
|
|
680
680
|
chunk_size: SizeSuffix | None = None,
|
|
681
|
-
|
|
681
|
+
read_threads: int = 16,
|
|
682
|
+
write_threads: int = 16,
|
|
682
683
|
retries: int = 3,
|
|
683
684
|
verbose: bool | None = None,
|
|
684
685
|
max_chunks_before_suspension: int | None = None,
|
|
@@ -691,10 +692,10 @@ class Rclone:
|
|
|
691
692
|
|
|
692
693
|
other_args: list[str] = ["--no-modtime", "--vfs-read-wait", "1s"]
|
|
693
694
|
chunk_size = chunk_size or SizeSuffix("128M")
|
|
694
|
-
unit_chunk_size = chunk_size /
|
|
695
|
+
unit_chunk_size = chunk_size / read_threads
|
|
695
696
|
vfs_read_chunk_size = unit_chunk_size
|
|
696
697
|
vfs_read_chunk_size_limit = chunk_size
|
|
697
|
-
vfs_read_chunk_streams =
|
|
698
|
+
vfs_read_chunk_streams = read_threads
|
|
698
699
|
vfs_disk_space_total_size = chunk_size
|
|
699
700
|
assert (
|
|
700
701
|
chunk_size.as_int() % vfs_read_chunk_size.as_int() == 0
|
|
@@ -773,6 +774,7 @@ class Rclone:
|
|
|
773
774
|
client = S3Client(s3_creds)
|
|
774
775
|
config: S3MutliPartUploadConfig = S3MutliPartUploadConfig(
|
|
775
776
|
chunk_size=chunk_size.as_int(),
|
|
777
|
+
max_write_threads=write_threads,
|
|
776
778
|
retries=retries,
|
|
777
779
|
resume_path_json=save_state_json,
|
|
778
780
|
max_chunks_before_suspension=max_chunks_before_suspension,
|
|
@@ -797,6 +799,7 @@ class Rclone:
|
|
|
797
799
|
upload_config = S3MutliPartUploadConfig(
|
|
798
800
|
chunk_size=chunk_size.as_int(),
|
|
799
801
|
retries=retries,
|
|
802
|
+
max_write_threads=write_threads,
|
|
800
803
|
resume_path_json=save_state_json,
|
|
801
804
|
max_chunks_before_suspension=max_chunks_before_suspension,
|
|
802
805
|
)
|
|
@@ -184,6 +184,9 @@ class UploadState:
|
|
|
184
184
|
def is_done(self) -> bool:
|
|
185
185
|
return self.remaining() == 0
|
|
186
186
|
|
|
187
|
+
def fingerprint(self) -> str:
|
|
188
|
+
return self.upload_info.fingerprint()
|
|
189
|
+
|
|
187
190
|
def count(self) -> tuple[int, int]: # count, num_chunks
|
|
188
191
|
num_chunks = self.upload_info.total_chunks()
|
|
189
192
|
count = 0
|
|
@@ -218,8 +221,31 @@ class UploadState:
|
|
|
218
221
|
|
|
219
222
|
def save(self) -> None:
|
|
220
223
|
with _SAVE_STATE_LOCK:
|
|
224
|
+
|
|
225
|
+
self._check_fingerprint_no_lock()
|
|
226
|
+
|
|
221
227
|
self._save_no_lock()
|
|
222
228
|
|
|
229
|
+
def _check_fingerprint_no_lock(self) -> None:
|
|
230
|
+
if self.peristant is None:
|
|
231
|
+
raise ValueError("No path to save to")
|
|
232
|
+
s3_client = self.upload_info.s3_client
|
|
233
|
+
path = self.peristant
|
|
234
|
+
last_upload_state: UploadState | None = None
|
|
235
|
+
if path.exists():
|
|
236
|
+
try:
|
|
237
|
+
last_upload_state = UploadState.from_json(s3_client, path)
|
|
238
|
+
except Exception as e:
|
|
239
|
+
locked_print(f"Error loading state: {e}")
|
|
240
|
+
last_upload_state = None
|
|
241
|
+
# now check that the fingerprint is the same
|
|
242
|
+
if last_upload_state is not None:
|
|
243
|
+
curr_fingerprint = self.fingerprint()
|
|
244
|
+
if curr_fingerprint != last_upload_state.fingerprint():
|
|
245
|
+
raise ValueError(
|
|
246
|
+
f"Cannot save state, fingerprint changed from {curr_fingerprint} to {self.upload_info.fingerprint()}"
|
|
247
|
+
)
|
|
248
|
+
|
|
223
249
|
def _save_no_lock(self) -> None:
|
|
224
250
|
assert self.peristant is not None, "No path to save to"
|
|
225
251
|
self.peristant.write_text(self.to_json_str(), encoding="utf-8")
|
|
@@ -115,6 +115,7 @@ def upload_file_multipart(
|
|
|
115
115
|
object_name: str,
|
|
116
116
|
resumable_info_path: Path | None,
|
|
117
117
|
chunk_size: int = 16 * 1024 * 1024, # Default chunk size is 16MB; can be overridden
|
|
118
|
+
upload_threads: int = 16,
|
|
118
119
|
retries: int = 20,
|
|
119
120
|
max_chunks_before_suspension: int | None = None,
|
|
120
121
|
abort_transfer_on_failure: bool = False,
|
|
@@ -197,7 +198,6 @@ def upload_file_multipart(
|
|
|
197
198
|
)
|
|
198
199
|
started_new_upload = finished == 0
|
|
199
200
|
upload_info = upload_state.upload_info
|
|
200
|
-
max_workers = 8
|
|
201
201
|
|
|
202
202
|
chunker_errors: Queue[Exception] = Queue()
|
|
203
203
|
|
|
@@ -220,7 +220,7 @@ def upload_file_multipart(
|
|
|
220
220
|
thread_chunker = Thread(target=chunker_task, daemon=True)
|
|
221
221
|
thread_chunker.start()
|
|
222
222
|
|
|
223
|
-
with ThreadPoolExecutor(max_workers=
|
|
223
|
+
with ThreadPoolExecutor(max_workers=upload_threads) as executor:
|
|
224
224
|
while True:
|
|
225
225
|
file_chunk: FileChunk | None = filechunks.get()
|
|
226
226
|
if file_chunk is None:
|
|
@@ -46,6 +46,7 @@ class S3MutliPartUploadConfig:
|
|
|
46
46
|
chunk_size: int
|
|
47
47
|
retries: int
|
|
48
48
|
resume_path_json: Path
|
|
49
|
+
max_write_threads: int
|
|
49
50
|
max_chunks_before_suspension: int | None = None
|
|
50
51
|
mount_path: Path | None = (
|
|
51
52
|
None # If set this will be used to mount the src file, otherwise it's one is chosen automatically
|
|
@@ -74,6 +74,7 @@ class RcloneS3Tester(unittest.TestCase):
|
|
|
74
74
|
# Uploads one chunk then stops.
|
|
75
75
|
upload_config_partial: S3MutliPartUploadConfig = S3MutliPartUploadConfig(
|
|
76
76
|
chunk_size=chunk_size,
|
|
77
|
+
max_write_threads=16,
|
|
77
78
|
retries=0,
|
|
78
79
|
resume_path_json=state_json,
|
|
79
80
|
max_chunks_before_suspension=1,
|
|
@@ -82,6 +83,7 @@ class RcloneS3Tester(unittest.TestCase):
|
|
|
82
83
|
# Finishes the upload.
|
|
83
84
|
upload_config_all: S3MutliPartUploadConfig = S3MutliPartUploadConfig(
|
|
84
85
|
chunk_size=chunk_size,
|
|
86
|
+
max_write_threads=16,
|
|
85
87
|
retries=0,
|
|
86
88
|
resume_path_json=state_json,
|
|
87
89
|
max_chunks_before_suspension=None,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|