rclone-api 1.0.70__py2.py3-none-any.whl → 1.0.73__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 +5 -2
- rclone_api/dir.py +3 -3
- rclone_api/rclone.py +13 -14
- rclone_api/{diff_walk.py → scan_missing_folders.py} +41 -21
- rclone_api/types.py +6 -0
- rclone_api/walk.py +16 -10
- {rclone_api-1.0.70.dist-info → rclone_api-1.0.73.dist-info}/METADATA +1 -1
- {rclone_api-1.0.70.dist-info → rclone_api-1.0.73.dist-info}/RECORD +12 -12
- {rclone_api-1.0.70.dist-info → rclone_api-1.0.73.dist-info}/LICENSE +0 -0
- {rclone_api-1.0.70.dist-info → rclone_api-1.0.73.dist-info}/WHEEL +0 -0
- {rclone_api-1.0.70.dist-info → rclone_api-1.0.73.dist-info}/entry_points.txt +0 -0
- {rclone_api-1.0.70.dist-info → rclone_api-1.0.73.dist-info}/top_level.txt +0 -0
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
|
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
|
-
|
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
|
-
|
60
|
+
order=order,
|
61
61
|
listing_option=listing_option,
|
62
62
|
)
|
63
63
|
|
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
|
-
|
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
|
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
|
-
|
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,
|
283
|
+
dir_obj, max_depth=max_depth, breadth_first=breadth_first, order=order
|
281
284
|
)
|
282
285
|
|
283
|
-
def
|
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
|
-
|
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.
|
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
|
307
|
-
src=src_dir, dst=dst_dir, max_depth=max_depth,
|
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,
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import random
|
1
2
|
import time
|
2
3
|
from concurrent.futures import ThreadPoolExecutor
|
3
4
|
from queue import Empty, Queue
|
@@ -6,7 +7,7 @@ from typing import Generator
|
|
6
7
|
|
7
8
|
from rclone_api import Dir
|
8
9
|
from rclone_api.dir_listing import DirListing
|
9
|
-
from rclone_api.types import ListingOption
|
10
|
+
from rclone_api.types import ListingOption, Order
|
10
11
|
from rclone_api.walk import walk_runner_depth_first
|
11
12
|
|
12
13
|
_MAX_OUT_QUEUE_SIZE = 50
|
@@ -14,34 +15,45 @@ _MAX_OUT_QUEUE_SIZE = 50
|
|
14
15
|
|
15
16
|
# ONLY Works from src -> dst diffing.
|
16
17
|
def _async_diff_dir_walk_task(
|
17
|
-
src: Dir, dst: Dir, max_depth: int, out_queue: Queue[Dir | None],
|
18
|
+
src: Dir, dst: Dir, max_depth: int, out_queue: Queue[Dir | None], order: Order
|
18
19
|
) -> None:
|
19
20
|
curr_src, curr_dst = src, dst
|
21
|
+
can_scan_two_deep = max_depth > 1 or max_depth == -1
|
22
|
+
ls_depth = 2 if can_scan_two_deep else 1
|
20
23
|
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
24
|
t1 = executor.submit(
|
24
|
-
src.ls,
|
25
|
+
src.ls,
|
26
|
+
listing_option=ListingOption.DIRS_ONLY,
|
27
|
+
order=order,
|
28
|
+
max_depth=ls_depth,
|
25
29
|
)
|
26
30
|
t2 = executor.submit(
|
27
|
-
dst.ls,
|
31
|
+
dst.ls,
|
32
|
+
listing_option=ListingOption.DIRS_ONLY,
|
33
|
+
order=order,
|
34
|
+
max_depth=ls_depth,
|
28
35
|
)
|
29
36
|
src_dir_listing: DirListing = t1.result()
|
30
37
|
dst_dir_listing: DirListing = t2.result()
|
31
38
|
next_depth = max_depth - 1 if max_depth > 0 else max_depth
|
32
|
-
|
33
|
-
|
34
|
-
dst_files_set: set[str] = set(
|
39
|
+
dst_dirs: list[str] = [d.name for d in dst_dir_listing.dirs]
|
40
|
+
src_dirs: list[str] = [d.name for d in src_dir_listing.dirs]
|
41
|
+
dst_files_set: set[str] = set(dst_dirs)
|
35
42
|
matching_dirs: list[str] = []
|
36
|
-
|
43
|
+
if order == Order.REVERSE:
|
44
|
+
src_dirs.reverse()
|
45
|
+
dst_dirs.reverse()
|
46
|
+
elif order == Order.RANDOM:
|
47
|
+
random.shuffle(src_dirs)
|
48
|
+
random.shuffle(dst_dirs)
|
49
|
+
for file in src_dirs:
|
37
50
|
if file not in dst_files_set:
|
38
|
-
# print(f"missing dir on src: {file}")
|
39
51
|
queue_dir_listing: Queue[DirListing | None] = Queue()
|
40
52
|
if next_depth > 0 or next_depth == -1:
|
41
53
|
walk_runner_depth_first(
|
42
54
|
dir=curr_src,
|
43
55
|
out_queue=queue_dir_listing,
|
44
|
-
|
56
|
+
order=order,
|
45
57
|
max_depth=next_depth,
|
46
58
|
)
|
47
59
|
while dirlisting := queue_dir_listing.get():
|
@@ -63,15 +75,17 @@ def _async_diff_dir_walk_task(
|
|
63
75
|
dst=dst_next,
|
64
76
|
max_depth=next_depth,
|
65
77
|
out_queue=out_queue,
|
66
|
-
|
78
|
+
order=order,
|
67
79
|
)
|
68
80
|
|
69
81
|
|
70
82
|
def async_diff_dir_walk_task(
|
71
|
-
src: Dir, dst: Dir, max_depth: int, out_queue: Queue[Dir | None],
|
83
|
+
src: Dir, dst: Dir, max_depth: int, out_queue: Queue[Dir | None], order: Order
|
72
84
|
) -> None:
|
73
85
|
try:
|
74
|
-
_async_diff_dir_walk_task(
|
86
|
+
_async_diff_dir_walk_task(
|
87
|
+
src=src, dst=dst, max_depth=max_depth, out_queue=out_queue, order=order
|
88
|
+
)
|
75
89
|
except Exception:
|
76
90
|
import _thread
|
77
91
|
|
@@ -81,11 +95,11 @@ def async_diff_dir_walk_task(
|
|
81
95
|
out_queue.put(None)
|
82
96
|
|
83
97
|
|
84
|
-
def
|
98
|
+
def scan_missing_folders(
|
85
99
|
src: Dir,
|
86
100
|
dst: Dir,
|
87
101
|
max_depth: int = -1,
|
88
|
-
|
102
|
+
order: Order = Order.NORMAL,
|
89
103
|
) -> Generator[Dir, None, None]:
|
90
104
|
"""Walk through the given directory recursively.
|
91
105
|
|
@@ -101,7 +115,13 @@ def diff_walk(
|
|
101
115
|
out_queue: Queue[Dir | None] = Queue(maxsize=_MAX_OUT_QUEUE_SIZE)
|
102
116
|
|
103
117
|
def task() -> None:
|
104
|
-
async_diff_dir_walk_task(
|
118
|
+
async_diff_dir_walk_task(
|
119
|
+
src=src,
|
120
|
+
dst=dst,
|
121
|
+
max_depth=max_depth,
|
122
|
+
out_queue=out_queue,
|
123
|
+
order=order,
|
124
|
+
)
|
105
125
|
|
106
126
|
worker = Thread(
|
107
127
|
target=task,
|
@@ -111,10 +131,10 @@ def diff_walk(
|
|
111
131
|
|
112
132
|
while True:
|
113
133
|
try:
|
114
|
-
|
115
|
-
if
|
134
|
+
dir = out_queue.get_nowait()
|
135
|
+
if dir is None:
|
116
136
|
break
|
117
|
-
yield
|
137
|
+
yield dir
|
118
138
|
except Empty:
|
119
139
|
time.sleep(0.1)
|
120
140
|
|
rclone_api/types.py
CHANGED
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
|
-
|
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,
|
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,
|
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
|
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
|
-
|
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,
|
98
|
+
walk_runner_breadth_first(dir, max_depth, out_queue, order)
|
93
99
|
else:
|
94
|
-
walk_runner_depth_first(dir, max_depth, out_queue,
|
100
|
+
walk_runner_depth_first(dir, max_depth, out_queue, order)
|
95
101
|
|
96
102
|
# Start worker thread
|
97
103
|
worker = Thread(
|
@@ -1,29 +1,29 @@
|
|
1
|
-
rclone_api/__init__.py,sha256=
|
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/
|
9
|
-
rclone_api/dir.py,sha256=BuXPd-bAvZund8k7mXjvx_UylsPFwbWq2zaUdflTX04,3520
|
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=
|
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/
|
18
|
+
rclone_api/scan_missing_folders.py,sha256=eySQNFwsHxdhN6AAB4K94iH9rJDWUHQIJbddlZKvdFQ,4441
|
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
|
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.
|
25
|
-
rclone_api-1.0.
|
26
|
-
rclone_api-1.0.
|
27
|
-
rclone_api-1.0.
|
28
|
-
rclone_api-1.0.
|
29
|
-
rclone_api-1.0.
|
24
|
+
rclone_api-1.0.73.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
|
25
|
+
rclone_api-1.0.73.dist-info/METADATA,sha256=le9xg4WsNvEKk5SO_RVrqJ9kfIiMS7ASt6bhAJ68GpE,4489
|
26
|
+
rclone_api-1.0.73.dist-info/WHEEL,sha256=9Hm2OB-j1QcCUq9Jguht7ayGIIZBRTdOXD1qg9cCgPM,109
|
27
|
+
rclone_api-1.0.73.dist-info/entry_points.txt,sha256=XUoTX3m7CWxdj2VAKhEuO0NMOfX2qf-OcEDFwdyk9ZE,72
|
28
|
+
rclone_api-1.0.73.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
|
29
|
+
rclone_api-1.0.73.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|