rclone-api 1.4.8__tar.gz → 1.4.10__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.
Files changed (106) hide show
  1. {rclone_api-1.4.8 → rclone_api-1.4.10}/PKG-INFO +1 -1
  2. {rclone_api-1.4.8 → rclone_api-1.4.10}/pyproject.toml +1 -1
  3. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/cmd/copy_large_s3_finish.py +24 -13
  4. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/detail/copy_file_parts.py +11 -0
  5. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/s3/create.py +1 -1
  6. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/s3/s3_multipart_uploader_by_copy.py +40 -16
  7. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api.egg-info/PKG-INFO +1 -1
  8. {rclone_api-1.4.8 → rclone_api-1.4.10}/.aiderignore +0 -0
  9. {rclone_api-1.4.8 → rclone_api-1.4.10}/.github/workflows/lint.yml +0 -0
  10. {rclone_api-1.4.8 → rclone_api-1.4.10}/.github/workflows/push_macos.yml +0 -0
  11. {rclone_api-1.4.8 → rclone_api-1.4.10}/.github/workflows/push_ubuntu.yml +0 -0
  12. {rclone_api-1.4.8 → rclone_api-1.4.10}/.github/workflows/push_win.yml +0 -0
  13. {rclone_api-1.4.8 → rclone_api-1.4.10}/.gitignore +0 -0
  14. {rclone_api-1.4.8 → rclone_api-1.4.10}/.pylintrc +0 -0
  15. {rclone_api-1.4.8 → rclone_api-1.4.10}/.vscode/launch.json +0 -0
  16. {rclone_api-1.4.8 → rclone_api-1.4.10}/.vscode/settings.json +0 -0
  17. {rclone_api-1.4.8 → rclone_api-1.4.10}/.vscode/tasks.json +0 -0
  18. {rclone_api-1.4.8 → rclone_api-1.4.10}/LICENSE +0 -0
  19. {rclone_api-1.4.8 → rclone_api-1.4.10}/MANIFEST.in +0 -0
  20. {rclone_api-1.4.8 → rclone_api-1.4.10}/README.md +0 -0
  21. {rclone_api-1.4.8 → rclone_api-1.4.10}/clean +0 -0
  22. {rclone_api-1.4.8 → rclone_api-1.4.10}/install +0 -0
  23. {rclone_api-1.4.8 → rclone_api-1.4.10}/lint +0 -0
  24. {rclone_api-1.4.8 → rclone_api-1.4.10}/requirements.testing.txt +0 -0
  25. {rclone_api-1.4.8 → rclone_api-1.4.10}/setup.cfg +0 -0
  26. {rclone_api-1.4.8 → rclone_api-1.4.10}/setup.py +0 -0
  27. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/__init__.py +0 -0
  28. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/assets/example.txt +0 -0
  29. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/cli.py +0 -0
  30. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/cmd/analyze.py +0 -0
  31. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/cmd/copy_large_s3.py +0 -0
  32. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/cmd/list_files.py +0 -0
  33. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/cmd/save_to_db.py +0 -0
  34. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/completed_process.py +0 -0
  35. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/config.py +0 -0
  36. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/convert.py +0 -0
  37. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/db/__init__.py +0 -0
  38. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/db/db.py +0 -0
  39. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/db/models.py +0 -0
  40. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/deprecated.py +0 -0
  41. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/detail/walk.py +0 -0
  42. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/diff.py +0 -0
  43. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/dir.py +0 -0
  44. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/dir_listing.py +0 -0
  45. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/exec.py +0 -0
  46. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/experimental/flags.py +0 -0
  47. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/experimental/flags_base.py +0 -0
  48. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/file.py +0 -0
  49. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/file_item.py +0 -0
  50. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/file_part.py +0 -0
  51. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/file_stream.py +0 -0
  52. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/filelist.py +0 -0
  53. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/group_files.py +0 -0
  54. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/http_server.py +0 -0
  55. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/log.py +0 -0
  56. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/mount.py +0 -0
  57. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/process.py +0 -0
  58. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/rclone_impl.py +0 -0
  59. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/remote.py +0 -0
  60. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/rpath.py +0 -0
  61. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/s3/api.py +0 -0
  62. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/s3/basic_ops.py +0 -0
  63. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/s3/chunk_task.py +0 -0
  64. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/s3/multipart/file_info.py +0 -0
  65. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/s3/multipart/finished_piece.py +0 -0
  66. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/s3/multipart/upload_info.py +0 -0
  67. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/s3/multipart/upload_state.py +0 -0
  68. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/s3/types.py +0 -0
  69. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/s3/upload_file_multipart.py +0 -0
  70. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/scan_missing_folders.py +0 -0
  71. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/types.py +0 -0
  72. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api/util.py +0 -0
  73. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api.egg-info/SOURCES.txt +0 -0
  74. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api.egg-info/dependency_links.txt +0 -0
  75. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api.egg-info/entry_points.txt +0 -0
  76. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api.egg-info/requires.txt +0 -0
  77. {rclone_api-1.4.8 → rclone_api-1.4.10}/src/rclone_api.egg-info/top_level.txt +0 -0
  78. {rclone_api-1.4.8 → rclone_api-1.4.10}/test +0 -0
  79. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/archive/test_paramiko.py.disabled +0 -0
  80. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/test_cmd_list_files.py +0 -0
  81. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/test_copy.py +0 -0
  82. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/test_copy_bytes.py +0 -0
  83. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/test_copy_file_resumable_s3.py +0 -0
  84. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/test_copy_files.py +0 -0
  85. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/test_db.py +0 -0
  86. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/test_diff.py +0 -0
  87. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/test_file_item.py +0 -0
  88. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/test_group_files.py +0 -0
  89. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/test_is_synced.py +0 -0
  90. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/test_ls.py +0 -0
  91. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/test_ls_stream_files.py +0 -0
  92. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/test_mount.py +0 -0
  93. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/test_mount_s3.py +0 -0
  94. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/test_obscure.py +0 -0
  95. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/test_rclone_config.py +0 -0
  96. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/test_read_write_text.py +0 -0
  97. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/test_remote_control.py +0 -0
  98. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/test_remotes.py +0 -0
  99. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/test_s3.py +0 -0
  100. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/test_scan_missing_folders.py +0 -0
  101. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/test_serve_http.py +0 -0
  102. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/test_size_files.py +0 -0
  103. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/test_size_suffix.py +0 -0
  104. {rclone_api-1.4.8 → rclone_api-1.4.10}/tests/test_walk.py +0 -0
  105. {rclone_api-1.4.8 → rclone_api-1.4.10}/tox.ini +0 -0
  106. {rclone_api-1.4.8 → rclone_api-1.4.10}/upload_package.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.4.8
