rclone-api 1.1.43__py2.py3-none-any.whl → 1.1.45__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/mount.py CHANGED
@@ -31,7 +31,13 @@ class Mount:
31
31
  if self._closed:
32
32
  return
33
33
  self._closed = True
34
- clean_mount(self, verbose=False)
34
+ clean_mount(self, verbose=False, wait=wait)
35
+
36
+ def __enter__(self) -> "Mount":
37
+ return self
38
+
39
+ def __exit__(self, exc_type, exc_value, traceback) -> None:
40
+ self.close(wait=True)
35
41
 
36
42
  def __del__(self):
37
43
  self.close(wait=False)
@@ -3,6 +3,7 @@ Unit test file.
3
3
  """
4
4
 
5
5
  import os
6
+ import shutil
6
7
  import time
7
8
  from dataclasses import dataclass
8
9
  from pathlib import Path
@@ -103,10 +104,11 @@ def _init() -> None:
103
104
  os.environ["RCLONE_API_VERBOSE"] = "1"
104
105
 
105
106
 
106
- def _run_profile(rclone: Rclone, src_file: str, transfers: int, size: int) -> None:
107
- mount_log = (
108
- Path("logs") / "mount" / f"mount_{SizeSuffix(size)}_threads_{transfers}.log"
109
- )
107
+ def _run_profile(
108
+ rclone: Rclone, src_file: str, transfers: int, size: int, log_dir: Path
109
+ ) -> None:
110
+
111
+ mount_log = log_dir / f"mount_{SizeSuffix(size)}_threads_{transfers}.log"
110
112
  print("\n\n")
111
113
  print("#" * 80)
112
114
  print(f"# Started test download of {SizeSuffix(size)} with {transfers} transfers")
@@ -177,10 +179,18 @@ def test_profile_copy_bytes() -> None:
177
179
  # sftp mount
178
180
  src_file = "src:aa_misc_data/aa_misc_data/world_lending_library_2024_11.tar.zst"
179
181
 
182
+ mount_root_path = Path("logs") / "mount"
183
+ if mount_root_path.exists():
184
+ shutil.rmtree(mount_root_path)
185
+
180
186
  for size in sizes:
181
187
  for transfers in transfer_list:
182
188
  _run_profile(
183
- rclone=rclone, src_file=src_file, transfers=transfers, size=size
189
+ rclone=rclone,
190
+ src_file=src_file,
191
+ transfers=transfers,
192
+ size=size,
193
+ log_dir=mount_root_path,
184
194
  )
185
195
  print("done")
186
196
 
rclone_api/rclone.py CHANGED
@@ -832,7 +832,10 @@ class Rclone:
832
832
  tmp_mnt = Path("tmp_mnt") / random_str(12)
833
833
  src_parent_path = Path(src).parent.as_posix()
834
834
  src_file = Path(src).name
835
- other_args: list[str] = ["--no-modtime", "--vfs-read-wait", "1s"]
835
+ other_args: list[str] = [
836
+ "--no-modtime",
837
+ # "--vfs-read-wait", "1s"
838
+ ]
836
839
  vfs_read_chunk_size = SizeSuffix(length // transfers)
837
840
  vfs_read_chunk_size_limit = SizeSuffix(length)
838
841
  vfs_read_chunk_streams = transfers
@@ -924,10 +927,11 @@ class Rclone:
924
927
 
925
928
  allow_writes = allow_writes or False
926
929
  use_links = use_links or True
927
- verbose = get_verbose(verbose)
930
+ verbose = get_verbose(verbose) or (log is not None)
928
931
  vfs_cache_mode = vfs_cache_mode or "full"
929
932
  clean_mount(outdir, verbose=verbose)
930
933
  prepare_mount(outdir, verbose=verbose)
934
+ debug_fuse = log is not None
931
935
  src_str = convert_to_str(src)
932
936
  cmd_list: list[str] = ["mount", src_str, str(outdir)]
933
937
  if not allow_writes:
@@ -937,12 +941,14 @@ class Rclone:
937
941
  if vfs_cache_mode:
938
942
  cmd_list.append("--vfs-cache-mode")
939
943
  cmd_list.append(vfs_cache_mode)
944
+ if debug_fuse:
945
+ cmd_list.append("--debug-fuse")
940
946
  if verbose:
941
947
  cmd_list.append("-vvvv")
942
948
  if other_args:
943
949
  cmd_list += other_args
944
950
  proc = self._launch_process(cmd_list, log=log)
945
- mount_read_only = "--read-only" in cmd_list # hint to allow fast teardown
951
+ mount_read_only = not allow_writes
946
952
  mount: Mount = Mount(mount_path=outdir, process=proc, read_only=mount_read_only)
947
953
  return mount
948
954
 
@@ -960,7 +966,6 @@ class Rclone:
960
966
  ) -> Generator[Mount, None, None]:
961
967
  """Like mount, but can be used in a context manager."""
962
968
  error_happened = False
963
- verbose = get_verbose(verbose)
964
969
  mount: Mount = self.mount(
965
970
  src,
966
971
  outdir,
@@ -979,7 +984,7 @@ class Rclone:
979
984
  warnings.warn(f"Error in scoped_mount: {e}\n\nStack Trace:\n{stack_trace}")
980
985
  raise
981
986
  finally:
982
- if not error_happened:
987
+ if not error_happened or (not allow_writes):
983
988
  mount.close()
984
989
 
985
990
  # Settings optimized for s3.
@@ -2,6 +2,7 @@ import time
2
2
  import warnings
3
3
  from pathlib import Path
4
4
  from queue import Queue
5
+ from threading import Event
5
6
 
6
7
  from rclone_api.s3.chunk_types import FileChunk, UploadState
7
8
  from rclone_api.util import locked_print
@@ -25,12 +26,16 @@ def _get_file_size(file_path: Path, timeout: int = 60) -> int:
25
26
 
26
27
 
27
28
  def file_chunker(
28
- upload_state: UploadState, max_chunks: int | None, output: Queue[FileChunk | None]
29
+ upload_state: UploadState,
30
+ max_chunks: int | None,
31
+ cancel_signal: Event,
32
+ output: Queue[FileChunk | None],
29
33
  ) -> None:
30
34
  count = 0
31
35
 
32
36
  def should_stop() -> bool:
33
37
  nonlocal count
38
+
34
39
  if max_chunks is None:
35
40
  return False
36
41
  if count >= max_chunks:
@@ -68,6 +73,12 @@ def file_chunker(
68
73
  return None
69
74
  return part_number
70
75
 
76
+ if cancel_signal.is_set():
77
+ print(
78
+ f"Cancel signal is set for file chunker while processing {file_path}, returning"
79
+ )
80
+ return
81
+
71
82
  while not should_stop():
72
83
  curr_parth_num = next_part_number()
73
84
  if curr_parth_num is None:
@@ -5,7 +5,7 @@ import warnings
5
5
  from concurrent.futures import ThreadPoolExecutor
6
6
  from pathlib import Path
7
7
  from queue import Queue
8
- from threading import Thread
8
+ from threading import Event, Thread
9
9
 
10
10
  from botocore.client import BaseClient
11
11
 
@@ -207,16 +207,21 @@ def upload_file_multipart(
207
207
  upload_info = upload_state.upload_info
208
208
 
209
209
  chunker_errors: Queue[Exception] = Queue()
210
+ cancel_chunker_event = Event()
210
211
 
211
212
  def chunker_task(
212
213
  upload_state=upload_state,
213
214
  output=filechunks,
214
215
  max_chunks=max_chunks_before_suspension,
216
+ cancel_signal=cancel_chunker_event,
215
217
  queue_errors=chunker_errors,
216
218
  ) -> None:
217
219
  try:
218
220
  file_chunker(
219
- upload_state=upload_state, output=output, max_chunks=max_chunks
221
+ upload_state=upload_state,
222
+ output=output,
223
+ max_chunks=max_chunks,
224
+ cancel_signal=cancel_signal,
220
225
  )
221
226
  except Exception as e:
222
227
  queue_errors.put(e)
@@ -228,25 +233,30 @@ def upload_file_multipart(
228
233
  thread_chunker.start()
229
234
 
230
235
  with ThreadPoolExecutor(max_workers=upload_threads) as executor:
231
- while True:
232
- file_chunk: FileChunk | None = filechunks.get()
233
- if file_chunk is None:
234
- break
236
+ try:
237
+ while True:
238
+ file_chunk: FileChunk | None = filechunks.get()
239
+ if file_chunk is None:
240
+ break
235
241
 
236
- def task(upload_info=upload_info, file_chunk=file_chunk):
237
- return handle_upload(upload_info, file_chunk)
242
+ def task(upload_info=upload_info, file_chunk=file_chunk):
243
+ return handle_upload(upload_info, file_chunk)
238
244
 
239
- fut = executor.submit(task)
245
+ fut = executor.submit(task)
240
246
 
241
- def done_cb(fut=fut):
242
- result = fut.result()
243
- if isinstance(result, Exception):
244
- warnings.warn(f"Error uploading part: {result}, skipping")
245
- return
246
- # upload_state.finished_parts.put(result)
247
- upload_state.add_finished(result)
247
+ def done_cb(fut=fut):
248
+ result = fut.result()
249
+ if isinstance(result, Exception):
250
+ warnings.warn(f"Error uploading part: {result}, skipping")
251
+ return
252
+ # upload_state.finished_parts.put(result)
253
+ upload_state.add_finished(result)
248
254
 
249
- fut.add_done_callback(done_cb)
255
+ fut.add_done_callback(done_cb)
256
+ except Exception:
257
+ cancel_chunker_event.set()
258
+ executor.shutdown(wait=False, cancel_futures=True)
259
+ raise
250
260
  # upload_state.finished_parts.put(None) # Signal the end of the queue
251
261
  upload_state.add_finished(None)
252
262
  thread_chunker.join()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.1.43
3
+ Version: 1.1.45
4
4
  Summary: rclone api in python
5
5
  Home-page: https://github.com/zackees/rclone-api
6
6
  License: BSD 3-Clause License
@@ -11,9 +11,9 @@ rclone_api/exec.py,sha256=Pd7pUBd8ib5MzqvMybG2DQISPRbDRu20VjVRL2mLAVY,1076
11
11
  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
- rclone_api/mount.py,sha256=ryAjkX4_kFeFZWLiBVpcGy2VilpvVhFbWeWfEX4jMKs,6104
14
+ rclone_api/mount.py,sha256=RgzGfJakHxgiTsLu0bFX4ZYIzr4GAdIH8CMlz-M-hsY,6267
15
15
  rclone_api/process.py,sha256=rBj_S86jC6nqCYop-jq8r9eMSteKeObxUrJMgH8LZvI,5084
16
- rclone_api/rclone.py,sha256=J_8UNI1aVARaWA3_EMoCU0z_oabt05nstcWD2hEHsCk,42829
16
+ rclone_api/rclone.py,sha256=-cMgLNykn4JFP83-sFeVWArFl28173LXgdWvpPr0_rE,42933
17
17
  rclone_api/remote.py,sha256=O9WDUFQy9f6oT1HdUbTixK2eg0xtBBm8k4Xl6aa6K00,431
18
18
  rclone_api/rpath.py,sha256=8ZA_1wxWtskwcy0I8V2VbjKDmzPkiWd8Q2JQSvh-sYE,2586
19
19
  rclone_api/scan_missing_folders.py,sha256=Kulca2Q6WZodt00ATFHkmqqInuoPvBkhTcS9703y6po,4740
@@ -25,17 +25,17 @@ rclone_api/cmd/copy_large_s3.py,sha256=-rfedi-ZzPUdCSP8ai9LRL0y1xVkvN-viQQlk8HVU
25
25
  rclone_api/cmd/list_files.py,sha256=x8FHODEilwKqwdiU1jdkeJbLwOqUkUQuDWPo2u_zpf0,741
26
26
  rclone_api/experimental/flags.py,sha256=qCVD--fSTmzlk9hloRLr0q9elzAOFzPsvVpKM3aB1Mk,2739
27
27
  rclone_api/experimental/flags_base.py,sha256=ajU_czkTcAxXYU-SlmiCfHY7aCQGHvpCLqJ-Z8uZLk0,2102
28
- rclone_api/profile/mount_copy_bytes.py,sha256=Jsvb49CNTvkdSAkIAKDxBJ5TzAk7-dOuZdDrigiTjsY,5684
28
+ rclone_api/profile/mount_copy_bytes.py,sha256=6hJJ7jo8brFzq-t96JCjQeCIYnzYT1Km9vRL1jbUyLo,5897
29
29
  rclone_api/s3/api.py,sha256=qxtRDUpHYqJ7StJRtP8U_PbF_BvYRg705568SyvF-R0,3770
30
30
  rclone_api/s3/basic_ops.py,sha256=hK3366xhVEzEcjz9Gk_8lFx6MRceAk72cax6mUrr6ko,2104
31
- rclone_api/s3/chunk_file.py,sha256=YELR-EzR7RHpzCDGpYdzlwu21NZW5wttIDvLoONI4aU,3477
31
+ rclone_api/s3/chunk_file.py,sha256=D6wM9Nuu73LNGC8JCCfevqjF3qdZ21mQxYQClFLZLMU,3726
32
32
  rclone_api/s3/chunk_types.py,sha256=LbXayXY1KgVU1LkdbASD_BQ7TpVpwVnzMjtz--8LBaE,10316
33
33
  rclone_api/s3/create.py,sha256=wgfkapv_j904CfKuWyiBIWJVxfAx_ftemFSUV14aT68,3149
34
34
  rclone_api/s3/types.py,sha256=yBnJ38Tjk6RlydJ-sqZ7DSfyFloy8KDYJ0mv3vlOzLE,1388
35
- rclone_api/s3/upload_file_multipart.py,sha256=1jQAdk35Fa9Tcq36bS65262cs7AcNG2DAFQ-NdYlWSw,9961
36
- rclone_api-1.1.43.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
37
- rclone_api-1.1.43.dist-info/METADATA,sha256=cL7VgGdbjfGJSJ7f3UO8ON4ucWQ_stuhj9vyKIUkAwE,4537
38
- rclone_api-1.1.43.dist-info/WHEEL,sha256=rF4EZyR2XVS6irmOHQIJx2SUqXLZKRMUrjsg8UwN-XQ,109
39
- rclone_api-1.1.43.dist-info/entry_points.txt,sha256=TV8kwP3FRzYwUEr0RLC7aJh0W03SAefIJNXTJ-FdMIQ,200
40
- rclone_api-1.1.43.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
41
- rclone_api-1.1.43.dist-info/RECORD,,
35
+ rclone_api/s3/upload_file_multipart.py,sha256=y9azNAU8QH5Ovwz33V2HZwNmJdlFjJg-jrXLZ1gtMds,10364
36
+ rclone_api-1.1.45.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
37
+ rclone_api-1.1.45.dist-info/METADATA,sha256=ZLx1uXz3hoeN194vwCxzj98SdfbBtdfRAeDtMKqafE0,4537
38
+ rclone_api-1.1.45.dist-info/WHEEL,sha256=rF4EZyR2XVS6irmOHQIJx2SUqXLZKRMUrjsg8UwN-XQ,109
39
+ rclone_api-1.1.45.dist-info/entry_points.txt,sha256=TV8kwP3FRzYwUEr0RLC7aJh0W03SAefIJNXTJ-FdMIQ,200
40
+ rclone_api-1.1.45.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
41
+ rclone_api-1.1.45.dist-info/RECORD,,