rclone-api 1.0.70__py2.py3-none-any.whl → 1.0.72__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/__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} +33 -21
- rclone_api/types.py +6 -0
- rclone_api/walk.py +16 -10
- {rclone_api-1.0.70.dist-info → rclone_api-1.0.72.dist-info}/METADATA +1 -1
- {rclone_api-1.0.70.dist-info → rclone_api-1.0.72.dist-info}/RECORD +12 -12
- {rclone_api-1.0.70.dist-info → rclone_api-1.0.72.dist-info}/LICENSE +0 -0
- {rclone_api-1.0.70.dist-info → rclone_api-1.0.72.dist-info}/WHEEL +0 -0
- {rclone_api-1.0.70.dist-info → rclone_api-1.0.72.dist-info}/entry_points.txt +0 -0
- {rclone_api-1.0.70.dist-info → rclone_api-1.0.72.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,37 @@ _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
|
20
21
|
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
22
|
t1 = executor.submit(
|
24
|
-
src.ls, listing_option=ListingOption.DIRS_ONLY,
|
23
|
+
src.ls, listing_option=ListingOption.DIRS_ONLY, order=order
|
25
24
|
)
|
26
25
|
t2 = executor.submit(
|
27
|
-
dst.ls, listing_option=ListingOption.DIRS_ONLY,
|
26
|
+
dst.ls, listing_option=ListingOption.DIRS_ONLY, order=order
|
28
27
|
)
|
29
28
|
src_dir_listing: DirListing = t1.result()
|
30
29
|
dst_dir_listing: DirListing = t2.result()
|
31
30
|
next_depth = max_depth - 1 if max_depth > 0 else max_depth
|
32
|
-
|
33
|
-
|
34
|
-
dst_files_set: set[str] = set(
|
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)
|
35
34
|
matching_dirs: list[str] = []
|
36
|
-
|
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:
|
37
42
|
if file not in dst_files_set:
|
38
|
-
# print(f"missing dir on src: {file}")
|
39
43
|
queue_dir_listing: Queue[DirListing | None] = Queue()
|
40
44
|
if next_depth > 0 or next_depth == -1:
|
41
45
|
walk_runner_depth_first(
|
42
46
|
dir=curr_src,
|
43
47
|
out_queue=queue_dir_listing,
|
44
|
-
|
48
|
+
order=order,
|
45
49
|
max_depth=next_depth,
|
46
50
|
)
|
47
51
|
while dirlisting := queue_dir_listing.get():
|
@@ -63,15 +67,17 @@ def _async_diff_dir_walk_task(
|
|
63
67
|
dst=dst_next,
|
64
68
|
max_depth=next_depth,
|
65
69
|
out_queue=out_queue,
|
66
|
-
|
70
|
+
order=order,
|
67
71
|
)
|
68
72
|
|
69
73
|
|
70
74
|
def async_diff_dir_walk_task(
|
71
|
-
src: Dir, dst: Dir, max_depth: int, out_queue: Queue[Dir | None],
|
75
|
+
src: Dir, dst: Dir, max_depth: int, out_queue: Queue[Dir | None], order: Order
|
72
76
|
) -> None:
|
73
77
|
try:
|
74
|
-
_async_diff_dir_walk_task(
|
78
|
+
_async_diff_dir_walk_task(
|
79
|
+
src=src, dst=dst, max_depth=max_depth, out_queue=out_queue, order=order
|
80
|
+
)
|
75
81
|
except Exception:
|
76
82
|
import _thread
|
77
83
|
|
@@ -81,11 +87,11 @@ def async_diff_dir_walk_task(
|
|
81
87
|
out_queue.put(None)
|
82
88
|
|
83
89
|
|
84
|
-
def
|
90
|
+
def scan_missing_folders(
|
85
91
|
src: Dir,
|
86
92
|
dst: Dir,
|
87
93
|
max_depth: int = -1,
|
88
|
-
|
94
|
+
order: Order = Order.NORMAL,
|
89
95
|
) -> Generator[Dir, None, None]:
|
90
96
|
"""Walk through the given directory recursively.
|
91
97
|
|
@@ -101,7 +107,13 @@ def diff_walk(
|
|
101
107
|
out_queue: Queue[Dir | None] = Queue(maxsize=_MAX_OUT_QUEUE_SIZE)
|
102
108
|
|
103
109
|
def task() -> None:
|
104
|
-
async_diff_dir_walk_task(
|
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
|
+
)
|
105
117
|
|
106
118
|
worker = Thread(
|
107
119
|
target=task,
|
@@ -111,10 +123,10 @@ def diff_walk(
|
|
111
123
|
|
112
124
|
while True:
|
113
125
|
try:
|
114
|
-
|
115
|
-
if
|
126
|
+
dir = out_queue.get_nowait()
|
127
|
+
if dir is None:
|
116
128
|
break
|
117
|
-
yield
|
129
|
+
yield dir
|
118
130
|
except Empty:
|
119
131
|
time.sleep(0.1)
|
120
132
|
|
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=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
|
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.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,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|