3
+ Version: 1.4.10
4
4
  Summary: rclone api in python
5
5
  Home-page: https://github.com/zackees/rclone-api
6
6
  License: BSD 3-Clause License
@@ -25,7 +25,7 @@ dependencies = [
25
25
  ]
26
26
 
27
27
  # Change this with the version number bump.
28
- version = "1.4.8"
28
+ version = "1.4.10"
29
29
 
30
30
  [tool.setuptools]
31
31
  package-dir = {"" = "src"}
@@ -116,13 +116,12 @@ def do_finish_part(rclone: Rclone, info: InfoJson, dst: str) -> None:
116
116
  if parts_dir.endswith("/"):
117
117
  parts_dir = parts_dir[:-1]
118
118
  source_keys = info.fetch_all_finished()
119
+ # print(parts_dir)
120
+ # print(source_keys)
119
121
 
120
- print(parts_dir)
121
- print(source_keys)
122
-
123
- parent_path = parts_dir.split(s3_bucket)[1]
124
- if parent_path.startswith("/"):
125
- parent_path = parent_path[1:]
122
+ parts_path = parts_dir.split(s3_bucket)[1]
123
+ if parts_path.startswith("/"):
124
+ parts_path = parts_path[1:]
126
125
 
127
126
  first_part: int | None = info.first_part
128
127
  last_part: int | None = info.last_part
@@ -132,16 +131,20 @@ def do_finish_part(rclone: Rclone, info: InfoJson, dst: str) -> None:
132
131
  assert last_part is not None
133
132
  assert size is not None
