rclone-api 1.0.69__py2.py3-none-any.whl → 1.0.72__py2.py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
rclone_api/__init__.py CHANGED
@@ -1,14 +1,15 @@
1
1
  from .completed_process import CompletedProcess
2
2
  from .config import Config
3
- from .diff import DiffItem, DiffType
3
+ from .diff import DiffItem, DiffOption, DiffType
4
4
  from .dir import Dir
5
5
  from .dir_listing import DirListing
6
6
  from .file import File
7
7
  from .filelist import FileList
8
8
  from .process import Process
9
- from .rclone import DiffOption, ListingOption, Rclone, rclone_verbose
9
+ from .rclone import Rclone, rclone_verbose
10
10
  from .remote import Remote
11
11
  from .rpath import RPath
12
+ from .types import ListingOption, Order
12
13
 
13
14
  __all__ = [
14
15
  "Rclone",
@@ -26,4 +27,6 @@ __all__ = [
26
27
  "CompletedProcess",
27
28
  "DiffOption",
28
29
  "ListingOption",
30
+ "Order",
31
+ "ListingOption",
29
32
  ]
rclone_api/dir.py CHANGED
@@ -5,7 +5,7 @@ from typing import Generator
5
5
  from rclone_api.dir_listing import DirListing
6
6
  from rclone_api.remote import Remote
7
7
  from rclone_api.rpath import RPath
8
- from rclone_api.types import ListingOption
8
+ from rclone_api.types import ListingOption, Order
9
9
 
10
10
 
11
11
  class Dir:
@@ -47,7 +47,7 @@ class Dir:
47
47
  self,
48
48
  max_depth: int | None = None,
49
49
  glob: str | None = None,
50
- reverse: bool = False,
50
+ order: Order = Order.NORMAL,
51
51
  listing_option: ListingOption = ListingOption.ALL,
52
52
  ) -> DirListing:
53
53
  """List files and directories in the given path."""
@@ -57,7 +57,7 @@ class Dir:
57
57
  dir,
58
58
  max_depth=max_depth,
59
59
  glob=glob,
60
- reverse=reverse,
60
+ order=order,
61
61
  listing_option=listing_option,
62
62
  )
63
63
 
@@ -102,7 +102,7 @@ class Dir:
102
102
  path = Path(self.path.path) / other
103
103
  rpath = RPath(
104
104
  self.path.remote,
105
- str(path),
105
+ str(path.as_posix()),
106
106
  name=other,
107
107
  size=0,
108
108
  mime_type="inode/directory",
rclone_api/rclone.py CHANGED
@@ -3,6 +3,7 @@ Unit test file.
3
3
  """
4
4
 
5
5
  import os
6
+ import random
6
7
  import subprocess
7
8
  import time
8
9
  import warnings
@@ -25,7 +26,7 @@ from rclone_api.group_files import group_files
25
26
  from rclone_api.process import Process
26
27
  from rclone_api.remote import Remote
27
28
  from rclone_api.rpath import RPath
28
- from rclone_api.types import ListingOption, ModTimeStrategy
29
+ from rclone_api.types import ListingOption, ModTimeStrategy, Order
29
30
  from rclone_api.util import (
30
31
  get_check,
31
32
  get_rclone_exe,
@@ -118,7 +119,7 @@ class Rclone:
118
119
  path: Dir | Remote | str,
119
120
  max_depth: int | None = None,
120
121
  glob: str | None = None,
121
- reverse: bool = False,
122
+ order: Order = Order.NORMAL,
122
123
  listing_option: ListingOption = ListingOption.ALL,
123
124
  ) -> DirListing:
124
125
  """List files in the given path.
@@ -164,8 +165,10 @@ class Rclone:
164
165
  if glob is not None:
165
166
  paths = [p for p in paths if fnmatch(p.path, glob)]
166
167
 
167
- if reverse:
168
+ if order == Order.REVERSE:
168
169
  paths.reverse()
170
+ elif order == Order.RANDOM:
171
+ random.shuffle(paths)
169
172
  return DirListing(paths)
170
173
 
171
174
  def listremotes(self) -> list[Remote]:
@@ -242,7 +245,7 @@ class Rclone:
242
245
  path: Dir | Remote | str,
243
246
  max_depth: int = -1,
244
247
  breadth_first: bool = True,
245
- reverse: bool = False,
248
+ order: Order = Order.NORMAL,
246
249
  ) -> Generator[DirListing, None, None]:
