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.
@@ -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: bytes, part_number: int, retries: int
28
+ info: UploadInfo, chunk: FilePart, part_number: int, retries: int
29
29
  ) -> FinishedPiece:
30
- assert len(chunk) > 0
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 {len(chunk)}"
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
- return fp
65
- assert isinstance(fp.extra, S3FileInfo)
66
- extra: S3FileInfo = fp.extra
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.load(),
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
- self.payload = get_chunk_tmpdir() / f"{random_str(12)}.chunk"
281
- with _TMP_DIR_ACCESS_LOCK:
282
- if not self.payload.parent.exists():
283
- self.payload.parent.mkdir(parents=True)
284
- self.payload.write_bytes(payload)
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 close(self) -> None:
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.close()
361
+ self.dispose()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.3.18
3
+ Version: 1.3.20
4
4
  Summary: rclone api in python
5
5
  Home-page: https://github.com/zackees/rclone-api
6
6
  License: BSD 3-Clause License
@@ -1,4 +1,4 @@
1
- rclone_api/__init__.py,sha256=bJ6x-7ySj1kC7xjQJqEEA-0cr46RUh_tvIZsebGcyu4,1224
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=aBHzC-sqwPJ5fSI8bj9XOXJY_I6aCevnxXqiLscl4oo,5393
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=bi4N-VkyPdM4RWUYhIqJ71lQrXanck4PpF3pY8k2xnQ,4722
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=ogWjSt--Ph2dpeq31mWsBRBvKhpf1EF0jJD2HxgQ8T0,50903
20
- rclone_api/remote.py,sha256=O9WDUFQy9f6oT1HdUbTixK2eg0xtBBm8k4Xl6aa6K00,431
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=XiiWoGXAzNxTEfRcszw3BeRF7ZATXHIAPFg2-aJzUfo,9926
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=1yoom28TGwCKVkb9WFbgDTTYE4SxmYfALDEUTW8_Pms,10351
33
- rclone_api/db/models.py,sha256=unKEiu8l4R4UAEoncEbOHEda227DpXm-cWwRV6vwJXE,1657
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=nZtqMukLhSzHq64Pn1I8pXwjoraqWjCKey3WLAeubx0,9069
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=plrQyjuHFdJfV55kKH4wqo6QGll9n4_7BO4B1A7j6kY,7121
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=d8ZWqO8n9wsqRF6JjmvAFmG1aCkFqdSB1L8yxe_5qiY,11669
44
- rclone_api-1.3.18.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
45
- rclone_api-1.3.18.dist-info/METADATA,sha256=ow95lJm4jG8h05LuHtbeagZgL8R_qb7dqtwYU9s0cV4,4598
46
- rclone_api-1.3.18.dist-info/WHEEL,sha256=rF4EZyR2XVS6irmOHQIJx2SUqXLZKRMUrjsg8UwN-XQ,109
47
- rclone_api-1.3.18.dist-info/entry_points.txt,sha256=fJteOlYVwgX3UbNuL9jJ0zUTuX2O79JFAeNgK7Sw7EQ,255
48
- rclone_api-1.3.18.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
49
- rclone_api-1.3.18.dist-info/RECORD,,
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,,