134
133
 
135
- def _to_s3_key(name: str) -> str:
136
- out = f"{parent_path}/{name}"
134
+ def _to_s3_key(name: str | None) -> str:
135
+ if name:
136
+ out = f"{parts_path}/{name}"
137
+ return out
138
+ out = f"{parts_path}"
137
139
  return out
138
140
 
139
141
  # s3_keys: list[str] = [_to_s3_key(name=p) for p in source_keys]
140
142
  parts: list[tuple[int, str]] = []
141
- for i in range(first_part, last_part + 1):
142
- part_name = f"part.{i:05d}"
143
- s3_key = _to_s3_key(name=part_name)
144
- parts.append((i, s3_key))
143
+ part_num = 1
144
+ for part_key in source_keys:
145
+ s3_key = _to_s3_key(name=part_key)
146
+ parts.append((part_num, s3_key))
147
+ part_num += 1
145
148
 
146
149
  # for key in parts:
147
150
  # print(key)
@@ -149,14 +152,22 @@ def do_finish_part(rclone: Rclone, info: InfoJson, dst: str) -> None:
149
152
  chunksize = info.chunksize
150
153
  assert chunksize is not None
151
154
 
155
+ import os
156
+
157
+ dst_name = info.dst_name
158
+ dst_dir = os.path.dirname(parts_path)
159
+ # dst_key =
160
+ dst_key = f"{dst_dir}/{dst_name}"
161
+
152
162
  finish_multipart_upload_from_keys(
153
163
  s3_client=s3_client,
154
164
  source_bucket=s3_creds.bucket_name,
155
165
  parts=parts,
156
166
  destination_bucket=s3_creds.bucket_name,
157
- destination_key=dst,
167
+ destination_key=dst_key,
158
168
  chunk_size=chunksize.as_int(),
159
169
  final_size=size.as_int(),
170
+ max_workers=100,
160
171
  retries=3,
161
172
  )
162
173
 
@@ -211,6 +211,17 @@ class InfoJson:
211
211
  def parts_dir(self) -> str:
212
212
  return os.path.dirname(self.src_info)
213
213
 
214
+ @property
215
+ def dst(self) -> str:
216
+ parts_dir = self.parts_dir
217
+ assert parts_dir.endswith("-parts")
218
+ out = parts_dir[:-6]
219
+ return out
220
+
221
+ @property
222
+ def dst_name(self) -> str:
223
+ return os.path.basename(self.dst)
224
+
214
225
  def compute_all_parts(self) -> list[PartInfo] | Exception:
215
226
  # full_part_infos: list[PartInfo] | Exception = PartInfo.split_parts(
216
227
  # src_size, SizeSuffix("96MB")
@@ -25,7 +25,7 @@ def _create_backblaze_s3_client(creds: S3Credentials, verbose: bool) -> BaseClie
25
25
  aws_access_key_id=access_key,
26
26
  aws_secret_access_key=secret_key,
27
27
  endpoint_url=endpoint_url,
28
- verify=False, # Disables SSL certificate verification
28
+ # verify=False, # Disables SSL certificate verification
29
29
  config=Config(
30
30
  signature_version="s3v4",
31
31
  region_name=region_name,
@@ -9,6 +9,7 @@ from existing S3 objects using upload_part_copy.
9
9
  from concurrent.futures import Future, ThreadPoolExecutor
10
10
  from dataclasses import dataclass
11
11
  from pathlib import Path
12
+ from threading import Semaphore
12
13
  from typing import Optional
13
14
 
14
15
  from botocore.client import BaseClient
@@ -183,7 +184,7 @@ def upload_part_copy_task(
183
184
  source_key: str,
184
185
  part_number: int,
185
186
  retries: int = 3,
186
- ) -> FinishedPiece:
187
+ ) -> FinishedPiece | Exception:
187
188
  """
188
189
  Upload a part by copying from an existing S3 object.
189
190
 
@@ -200,8 +201,11 @@ def upload_part_copy_task(
200
201
  """
201
202
  copy_source = {"Bucket": source_bucket, "Key": source_key}
202
203
 