247
250
  """Walk through the given path recursively.
248
251
 
@@ -277,15 +280,15 @@ class Rclone:
277
280
  assert f"Invalid type for path: {type(path)}"
278
281
 
279
282
  yield from walk(
280
- dir_obj, max_depth=max_depth, breadth_first=breadth_first, reverse=reverse
283
+ dir_obj, max_depth=max_depth, breadth_first=breadth_first, order=order
281
284
  )
282
285
 
283
- def diff_walk(
286
+ def scan_missing_folders(
284
287
  self,
285
288
  src: Dir | Remote | str,
286
289
  dst: Dir | Remote | str,
287
290
  max_depth: int = -1,
288
- reverse: bool = False,
291
+ order: Order = Order.NORMAL,
289
292
  ) -> Generator[Dir, None, None]:
290
293
  """Walk through the given path recursively.
291
294
 
@@ -299,12 +302,12 @@ class Rclone:
299
302
  Yields:
300
303
  DirListing: Directory listing for each directory encountered
301
304
  """
302
- from rclone_api.diff_walk import diff_walk
305
+ from rclone_api.scan_missing_folders import scan_missing_folders
303
306
 
304
307
  src_dir = Dir(to_path(src, self))
305
308
  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
309
+ yield from scan_missing_folders(
310
+ src=src_dir, dst=dst_dir, max_depth=max_depth, order=order
308
311
  )
309
312
 
310
313
  def cleanup(
@@ -426,10 +429,6 @@ class Rclone:
426
429
  chunk = files_fqdn[i : i + chunk_size]
427
430
  files_str = "\n".join(chunk)
428
431
  print(f"{files_str}")
429
- # files_str = "\n".join(files_fqdn)
430
- # print(f"Copying {nfiles} files: \n{files_str}")
431
-
432
- # print(include_files_txt)
433
432
  cmd_list: list[str] = [
434
433
  "copy",
435
434
  src_path,
@@ -0,0 +1,135 @@
1
+ import random
2
+ import time
3
+ from concurrent.futures import ThreadPoolExecutor
4
+ from queue import Empty, Queue
5
+ from threading import Thread
6
+ from typing import Generator
7
+
8
+ from rclone_api import Dir
9
+ from rclone_api.dir_listing import DirListing
10
+ from rclone_api.types import ListingOption, Order
11
+ from rclone_api.walk import walk_runner_depth_first
12
+
13
+ _MAX_OUT_QUEUE_SIZE = 50
14
+
15
+
16
+ # ONLY Works from src -> dst diffing.
17
+ def _async_diff_dir_walk_task(
18
+ src: Dir, dst: Dir, max_depth: int, out_queue: Queue[Dir | None], order: Order
19
+ ) -> None:
20
+ curr_src, curr_dst = src, dst
21
+ with ThreadPoolExecutor(max_workers=2) as executor:
22
+ t1 = executor.submit(
23
+ src.ls, listing_option=ListingOption.DIRS_ONLY, order=order
24
+ )
25
+ t2 = executor.submit(
26
+ dst.ls, listing_option=ListingOption.DIRS_ONLY, order=order
27
+ )
28
+ src_dir_listing: DirListing = t1.result()
29
+ dst_dir_listing: DirListing = t2.result()
30
+ next_depth = max_depth - 1 if max_depth > 0 else max_depth
31
+ dst_dirs: list[str] = [d.name for d in dst_dir_listing.dirs]
32
+ src_dirs: list[str] = [d.name for d in src_dir_listing.dirs]
33
+ dst_files_set: set[str] = set(dst_dirs)
34
+ matching_dirs: list[str] = []
35
+ if order == Order.REVERSE:
36
+ src_dirs.reverse()
37
+ dst_dirs.reverse()
38
+ elif order == Order.RANDOM:
39
+ random.shuffle(src_dirs)
40
+ random.shuffle(dst_dirs)
41
+ for file in src_dirs:
42
+ if file not in dst_files_set:
43
+ queue_dir_listing: Queue[DirListing | None] = Queue()
44
+ if next_depth > 0 or next_depth == -1:
45
+ walk_runner_depth_first(
46
+ dir=curr_src,
47
+ out_queue=queue_dir_listing,
48
+ order=order,
49
+ max_depth=next_depth,
50
+ )
51
+ while dirlisting := queue_dir_listing.get():
52
+ if dirlisting is None:
53
+ break
54
+ # print(f"dirlisting: {dirlisting}")
55
+ for d in dirlisting.dirs:
56
+ out_queue.put(d)
57
+ else:
58
+ matching_dirs.append(file)
59
+
60
+ for matching_dir in matching_dirs:
61
+ # print(f"matching dir: {matching_dir}")
62
+ if next_depth > 0 or next_depth == -1:
63
+ src_next = curr_src / matching_dir
64
+ dst_next = curr_dst / matching_dir
65
+ _async_diff_dir_walk_task(
66
+ src=src_next,
67
+ dst=dst_next,
68
+ max_depth=next_depth,
69
+ out_queue=out_queue,
70
+ order=order,
71
+ )
72
+
73
+
74
+ def async_diff_dir_walk_task(
75
+ src: Dir, dst: Dir, max_depth: int, out_queue: Queue[Dir | None], order: Order
76
+ ) -> None:
77
+ try:
78
+ _async_diff_dir_walk_task(
79
+ src=src, dst=dst, max_depth=max_depth, out_queue=out_queue, order=order
80
+ )
81
+ except Exception:
82
+ import _thread
83
+
84
+ _thread.interrupt_main()
85
+ raise
86
+ finally:
87
+ out_queue.put(None)
88
+
89
+
90
+ def scan_missing_folders(
91
+ src: Dir,
92
+ dst: Dir,
93
+ max_depth: int = -1,
94
+ order: Order = Order.NORMAL,
95
+ ) -> Generator[Dir, None, None]:
96
+ """Walk through the given directory recursively.
97
+
98
+ Args:
99
+ dir: Directory or Remote to walk through
100
+ max_depth: Maximum depth to traverse (-1 for unlimited)
101
+
102
+ Yields:
103
+ DirListing: Directory listing for each directory encountered
104
+ """
105
+
106
+ try:
107
+ out_queue: Queue[Dir | None] = Queue(maxsize=_MAX_OUT_QUEUE_SIZE)
108
+
109
+ def task() -> None:
110
+ async_diff_dir_walk_task(
111
+ src=src,
112
+ dst=dst,
113
+ max_depth=max_depth,
114
+ out_queue=out_queue,
115
+ order=order,
116
+ )
117
+
118
+ worker = Thread(
119
+ target=task,
120
+ daemon=True,
121
+ )
122
+ worker.start()
123
+
124
+ while True:
125
+ try:
126
+ dir = out_queue.get_nowait()
127
+ if dir is None:
128
+ break
129
+ yield dir
130
+ except Empty:
131
+ time.sleep(0.1)
132
+
133
+ worker.join()
134
+ except KeyboardInterrupt:
135
+ pass
rclone_api/types.py CHANGED
@@ -10,3 +10,9 @@ class ListingOption(Enum):
10
10
  DIRS_ONLY = "dirs-only"
11
11
  FILES_ONLY = "files-only"
12
12
  ALL = "all"
13
+
14
+
15
+ class Order(Enum):
16
+ NORMAL = "normal"
17
+ REVERSE = "reverse"
18
+ RANDOM = "random"
rclone_api/walk.py CHANGED
@@ -1,3 +1,4 @@
1
+ import random
1
2
  from queue import Queue
2
3
  from threading import Thread
3
4
  from typing import Generator
@@ -5,6 +6,7 @@ from typing import Generator
5
6
  from rclone_api import Dir
6
7
  from rclone_api.dir_listing import DirListing
7
8
  from rclone_api.remote import Remote
9
+ from rclone_api.types import Order
8
10
 
9
11
  _MAX_OUT_QUEUE_SIZE = 50
10
12
 
@@ -13,14 +15,14 @@ def walk_runner_breadth_first(
13
15
  dir: Dir,
14
16
  max_depth: int,
15
17
  out_queue: Queue[DirListing | None],
16
- reverse: bool = False,
18
+ order: Order = Order.NORMAL,
17
19
  ) -> None:
18
20
  queue: Queue[Dir] = Queue()
19
21
  queue.put(dir)
20
22
  try:
21
23
  while not queue.empty():
22
24
  current_dir = queue.get()
23
- dirlisting = current_dir.ls(max_depth=0, reverse=reverse)
25
+ dirlisting = current_dir.ls(max_depth=0, order=order)
24
26
  out_queue.put(dirlisting)
25
27
  dirs = dirlisting.dirs
26
28
 
@@ -41,22 +43,26 @@ def walk_runner_breadth_first(
41
43
 
42
44
 
43
45
  def walk_runner_depth_first(
44
- dir: Dir, max_depth: int, out_queue: Queue[DirListing | None], reverse=False
46
+ dir: Dir,
47
+ max_depth: int,
48
+ out_queue: Queue[DirListing | None],
49
+ order: Order = Order.NORMAL,
45
50
  ) -> None:
46
51
  try:
47
52
  stack = [(dir, max_depth)]
48
53
  while stack:
49
54
  current_dir, depth = stack.pop()
50
55
  dirlisting = current_dir.ls()
51
- if reverse:
56
+ if order == Order.REVERSE:
52
57
  dirlisting.dirs.reverse()
58
+ if order == Order.RANDOM:
59
+
60
+ random.shuffle(dirlisting.dirs)
53
61
  if depth != 0:
54
62
  for subdir in dirlisting.dirs: # Process deeper directories first
55
63
  # stack.append((child, depth - 1 if depth > 0 else depth))
56
64
  next_depth = depth - 1 if depth > 0 else depth
57
- walk_runner_depth_first(
58
- subdir, next_depth, out_queue, reverse=reverse
59
- )
65
+ walk_runner_depth_first(subdir, next_depth, out_queue, order=order)
60
66
  out_queue.put(dirlisting)
61
67
  out_queue.put(None)
62
68
  except KeyboardInterrupt:
@@ -70,7 +76,7 @@ def walk(
70
76
  dir: Dir | Remote,
71
77
  breadth_first: bool,
72
78
  max_depth: int = -1,
73
- reverse: bool = False,
79
+ order: Order = Order.NORMAL,
74
80
  ) -> Generator[DirListing, None, None]:
75
81
  """Walk through the given directory recursively.
