rclone-api 1.0.68__py2.py3-none-any.whl → 1.0.70__py2.py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rclone-api might be problematic. Click here for more details.

@@ -0,0 +1,123 @@
1
+ import time
2
+ from concurrent.futures import ThreadPoolExecutor
3
+ from queue import Empty, Queue
4
+ from threading import Thread
5
+ from typing import Generator
6
+
7
+ from rclone_api import Dir
8
+ from rclone_api.dir_listing import DirListing
9
+ from rclone_api.types import ListingOption
10
+ from rclone_api.walk import walk_runner_depth_first
11
+
12
+ _MAX_OUT_QUEUE_SIZE = 50
13
+
14
+
15
+ # ONLY Works from src -> dst diffing.
16
+ def _async_diff_dir_walk_task(
17
+ src: Dir, dst: Dir, max_depth: int, out_queue: Queue[Dir | None], reverse
18
+ ) -> None:
19
+ curr_src, curr_dst = src, dst
20
+ with ThreadPoolExecutor(max_workers=2) as executor:
21
+ # src_dir_listing = src.ls(listing_option=ListingOption.DIRS_ONLY)
22
+ # dst_dir_listing = dst.ls(listing_option=ListingOption.DIRS_ONLY)
23
+ t1 = executor.submit(
24
+ src.ls, listing_option=ListingOption.DIRS_ONLY, reverse=reverse
25
+ )
26
+ t2 = executor.submit(
27
+ dst.ls, listing_option=ListingOption.DIRS_ONLY, reverse=reverse
28
+ )
29
+ src_dir_listing: DirListing = t1.result()
30
+ dst_dir_listing: DirListing = t2.result()
31
+ next_depth = max_depth - 1 if max_depth > 0 else max_depth
32
+ dst_files: list[str] = [d.name for d in dst_dir_listing.dirs]
33
+ src_files: list[str] = [d.name for d in src_dir_listing.dirs]
34
+ dst_files_set: set[str] = set(dst_files)
35
+ matching_dirs: list[str] = []
36
+ for file in src_files:
37
+ if file not in dst_files_set:
38
+ # print(f"missing dir on src: {file}")
39
+ queue_dir_listing: Queue[DirListing | None] = Queue()
40
+ if next_depth > 0 or next_depth == -1:
41
+ walk_runner_depth_first(
42
+ dir=curr_src,
43
+ out_queue=queue_dir_listing,
44
+ reverse=reverse,
45
+ max_depth=next_depth,
46
+ )
47
+ while dirlisting := queue_dir_listing.get():
48
+ if dirlisting is None:
49
+ break
50
+ # print(f"dirlisting: {dirlisting}")
51
+ for d in dirlisting.dirs:
52
+ out_queue.put(d)
53
+ else:
54
+ matching_dirs.append(file)
55
+
56
+ for matching_dir in matching_dirs:
57
+ # print(f"matching dir: {matching_dir}")
58
+ if next_depth > 0 or next_depth == -1:
59
+ src_next = curr_src / matching_dir
60
+ dst_next = curr_dst / matching_dir
61
+ _async_diff_dir_walk_task(
62
+ src=src_next,
63
+ dst=dst_next,
64
+ max_depth=next_depth,
65
+ out_queue=out_queue,
66
+ reverse=reverse,
67
+ )
68
+
69
+
70
+ def async_diff_dir_walk_task(
71
+ src: Dir, dst: Dir, max_depth: int, out_queue: Queue[Dir | None], reverse=False
72
+ ) -> None:
73
+ try:
74
+ _async_diff_dir_walk_task(src, dst, max_depth, out_queue, reverse)
75
+ except Exception:
76
+ import _thread
77
+
78
+ _thread.interrupt_main()
79
+ raise
80
+ finally:
81
+ out_queue.put(None)
82
+
83
+
84
+ def diff_walk(
85
+ src: Dir,
86
+ dst: Dir,
87
+ max_depth: int = -1,
88
+ reverse: bool = False,
89
+ ) -> Generator[Dir, None, None]:
90
+ """Walk through the given directory recursively.
91
+
92
+ Args:
93
+ dir: Directory or Remote to walk through
94
+ max_depth: Maximum depth to traverse (-1 for unlimited)
95
+
96
+ Yields:
97
+ DirListing: Directory listing for each directory encountered
98
+ """
99
+
100
+ try:
101
+ out_queue: Queue[Dir | None] = Queue(maxsize=_MAX_OUT_QUEUE_SIZE)
102
+
103
+ def task() -> None:
104
+ async_diff_dir_walk_task(src, dst, max_depth, out_queue, reverse=reverse)
105
+
106
+ worker = Thread(
107
+ target=task,
108
+ daemon=True,
109
+ )
110
+ worker.start()
111
+
112
+ while True:
113
+ try:
114
+ dirlisting = out_queue.get_nowait()
115
+ if dirlisting is None:
116
+ break
117
+ yield dirlisting
118
+ except Empty:
119
+ time.sleep(0.1)
120
+
121
+ worker.join()
122
+ except KeyboardInterrupt:
123
+ pass
rclone_api/dir.py CHANGED
@@ -1,9 +1,11 @@
1
1
  import json
