rclone-api 1.3.23__py2.py3-none-any.whl → 1.3.25__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/s3/chunk_task.py +5 -0
- rclone_api/s3/upload_file_multipart.py +45 -27
- rclone_api/types.py +41 -1
- {rclone_api-1.3.23.dist-info → rclone_api-1.3.25.dist-info}/METADATA +1 -1
- {rclone_api-1.3.23.dist-info → rclone_api-1.3.25.dist-info}/RECORD +9 -9
- {rclone_api-1.3.23.dist-info → rclone_api-1.3.25.dist-info}/LICENSE +0 -0
- {rclone_api-1.3.23.dist-info → rclone_api-1.3.25.dist-info}/WHEEL +0 -0
- {rclone_api-1.3.23.dist-info → rclone_api-1.3.25.dist-info}/entry_points.txt +0 -0
- {rclone_api-1.3.23.dist-info → rclone_api-1.3.25.dist-info}/top_level.txt +0 -0
rclone_api/s3/chunk_task.py
CHANGED
@@ -184,11 +184,16 @@ def file_chunker(
|
|
184
184
|
logger.info(
|
185
185
|
f"Reading chunk {curr_part_number} of {num_parts} for {file_path}"
|
186
186
|
)
|
187
|
+
logger.debug(
|
188
|
+
f"Fetching part {curr_part_number} with offset {offset} and size {fetch_size}"
|
189
|
+
)
|
187
190
|
fut = fetcher(
|
188
191
|
offset, fetch_size, S3FileInfo(upload_info.upload_id, curr_part_number)
|
189
192
|
)
|
190
193
|
fut.add_done_callback(callback.on_complete)
|
191
194
|
# wait until the queue_upload queue can accept the next chunk
|
195
|
+
qsize = queue_upload.qsize()
|
196
|
+
print(f"queue_upload_size: {qsize}")
|
192
197
|
while queue_upload.full():
|
193
198
|
time.sleep(0.1)
|
194
199
|
except Exception as e:
|
@@ -25,7 +25,10 @@ _MIN_UPLOAD_CHUNK_SIZE = 5 * 1024 * 1024 # 5MB
|
|
25
25
|
|
26
26
|
|
27
27
|
def upload_task(
|
28
|
-
info: UploadInfo,
|
28
|
+
info: UploadInfo,
|
29
|
+
chunk: FilePart,
|
30
|
+
part_number: int,
|
31
|
+
retries: int,
|
29
32
|
) -> FinishedPiece:
|
30
33
|
file_or_err: Path | Exception = chunk.get_file()
|
31
34
|
if isinstance(file_or_err, Exception):
|
@@ -139,6 +142,40 @@ def _abort_previous_upload(upload_state: UploadState) -> None:
|
|
139
142
|
locked_print(f"Error aborting previous upload: {e}")
|
140
143
|
|
141
144
|
|
145
|
+
def upload_runner(
|
146
|
+
upload_state: UploadState,
|
147
|
+
upload_info: UploadInfo,
|
148
|
+
upload_threads: int,
|
149
|
+
queue_upload: Queue[FilePart | EndOfStream],
|
150
|
+
cancel_chunker_event: Event,
|
151
|
+
) -> None:
|
152
|
+
with ThreadPoolExecutor(max_workers=upload_threads) as executor:
|
153
|
+
try:
|
154
|
+
while True:
|
155
|
+
file_chunk: FilePart | EndOfStream = queue_upload.get()
|
156
|
+
if isinstance(file_chunk, EndOfStream):
|
157
|
+
break
|
158
|
+
|
159
|
+
def task(upload_info=upload_info, file_chunk=file_chunk):
|
160
|
+
return handle_upload(upload_info, file_chunk)
|
161
|
+
|
162
|
+
fut = executor.submit(task)
|
163
|
+
|
164
|
+
def done_cb(fut=fut):
|
165
|
+
result = fut.result()
|
166
|
+
if isinstance(result, Exception):
|
167
|
+
warnings.warn(f"Error uploading part: {result}, skipping")
|
168
|
+
return
|
169
|
+
# upload_state.finished_parts.put(result)
|
170
|
+
upload_state.add_finished(result)
|
171
|
+
|
172
|
+
fut.add_done_callback(done_cb)
|
173
|
+
except Exception:
|
174
|
+
cancel_chunker_event.set()
|
175
|
+
executor.shutdown(wait=False, cancel_futures=True)
|
176
|
+
raise
|
177
|
+
|
178
|
+
|
142
179
|
def upload_file_multipart(
|
143
180
|
s3_client: BaseClient,
|
144
181
|
chunk_fetcher: Callable[[int, int, Any], Future[FilePart]],
|
@@ -265,32 +302,13 @@ def upload_file_multipart(
|
|
265
302
|
try:
|
266
303
|
thread_chunker = Thread(target=chunker_task, daemon=True)
|
267
304
|
thread_chunker.start()
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
def task(upload_info=upload_info, file_chunk=file_chunk):
|
277
|
-
return handle_upload(upload_info, file_chunk)
|
278
|
-
|
279
|
-
fut = executor.submit(task)
|
280
|
-
|
281
|
-
def done_cb(fut=fut):
|
282
|
-
result = fut.result()
|
283
|
-
if isinstance(result, Exception):
|
284
|
-
warnings.warn(f"Error uploading part: {result}, skipping")
|
285
|
-
return
|
286
|
-
# upload_state.finished_parts.put(result)
|
287
|
-
upload_state.add_finished(result)
|
288
|
-
|
289
|
-
fut.add_done_callback(done_cb)
|
290
|
-
except Exception:
|
291
|
-
cancel_chunker_event.set()
|
292
|
-
executor.shutdown(wait=False, cancel_futures=True)
|
293
|
-
raise
|
305
|
+
upload_runner(
|
306
|
+
upload_state=upload_state,
|
307
|
+
upload_info=upload_info,
|
308
|
+
upload_threads=upload_threads,
|
309
|
+
queue_upload=queue_upload,
|
310
|
+
cancel_chunker_event=cancel_chunker_event,
|
311
|
+
)
|
294
312
|
# upload_state.finished_parts.put(None) # Signal the end of the queue
|
295
313
|
upload_state.add_finished(EndOfStream())
|
296
314
|
thread_chunker.join()
|
rclone_api/types.py
CHANGED
@@ -6,7 +6,7 @@ import warnings
|
|
6
6
|
from dataclasses import dataclass
|
7
7
|
from enum import Enum
|
8
8
|
from pathlib import Path
|
9
|
-
from threading import Lock
|
9
|
+
from threading import Lock, Thread
|
10
10
|
from typing import Any
|
11
11
|
|
12
12
|
|
@@ -288,10 +288,38 @@ def _on_exit_cleanup() -> None:
|
|
288
288
|
atexit.register(_on_exit_cleanup)
|
289
289
|
|
290
290
|
|
291
|
+
_FILEPARTS: list["FilePart"] = []
|
292
|
+
|
293
|
+
|
294
|
+
class ListFileParts(Thread):
|
295
|
+
def __init__(self):
|
296
|
+
super().__init__(daemon=True)
|
297
|
+
self.start()
|
298
|
+
|
299
|
+
def run(self):
|
300
|
+
while True:
|
301
|
+
print("File parts:")
|
302
|
+
for part in _FILEPARTS:
|
303
|
+
print(part)
|
304
|
+
print(part.stacktrace)
|
305
|
+
print("\n")
|
306
|
+
print("\n\n")
|
307
|
+
time.sleep(5)
|
308
|
+
|
309
|
+
|
310
|
+
dbg_thread = ListFileParts()
|
311
|
+
|
312
|
+
|
291
313
|
class FilePart:
|
292
314
|
def __init__(self, payload: Path | bytes | Exception, extra: Any) -> None:
|
315
|
+
import traceback
|
316
|
+
|
293
317
|
from rclone_api.util import random_str
|
294
318
|
|
319
|
+
stacktrace = traceback.format_stack()
|
320
|
+
self.stacktrace = stacktrace
|
321
|
+
_FILEPARTS.append(self)
|
322
|
+
|
295
323
|
self.extra = extra
|
296
324
|
self._lock = Lock()
|
297
325
|
self.payload: Path | Exception
|
@@ -299,6 +327,7 @@ class FilePart:
|
|
299
327
|
self.payload = payload
|
300
328
|
return
|
301
329
|
if isinstance(payload, bytes):
|
330
|
+
print(f"Creating file part with payload: {len(payload)}")
|
302
331
|
self.payload = get_chunk_tmpdir() / f"{random_str(12)}.chunk"
|
303
332
|
with _TMP_DIR_ACCESS_LOCK:
|
304
333
|
if not self.payload.parent.exists():
|
@@ -306,7 +335,9 @@ class FilePart:
|
|
306
335
|
self.payload.write_bytes(payload)
|
307
336
|
_add_for_cleanup(self.payload)
|
308
337
|
if isinstance(payload, Path):
|
338
|
+
print("Adopting payload: ", payload)
|
309
339
|
self.payload = payload
|
340
|
+
_add_for_cleanup(self.payload)
|
310
341
|
|
311
342
|
def get_file(self) -> Path | Exception:
|
312
343
|
return self.payload
|
@@ -344,18 +375,27 @@ class FilePart:
|
|
344
375
|
return isinstance(self.payload, Exception)
|
345
376
|
|
346
377
|
def dispose(self) -> None:
|
378
|
+
_FILEPARTS.remove(self)
|
379
|
+
print("Disposing file part")
|
347
380
|
with self._lock:
|
348
381
|
if isinstance(self.payload, Exception):
|
349
382
|
warnings.warn(
|
350
383
|
f"Cannot close file part because the payload represents an error: {self.payload}"
|
351
384
|
)
|
385
|
+
print("Cannot close file part because the payload represents an error")
|
352
386
|
return
|
353
387
|
if self.payload.exists():
|
388
|
+
print(f"File part {self.payload} exists")
|
354
389
|
try:
|
390
|
+
print(f"Unlinking file part {self.payload}")
|
355
391
|
self.payload.unlink()
|
356
392
|
print(f"File part {self.payload} deleted")
|
357
393
|
except Exception as e:
|
358
394
|
warnings.warn(f"Cannot close file part because of error: {e}")
|
395
|
+
else:
|
396
|
+
warnings.warn(
|
397
|
+
f"Cannot close file part because it does not exist: {self.payload}"
|
398
|
+
)
|
359
399
|
|
360
400
|
def __del__(self):
|
361
401
|
self.dispose()
|
@@ -21,7 +21,7 @@ rclone_api/rclone.py,sha256=yRhQoCBJI-tfhxySR17a-vSEhWw5cMbk8_WbYs5WqRc,54117
|
|
21
21
|
rclone_api/remote.py,sha256=jq3dPbAGvYZFW5cTviqxT2w6_jG2LLfS1RIcYSmMsQQ,503
|
22
22
|
rclone_api/rpath.py,sha256=8ZA_1wxWtskwcy0I8V2VbjKDmzPkiWd8Q2JQSvh-sYE,2586
|
23
23
|
rclone_api/scan_missing_folders.py,sha256=Kulca2Q6WZodt00ATFHkmqqInuoPvBkhTcS9703y6po,4740
|
24
|
-
rclone_api/types.py,sha256=
|
24
|
+
rclone_api/types.py,sha256=ywPapK0Mw9inphdsxnxQwodyA2hCEfblwG7Pz1mvl3E,11792
|
25
25
|
rclone_api/util.py,sha256=F9Q3zbWRsgPF4NG6OWB63cZ7GVq82lsraP47gmmDohU,5416
|
26
26
|
rclone_api/walk.py,sha256=-54NVE8EJcCstwDoaC_UtHm73R2HrZwVwQmsnv55xNU,3369
|
27
27
|
rclone_api/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
|
@@ -37,14 +37,14 @@ rclone_api/experimental/flags_base.py,sha256=ajU_czkTcAxXYU-SlmiCfHY7aCQGHvpCLqJ
|
|
37
37
|
rclone_api/profile/mount_copy_bytes.py,sha256=M1vZn-Mrga14Ik7MHGZHbnwYli41Ep6Tyll7hQc7Wmo,9071
|
38
38
|
rclone_api/s3/api.py,sha256=PafsIEyWDpLWAXsZAjFm9CY14vJpsDr9lOsn0kGRLZ0,4009
|
39
39
|
rclone_api/s3/basic_ops.py,sha256=hK3366xhVEzEcjz9Gk_8lFx6MRceAk72cax6mUrr6ko,2104
|
40
|
-
rclone_api/s3/chunk_task.py,sha256=
|
40
|
+
rclone_api/s3/chunk_task.py,sha256=AanVCygDoUjmMOUdEIYl-hEpPEGSJTIU_MSFGQ0tI0Q,7421
|
41
41
|
rclone_api/s3/chunk_types.py,sha256=oSWv8No9V3BeM7IcGnowyR2a7YrszdAXzEJlxaeZcp0,8852
|
42
42
|
rclone_api/s3/create.py,sha256=wgfkapv_j904CfKuWyiBIWJVxfAx_ftemFSUV14aT68,3149
|
43
43
|
rclone_api/s3/types.py,sha256=Elmh__gvZJyJyElYwMmvYZIBIunDJiTRAbEg21GmsRU,1604
|
44
|
-
rclone_api/s3/upload_file_multipart.py,sha256=
|
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.
|
50
|
-
rclone_api-1.3.
|
44
|
+
rclone_api/s3/upload_file_multipart.py,sha256=bxAB_SFkyydBBzC_cAv7gShe93nBkFVS-TXwM2Xttfk,12425
|
45
|
+
rclone_api-1.3.25.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
|
46
|
+
rclone_api-1.3.25.dist-info/METADATA,sha256=Pq7IVLcxa50RVNbvCcqG3yGkL2QN9mgb9t_aCA-9sn0,4628
|
47
|
+
rclone_api-1.3.25.dist-info/WHEEL,sha256=rF4EZyR2XVS6irmOHQIJx2SUqXLZKRMUrjsg8UwN-XQ,109
|
48
|
+
rclone_api-1.3.25.dist-info/entry_points.txt,sha256=fJteOlYVwgX3UbNuL9jJ0zUTuX2O79JFAeNgK7Sw7EQ,255
|
49
|
+
rclone_api-1.3.25.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
|
50
|
+
rclone_api-1.3.25.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|