76
82
 
@@ -89,9 +95,9 @@ def walk(
89
95
 
90
96
  def _task() -> None:
91
97
  if breadth_first:
92
- walk_runner_breadth_first(dir, max_depth, out_queue, reverse)
98
+ walk_runner_breadth_first(dir, max_depth, out_queue, order)
93
99
  else:
94
- walk_runner_depth_first(dir, max_depth, out_queue, reverse)
100
+ walk_runner_depth_first(dir, max_depth, out_queue, order)
95
101
 
96
102
  # Start worker thread
97
103
  worker = Thread(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.0.69
3
+ Version: 1.0.72
4
4
  Summary: rclone api in python
5
5
  Home-page: https://github.com/zackees/rclone-api
6
6
  Maintainer: Zachary Vorhies
@@ -1,29 +1,29 @@
1
- rclone_api/__init__.py,sha256=B9q_4JU5Myh0DN-wqr_IgF5hZeAvszrGcqs2x-3hq-Q,633
1
+ rclone_api/__init__.py,sha256=kJUk9KAxQ2AEms9lM5yqw-dqlabarhU69b2FAxJVBlY,692
2
2
  rclone_api/cli.py,sha256=dibfAZIh0kXWsBbfp3onKLjyZXo54mTzDjUdzJlDlWo,231
3
3
  rclone_api/completed_process.py,sha256=Pp-hXnLgej0IGO5ee9Fmx64dGzIofbQFEUyXdFCvO54,1371
4
4
  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/diff_walk.py,sha256=V8U6qwCzG7_Z6-xFpB4zP6kCq_jN30OMYo7tT92OTd0,5061
9
- rclone_api/dir.py,sha256=8EwaGonkCeHhe-rsVl98PkEKvyf-3pFUJnXqkN6Fx24,3509
8
+ rclone_api/dir.py,sha256=hL0i4zrNUbpvFWI3TvKyOyanJ2okcb1lz4r2kxlvSi4,3529
10
9
  rclone_api/dir_listing.py,sha256=9Qqf2SUswrOEkyqmaH23V51I18X6ePiXb9B1vUwRF5o,1571
11
10
  rclone_api/exec.py,sha256=1ovvaMXDEfLiT7BrYZyE85u_yFhEUwUNW3jPOzqknR8,1023
12
11
  rclone_api/file.py,sha256=YtR5Y6c0YfXTS-sReOy2UgiSnafcAeO6b2hnbojBQD4,1423
13
12
  rclone_api/filelist.py,sha256=xbiusvNgaB_b_kQOZoHMJJxn6TWGtPrWd2J042BI28o,767
14
13
  rclone_api/group_files.py,sha256=kOHh6ysFDkxjldSwvW6KqmiADUC1yFCdrZRY57TvbGY,5328
15
14
  rclone_api/process.py,sha256=RrMfTe0bndmJ6gBK67ioqNvCstJ8aTC8RlGX1XBLlcw,4191
16
- rclone_api/rclone.py,sha256=nN1bqUmiopCKP6VNG4XpSfBfWx4EuAIyxB9NevYp-OA,28854
15
+ rclone_api/rclone.py,sha256=kIamoje3fUaWboMdF_d_a4WVaSa8BfK6zDFY8U6pNbs,28820
17
16
  rclone_api/remote.py,sha256=c9hlRKBCg1BFB9MCINaQIoCg10qyAkeqiS4brl8ce-8,343
18
17
  rclone_api/rpath.py,sha256=8ZA_1wxWtskwcy0I8V2VbjKDmzPkiWd8Q2JQSvh-sYE,2586
19
- rclone_api/types.py,sha256=VXA-56va2S41onAWM_UmhHlG4qeyMUEF5dsVFyfLlH8,232
18
+ rclone_api/scan_missing_folders.py,sha256=ke2AVIdX-MiuJUi7poJBzP8GxjzzMhCsr1Mxbg0EIu4,4217
19
+ rclone_api/types.py,sha256=DcbNw1R6j2f_1zHrhqEJcpCR-8kJfFJawMY0AmPsCnM,321
20
20
  rclone_api/util.py,sha256=_cvmHcJPRl2yXw4zgZiop3z-riA_8Ek6S5NDPw8cqSY,4198
21
- rclone_api/walk.py,sha256=ndWV7WBVQLbpZu3HuJrAe1cXcmQVjT9_YPsbat158bQ,3231
21
+ rclone_api/walk.py,sha256=-54NVE8EJcCstwDoaC_UtHm73R2HrZwVwQmsnv55xNU,3369
22
22
  rclone_api/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
23
23
  rclone_api/cmd/list_files.py,sha256=x8FHODEilwKqwdiU1jdkeJbLwOqUkUQuDWPo2u_zpf0,741
24
- rclone_api-1.0.69.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
25
- rclone_api-1.0.69.dist-info/METADATA,sha256=bl94_jCcD0a0buUsLVFIQ-MPQ5O2fy-hFrvK1EvU7Ls,4489
26
- rclone_api-1.0.69.dist-info/WHEEL,sha256=9Hm2OB-j1QcCUq9Jguht7ayGIIZBRTdOXD1qg9cCgPM,109
27
- rclone_api-1.0.69.dist-info/entry_points.txt,sha256=XUoTX3m7CWxdj2VAKhEuO0NMOfX2qf-OcEDFwdyk9ZE,72
28
- rclone_api-1.0.69.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
29
- rclone_api-1.0.69.dist-info/RECORD,,
24
+ rclone_api-1.0.72.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
25
+ rclone_api-1.0.72.dist-info/METADATA,sha256=xzlW0jfYuqOPGSCI9k8MxNvp4BpI0Duojacxq_l-C-M,4489
26
+ rclone_api-1.0.72.dist-info/WHEEL,sha256=9Hm2OB-j1QcCUq9Jguht7ayGIIZBRTdOXD1qg9cCgPM,109
27
+ rclone_api-1.0.72.dist-info/entry_points.txt,sha256=XUoTX3m7CWxdj2VAKhEuO0NMOfX2qf-OcEDFwdyk9ZE,72
28
+ rclone_api-1.0.72.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
29
+ rclone_api-1.0.72.dist-info/RECORD,,
rclone_api/diff_walk.py DELETED
@@ -1,143 +0,0 @@
1
- from concurrent.futures import ThreadPoolExecutor
2
- from queue import Queue
3
- from threading import Thread
4
- from typing import Generator
5
-
6
- from rclone_api import Dir
7
- from rclone_api.dir_listing import DirListing
8
- from rclone_api.types import ListingOption
9
- from rclone_api.walk import walk_runner_depth_first
10
-
11
- _MAX_OUT_QUEUE_SIZE = 50
12
-
13
-
14
- # ONLY Works from src -> dst diffing.
15
- def _async_diff_dir_walk_task(
16
- src: Dir, dst: Dir, max_depth: int, out_queue: Queue[Dir | None], reverse=False
17
- ) -> None:
18
-
19
- try:
20
- stack = [(src, dst)]
21
- while stack:
22
- curr_src, curr_dst = stack.pop()
23
- curr_src = curr_src
24
- curr_dst = curr_dst
25
-
26
- with ThreadPoolExecutor(max_workers=2) as executor:
27
- # src_dir_listing = src.ls(listing_option=ListingOption.DIRS_ONLY)
28
- # dst_dir_listing = dst.ls(listing_option=ListingOption.DIRS_ONLY)
29
- t1 = executor.submit(
30
- src.ls, listing_option=ListingOption.DIRS_ONLY, reverse=reverse
31
- )
32
- t2 = executor.submit(
33
- dst.ls, listing_option=ListingOption.DIRS_ONLY, reverse=reverse
34
- )
35
- src_dir_listing: DirListing = t1.result()
36
- dst_dir_listing: DirListing = t2.result()
37
-
38
- # dirlisting = current_dir.ls()
39
- # if reverse:
40
- # dirlisting.dirs.reverse()
41
- # if depth != 0:
42
- # for subdir in dirlisting.dirs: # Process deeper directories first
43
- # # stack.append((child, depth - 1 if depth > 0 else depth))
44
- # next_depth = depth - 1 if depth > 0 else depth
45
- # _walk_runner_depth_first(
46
- # subdir, next_depth, out_queue, reverse=reverse
47
- # )
48
- # out_queue.put(dirlisting)
49
-
50
- # for subdir in dst_dir_listing.dirs:
51
- # subdir.to_string(include_remote=False)
52
- # walk_runner_depth_first()
53
-
54
- # find elements missing on dst
55
- # missing_on_dst: set[Dir] = set(src_dir_listing.dirs) - set(
56
- # dst_dir_listing.dirs
57
- # )
58
- # exists_on_dst: set[Dir] = set(src_dir_listing.dirs) - missing_on_dst
59
-
60
- dst_files: list[str] = [d.name for d in dst_dir_listing.dirs]
61
- src_files: list[str] = [d.name for d in src_dir_listing.dirs]
62
-
63
- dst_files_set: set[str] = set(dst_files)
64
- # src_files_set: set[str] = set(src_files)
65
-
66
- # print(f"src_files: {src_files}")
67
- # print(f"dst_files: {dst_files}")
68
-
69
- matching_dirs: list[str] = []
70
-
71
- for file in src_files:
72
- if file not in dst_files_set:
73
- # print(f"missing dir on src: {file}")
74
- queue_dir_listing: Queue[DirListing | None] = Queue()
75
- walk_runner_depth_first(
76
- dir=curr_src,
77
- max_depth=max_depth,
78
- out_queue=queue_dir_listing,
79
- reverse=reverse,
80
- )
81
- while dirlisting := queue_dir_listing.get():
82
- if dirlisting is None:
83
- break
84
- # print(f"dirlisting: {dirlisting}")
85
- for d in dirlisting.dirs:
86
- out_queue.put(d)
87
- else:
88
- matching_dirs.append(file)
89
-
90
- for matching_dir in matching_dirs:
91
- # print(f"matching dir: {matching_dir}")
92
- _async_diff_dir_walk_task(
93
- src=curr_src / matching_dir,
94
- dst=curr_dst / matching_dir,
95
- max_depth=max_depth,
96
- out_queue=out_queue,
97
- reverse=reverse,
98
- )
99
-
100
- out_queue.put(None)
101
- except KeyboardInterrupt:
102
- import _thread
103
-
104
- out_queue.put(None)
105
- _thread.interrupt_main()
106
-
107
-
108
- def diff_walk(
109
- src: Dir,
110
- dst: Dir,
111
- max_depth: int = -1,
112
- reverse: bool = False,
113
- ) -> Generator[Dir, None, None]:
114
- """Walk through the given directory recursively.
115
-
116
- Args:
117
- dir: Directory or Remote to walk through
118
- max_depth: Maximum depth to traverse (-1 for unlimited)
119
-
120
- Yields:
121
- DirListing: Directory listing for each directory encountered
122
- """
123
-
124
- try:
125
- out_queue: Queue[Dir | None] = Queue(maxsize=_MAX_OUT_QUEUE_SIZE)
126
-
127
- def task() -> None:
128
- _async_diff_dir_walk_task(src, dst, max_depth, out_queue, reverse=reverse)
129
-
130
- worker = Thread(
131
- target=task,
132
- daemon=True,
133
- )
134
- worker.start()
135
-
136
- while dirlisting := out_queue.get():
137
- if dirlisting is None:
138
- break
139
- yield dirlisting
140
-
141
- worker.join()
142
- except KeyboardInterrupt:
143
- pass