2
+ from pathlib import Path
2
3
  from typing import Generator
3
4
 
4
5
  from rclone_api.dir_listing import DirListing
5
6
  from rclone_api.remote import Remote
6
7
  from rclone_api.rpath import RPath
8
+ from rclone_api.types import ListingOption
7
9
 
8
10
 
9
11
  class Dir:
@@ -41,11 +43,30 @@ class Dir:
41
43
  # self.path.set_rclone(self.path.remote.rclone)
42
44
  assert self.path.rclone is not None
43
45
 
44
- def ls(self, max_depth: int = 0, reverse: bool = False) -> DirListing:
46
+ def ls(
47
+ self,
48
+ max_depth: int | None = None,
49
+ glob: str | None = None,
50
+ reverse: bool = False,
51
+ listing_option: ListingOption = ListingOption.ALL,
52
+ ) -> DirListing:
45
53
  """List files and directories in the given path."""
46
54
  assert self.path.rclone is not None
47
55
  dir = Dir(self.path)
48
- return self.path.rclone.ls(dir, max_depth=max_depth, reverse=reverse)
56
+ return self.path.rclone.ls(
57
+ dir,
58
+ max_depth=max_depth,
59
+ glob=glob,
60
+ reverse=reverse,
61
+ listing_option=listing_option,
62
+ )
63
+
64
+ def relative_to(self, other: "Dir") -> str:
65
+ """Return the relative path to the other directory."""
66
+ self_path = Path(self.path.path)
67
+ other_path = Path(other.path.path)
68
+ rel_path = self_path.relative_to(other_path)
69
+ return str(rel_path)
49
70
 