204
+ # from botocore.exceptions import NoSuchKey
205
+
203
206
  retries = retries + 1 # Add one for the initial attempt
204
207
  for retry in range(retries):
208
+ params: dict = {}
205
209
  try:
206
210
  if retry > 0:
207
211
  locked_print(f"Retrying part copy {part_number} for {info.object_name}")
@@ -226,16 +230,23 @@ def upload_part_copy_task(
226
230
  etag = part["CopyPartResult"]["ETag"]
227
231
 
228
232
  return FinishedPiece(etag=etag, part_number=part_number)
233
+ # except NoSuchKey as e:
234
+ # locked_print(f"Error copying part {part_number}: {e}")
235
+ # return e
229
236
 
230
237
  except Exception as e:
238
+ msg = f"Error copying {copy_source} -> {info.object_name}: {e}, params={params}"
239
+ if "NoSuchKey" in str(e):
240
+ locked_print(msg)
241
+ return e
231
242
  if retry == retries - 1:
232
- locked_print(f"Error copying part {part_number}: {e}")
233
- raise e
243
+ locked_print(msg)
244
+ return e
234
245
  else:
235
- locked_print(f"Error copying part {part_number}: {e}, retrying")
246
+ locked_print(f"{msg}, retrying")
236
247
  continue
237
248
 
238
- raise Exception("Should not reach here")
249
+ return Exception("Should not reach here")
239
250
 
240
251
 
241
252
  def complete_multipart_upload_from_parts(
@@ -303,9 +314,14 @@ def finish_multipart_upload_from_keys(
303
314
  locked_print(
304
315
  f"Creating multipart upload for {destination_bucket}/{destination_key} from {len(parts)} source objects"
305
316
  )
306
- mpu = s3_client.create_multipart_upload(
307
- Bucket=destination_bucket, Key=destination_key
308
- )
317
+
318
+ create_params: dict[str, str] = {
319
+ "Bucket": destination_bucket,
320
+ "Key": destination_key,
321
+ }
322
+ print(f"Creating multipart upload with {create_params}")
323
+ mpu = s3_client.create_multipart_upload(**create_params)
324
+ print(f"Created multipart upload: {mpu}")
309
325
  upload_id = mpu["UploadId"]
310
326
 
311
327
  # Create upload info
@@ -319,9 +335,12 @@ def finish_multipart_upload_from_keys(
319
335
  file_size=final_size,
320
336
  )
321
337
 
322
- futures: list[Future[FinishedPiece]] = []
338
+ futures: list[Future[FinishedPiece | Exception]] = []
323
339
 
324
340
  with ThreadPoolExecutor(max_workers=max_workers) as executor:
341
+ # semaphore
342
+
343
+ semaphore = Semaphore(max_workers * 2)
325
344
  for part_number, source_key in parts:
326
345
 
327
346
  def task(
@@ -340,17 +359,22 @@ def finish_multipart_upload_from_keys(
340
359
  )
341
360
 
342
361
  fut = executor.submit(task)
362
+ fut.add_done_callback(lambda x: semaphore.release())
343
363
  futures.append(fut)
364
+ semaphore.acquire()
344
365
 
345
- # Upload parts by copying from source objects
346
- finished_parts = []
366
+ # Upload parts by copying from source objects
367
+ finished_parts: list[FinishedPiece] = []
347
368
 
348
- for fut in futures:
349
- finished_part = fut.result()
350
- finished_parts.append(finished_part)
369
+ for fut in futures:
370
+ finished_part = fut.result()
371
+ if isinstance(finished_part, Exception):
372
+ executor.shutdown(wait=True, cancel_futures=True)
373
+ raise finished_part
374
+ finished_parts.append(finished_part)
351
375
 
352
- # Complete the multipart upload
353
- return complete_multipart_upload_from_parts(upload_info, finished_parts)
376
+ # Complete the multipart upload
377
+ return complete_multipart_upload_from_parts(upload_info, finished_parts)
354
378
 
355
379
 
356
380
  class S3MultiPartUploader:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.4.8
3
+ Version: 1.4.10
4
4
  Summary: rclone api in python
5
5
  Home-page: https://github.com/zackees/rclone-api
6
6
  License: BSD 3-Clause License
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