50
71
  def walk(
51
72
  self, breadth_first: bool, max_depth: int = -1
@@ -67,3 +88,26 @@ class Dir:
67
88
  data = self.path.to_json()
68
89
  data_str = json.dumps(data)
69
90
  return data_str
91
+
92
+ def to_string(self, include_remote: bool = True) -> str:
93
+ """Convert the File to a string."""
94
+ out = str(self.path)
95
+ if not include_remote:
96
+ _, out = out.split(":", 1)
97
+ return out
98
+
99
+ # / operator
100
+ def __truediv__(self, other: str) -> "Dir":
101
+ """Join the current path with another path."""
102
+ path = Path(self.path.path) / other
103
+ rpath = RPath(
104
+ self.path.remote,
105
+ str(path.as_posix()),
106
+ name=other,
107
+ size=0,
108
+ mime_type="inode/directory",
109
+ mod_time="",
110
+ is_dir=True,
111
+ )
112
+ rpath.set_rclone(self.path.rclone)
113
+ return Dir(rpath)
rclone_api/rclone.py CHANGED
@@ -7,7 +7,6 @@ import subprocess
7
7
  import time
8
8
  import warnings
9
9
  from concurrent.futures import Future, ThreadPoolExecutor
10
- from enum import Enum
11
10
  from fnmatch import fnmatch
12
11
  from pathlib import Path
13
12
  from tempfile import TemporaryDirectory
@@ -26,6 +25,7 @@ from rclone_api.group_files import group_files
26
25
  from rclone_api.process import Process
27
26
  from rclone_api.remote import Remote
28
27
  from rclone_api.rpath import RPath
28
+ from rclone_api.types import ListingOption, ModTimeStrategy
29
29
  from rclone_api.util import (
30
30
  get_check,
31
31
  get_rclone_exe,
@@ -42,17 +42,6 @@ def rclone_verbose(verbose: bool | None) -> bool:
42
42
  return bool(int(os.getenv("RCLONE_API_VERBOSE", "0")))
43
43
 
44
44
 
45
- class ModTimeStrategy(Enum):
46
- USE_SERVER_MODTIME = "use-server-modtime"
47
- NO_MODTIME = "no-modtime"
48
-
49
-
50
- class ListingOption(Enum):
51
- DIRS_ONLY = "dirs-only"
52
- FILES_ONLY = "files-only"
53
- ALL = "all"
54
-
55
-
56
45
  class Rclone:
57
46
  def __init__(
58
47
  self, rclone_conf: Path | Config, rclone_exe: Path | None = None
@@ -203,17 +192,20 @@ class Rclone:
203
192
  diff_option: DiffOption = DiffOption.COMBINED,
204
193
  fast_list: bool = True,
205
194
  size_only: bool | None = None,
195
+ checkers: int | None = None,
206
196
  other_args: list[str] | None = None,
207
197
  ) -> Generator[DiffItem, None, None]:
208
198
  """Be extra careful with the src and dst values. If you are off by one
209
199
  parent directory, you will get a huge amount of false diffs."""
210
200
  other_args = other_args or []
201
+ if checkers is None or checkers < 1:
202
+ checkers = 1000
211
203
  cmd = [
212
204
  "check",
213
205
  src,
214
206
  dst,
215
207
  "--checkers",
216
- "1000",
208
+ str(checkers),
217
209
  "--log-level",
218
210
  "INFO",
219
211
  f"--{diff_option.value}",
@@ -288,6 +280,33 @@ class Rclone:
288
280
  dir_obj, max_depth=max_depth, breadth_first=breadth_first, reverse=reverse
289
281
  )
290
282
 
283
+ def diff_walk(
284
+ self,
285
+ src: Dir | Remote | str,
286
+ dst: Dir | Remote | str,
287
+ max_depth: int = -1,
288
+ reverse: bool = False,
289
+ ) -> Generator[Dir, None, None]:
290
+ """Walk through the given path recursively.
291
+
292
+ WORK IN PROGRESS!!
293
+
294
+ Args:
295
+ src: Source directory or Remote to walk through
296
+ dst: Destination directory or Remote to walk through
297
+ max_depth: Maximum depth to traverse (-1 for unlimited)
298
+
299
+ Yields:
300
+ DirListing: Directory listing for each directory encountered
301
+ """
302
+ from rclone_api.diff_walk import diff_walk
303
+
304
+ src_dir = Dir(to_path(src, self))
305
+ dst_dir = Dir(to_path(dst, self))
306
+ yield from diff_walk(
307
+ src=src_dir, dst=dst_dir, max_depth=max_depth, reverse=reverse
308
+ )
309
+
291
310
  def cleanup(
292
311
  self, path: str, other_args: list[str] | None = None
293
312
  ) -> CompletedProcess:
rclone_api/types.py ADDED
@@ -0,0 +1,12 @@
1
+ from enum import Enum
2
+
3
+
4
+ class ModTimeStrategy(Enum):
5
+ USE_SERVER_MODTIME = "use-server-modtime"
6
+ NO_MODTIME = "no-modtime"
7
+
8
+
9
+ class ListingOption(Enum):
10
+ DIRS_ONLY = "dirs-only"
11
+ FILES_ONLY = "files-only"
12
+ ALL = "all"
rclone_api/walk.py CHANGED
@@ -9,7 +9,7 @@ from rclone_api.remote import Remote
9
9
  _MAX_OUT_QUEUE_SIZE = 50
10
10
 
11
11
 
12
- def _walk_runner_breadth_first(
12
+ def walk_runner_breadth_first(
13
13
  dir: Dir,
14
14
  max_depth: int,
15
15
  out_queue: Queue[DirListing | None],
@@ -40,7 +40,7 @@ def _walk_runner_breadth_first(
40
40
  _thread.interrupt_main()
41
41
 
42
42
 
43
- def _walk_runner_depth_first(
43
+ def walk_runner_depth_first(
44
44
  dir: Dir, max_depth: int, out_queue: Queue[DirListing | None], reverse=False
45
45
  ) -> None:
46
46
  try:
@@ -54,7 +54,7 @@ def _walk_runner_depth_first(
54
54
  for subdir in dirlisting.dirs: # Process deeper directories first
55
55
  # stack.append((child, depth - 1 if depth > 0 else depth))
56
56
  next_depth = depth - 1 if depth > 0 else depth
57
- _walk_runner_depth_first(
57
+ walk_runner_depth_first(
58
58
  subdir, next_depth, out_queue, reverse=reverse
59
59
  )
60
60
  out_queue.put(dirlisting)
@@ -85,16 +85,17 @@ def walk(
85
85
  # Convert Remote to Dir if needed
86
86
  if isinstance(dir, Remote):
87
87
  dir = Dir(dir)
88
- out_queue: Queue[DirListing] = Queue(maxsize=_MAX_OUT_QUEUE_SIZE)
88
+ out_queue: Queue[DirListing | None] = Queue(maxsize=_MAX_OUT_QUEUE_SIZE)
89
89
 
90
- strategy = (
91
- _walk_runner_breadth_first if breadth_first else _walk_runner_depth_first
92
- )
90
+ def _task() -> None:
91
+ if breadth_first:
92
+ walk_runner_breadth_first(dir, max_depth, out_queue, reverse)
93
+ else:
94
+ walk_runner_depth_first(dir, max_depth, out_queue, reverse)
93
95
 
94
96
  # Start worker thread
95
97
  worker = Thread(
96
- target=strategy,
97
- args=(dir, max_depth, out_queue, reverse),
98
+ target=_task,
98
99
  daemon=True,
99
100
  )
100
101
  worker.start()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.0.68
3
+ Version: 1.0.70
4
4
  Summary: rclone api in python
5
5
  Home-page: https://github.com/zackees/rclone-api
6
6
  Maintainer: Zachary Vorhies
@@ -5,23 +5,25 @@ rclone_api/config.py,sha256=tP6cU9DnCCEIRc_KP9HPur1jFLLg2QGFSxNwFm6_MVw,118
5
5
  rclone_api/convert.py,sha256=Mx9Qo7zhkOedJd8LdhPvNGHp8znJzOk4f_2KWnoGc78,1012
6
6
  rclone_api/deprecated.py,sha256=qWKpnZdYcBK7YQZKuVoWWXDwi-uqiAtbjgPcci_efow,590
7
7
  rclone_api/diff.py,sha256=ggdDLUZxa13jMcPzKBcwAElmPCNWMOSR89D4yhpO74M,5264
8
- rclone_api/dir.py,sha256=2-PFaDpjEs28z82DQ-TyaCgrm_OgpwlkwfTnx1-Wwpk,2194
8
+ rclone_api/diff_walk.py,sha256=fnCjzenKBBocu6sEd9CDEcwNqCUYwz3rZsxUuKNaYEg,4049
9
+ rclone_api/dir.py,sha256=BuXPd-bAvZund8k7mXjvx_UylsPFwbWq2zaUdflTX04,3520
9
10
  rclone_api/dir_listing.py,sha256=9Qqf2SUswrOEkyqmaH23V51I18X6ePiXb9B1vUwRF5o,1571
10
11
  rclone_api/exec.py,sha256=1ovvaMXDEfLiT7BrYZyE85u_yFhEUwUNW3jPOzqknR8,1023
11
12
  rclone_api/file.py,sha256=YtR5Y6c0YfXTS-sReOy2UgiSnafcAeO6b2hnbojBQD4,1423
12
13
  rclone_api/filelist.py,sha256=xbiusvNgaB_b_kQOZoHMJJxn6TWGtPrWd2J042BI28o,767
13
14
  rclone_api/group_files.py,sha256=kOHh6ysFDkxjldSwvW6KqmiADUC1yFCdrZRY57TvbGY,5328
14
15
  rclone_api/process.py,sha256=RrMfTe0bndmJ6gBK67ioqNvCstJ8aTC8RlGX1XBLlcw,4191
15
- rclone_api/rclone.py,sha256=Hnk74ZyCnxSkeNIGRfZLqtGdWHTzPRye2lzRah6vLeI,28071
16
+ rclone_api/rclone.py,sha256=nN1bqUmiopCKP6VNG4XpSfBfWx4EuAIyxB9NevYp-OA,28854
16
17
  rclone_api/remote.py,sha256=c9hlRKBCg1BFB9MCINaQIoCg10qyAkeqiS4brl8ce-8,343
17
18
  rclone_api/rpath.py,sha256=8ZA_1wxWtskwcy0I8V2VbjKDmzPkiWd8Q2JQSvh-sYE,2586
19
+ rclone_api/types.py,sha256=VXA-56va2S41onAWM_UmhHlG4qeyMUEF5dsVFyfLlH8,232
18
20
  rclone_api/util.py,sha256=_cvmHcJPRl2yXw4zgZiop3z-riA_8Ek6S5NDPw8cqSY,4198
19
- rclone_api/walk.py,sha256=UaNOE3ICd8k5ouSFZvkVEH4r2GnnrD9TxfwkFcQnayo,3170
21
+ rclone_api/walk.py,sha256=ndWV7WBVQLbpZu3HuJrAe1cXcmQVjT9_YPsbat158bQ,3231
20
22
  rclone_api/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
21
23
  rclone_api/cmd/list_files.py,sha256=x8FHODEilwKqwdiU1jdkeJbLwOqUkUQuDWPo2u_zpf0,741
22
- rclone_api-1.0.68.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
23
- rclone_api-1.0.68.dist-info/METADATA,sha256=p3oHKpaFeHKBgQhxdAqBHrHVBwzm7k_VuL8W8oYmx9U,4489
24
- rclone_api-1.0.68.dist-info/WHEEL,sha256=9Hm2OB-j1QcCUq9Jguht7ayGIIZBRTdOXD1qg9cCgPM,109
25
- rclone_api-1.0.68.dist-info/entry_points.txt,sha256=XUoTX3m7CWxdj2VAKhEuO0NMOfX2qf-OcEDFwdyk9ZE,72
26
- rclone_api-1.0.68.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
27
- rclone_api-1.0.68.dist-info/RECORD,,
24
+ rclone_api-1.0.70.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
25
+ rclone_api-1.0.70.dist-info/METADATA,sha256=73129SIcLoTZ3R35zB0vuZZ65SC0RBttGk1LtPARAx8,4489
26
+ rclone_api-1.0.70.dist-info/WHEEL,sha256=9Hm2OB-j1QcCUq9Jguht7ayGIIZBRTdOXD1qg9cCgPM,109
27
+ rclone_api-1.0.70.dist-info/entry_points.txt,sha256=XUoTX3m7CWxdj2VAKhEuO0NMOfX2qf-OcEDFwdyk9ZE,72
28
+ rclone_api-1.0.70.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
29
+ rclone_api-1.0.70.dist-info/RECORD,,