rclone-api 1.0.58__py2.py3-none-any.whl → 1.0.60__py2.py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- rclone_api/diff.py +4 -3
- rclone_api/dir.py +2 -2
- rclone_api/rclone.py +41 -9
- rclone_api/util.py +7 -0
- rclone_api/walk.py +14 -8
- {rclone_api-1.0.58.dist-info → rclone_api-1.0.60.dist-info}/METADATA +1 -1
- {rclone_api-1.0.58.dist-info → rclone_api-1.0.60.dist-info}/RECORD +11 -11
- {rclone_api-1.0.58.dist-info → rclone_api-1.0.60.dist-info}/LICENSE +0 -0
- {rclone_api-1.0.58.dist-info → rclone_api-1.0.60.dist-info}/WHEEL +0 -0
- {rclone_api-1.0.58.dist-info → rclone_api-1.0.60.dist-info}/entry_points.txt +0 -0
- {rclone_api-1.0.58.dist-info → rclone_api-1.0.60.dist-info}/top_level.txt +0 -0
rclone_api/diff.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import warnings
|
1
2
|
from dataclasses import dataclass
|
2
3
|
from enum import Enum
|
3
4
|
from queue import Queue
|
@@ -104,9 +105,9 @@ def _async_diff_stream_from_running_process(
|
|
104
105
|
_thread.interrupt_main()
|
105
106
|
if count == 0 and check:
|
106
107
|
first_lines_str = "\n".join(first_few_lines)
|
107
|
-
|
108
|
-
|
109
|
-
)
|
108
|
+
warning_msg = f"No output from rclone check, first few lines: {first_lines_str}"
|
109
|
+
warnings.warn(warning_msg)
|
110
|
+
raise ValueError(warning_msg)
|
110
111
|
|
111
112
|
|
112
113
|
def diff_stream_from_running_process(
|
rclone_api/dir.py
CHANGED
@@ -41,11 +41,11 @@ class Dir:
|
|
41
41
|
# self.path.set_rclone(self.path.remote.rclone)
|
42
42
|
assert self.path.rclone is not None
|
43
43
|
|
44
|
-
def ls(self, max_depth: int = 0) -> DirListing:
|
44
|
+
def ls(self, max_depth: int = 0, reverse: bool = False) -> DirListing:
|
45
45
|
"""List files and directories in the given path."""
|
46
46
|
assert self.path.rclone is not None
|
47
47
|
dir = Dir(self.path)
|
48
|
-
return self.path.rclone.ls(dir, max_depth=max_depth)
|
48
|
+
return self.path.rclone.ls(dir, max_depth=max_depth, reverse=reverse)
|
49
49
|
|
50
50
|
def walk(
|
51
51
|
self, breadth_first: bool, max_depth: int = -1
|
rclone_api/rclone.py
CHANGED
@@ -27,6 +27,7 @@ from rclone_api.process import Process
|
|
27
27
|
from rclone_api.remote import Remote
|
28
28
|
from rclone_api.rpath import RPath
|
29
29
|
from rclone_api.util import (
|
30
|
+
get_check,
|
30
31
|
get_rclone_exe,
|
31
32
|
get_verbose,
|
32
33
|
to_path,
|
@@ -56,7 +57,7 @@ class Rclone:
|
|
56
57
|
self._exec = RcloneExec(rclone_conf, get_rclone_exe(rclone_exe))
|
57
58
|
|
58
59
|
def _run(
|
59
|
-
self, cmd: list[str], check: bool =
|
60
|
+
self, cmd: list[str], check: bool = False, capture: bool | None = None
|
60
61
|
) -> subprocess.CompletedProcess:
|
61
62
|
return self._exec.execute(cmd, check=check, capture=capture)
|
62
63
|
|
@@ -72,7 +73,7 @@ class Rclone:
|
|
72
73
|
|
73
74
|
def launch_server(
|
74
75
|
self,
|
75
|
-
addr: str
|
76
|
+
addr: str,
|
76
77
|
user: str | None = None,
|
77
78
|
password: str | None = None,
|
78
79
|
other_args: list[str] | None = None,
|
@@ -122,6 +123,7 @@ class Rclone:
|
|
122
123
|
path: Dir | Remote | str,
|
123
124
|
max_depth: int | None = None,
|
124
125
|
glob: str | None = None,
|
126
|
+
reverse: bool = False,
|
125
127
|
) -> DirListing:
|
126
128
|
"""List files in the given path.
|
127
129
|
|
@@ -162,6 +164,9 @@ class Rclone:
|
|
162
164
|
# do we have a glob pattern?
|
163
165
|
if glob is not None:
|
164
166
|
paths = [p for p in paths if fnmatch(p.path, glob)]
|
167
|
+
|
168
|
+
if reverse:
|
169
|
+
paths.reverse()
|
165
170
|
return DirListing(paths)
|
166
171
|
|
167
172
|
def listremotes(self) -> list[Remote]:
|
@@ -175,9 +180,21 @@ class Rclone:
|
|
175
180
|
out = [Remote(name=t, rclone=self) for t in tmp]
|
176
181
|
return out
|
177
182
|
|
178
|
-
def diff(
|
183
|
+
def diff(
|
184
|
+
self,
|
185
|
+
src: str,
|
186
|
+
dst: str,
|
187
|
+
min_size: (
|
188
|
+
str | None
|
189
|
+
) = None, # e. g. "1MB" - see rclone documentation: https://rclone.org/commands/rclone_check/
|
190
|
+
max_size: (
|
191
|
+
str | None
|
192
|
+
) = None, # e. g. "1GB" - see rclone documentation: https://rclone.org/commands/rclone_check/
|
193
|
+
other_args: list[str] | None = None,
|
194
|
+
) -> Generator[DiffItem, None, None]:
|
179
195
|
"""Be extra careful with the src and dst values. If you are off by one
|
180
196
|
parent directory, you will get a huge amount of false diffs."""
|
197
|
+
other_args = other_args or []
|
181
198
|
cmd = [
|
182
199
|
"check",
|
183
200
|
src,
|
@@ -189,6 +206,12 @@ class Rclone:
|
|
189
206
|
"--combined",
|
190
207
|
"-",
|
191
208
|
]
|
209
|
+
if min_size:
|
210
|
+
cmd += ["--min-size", min_size]
|
211
|
+
if max_size:
|
212
|
+
cmd += ["--max-size", max_size]
|
213
|
+
if other_args:
|
214
|
+
cmd += other_args
|
192
215
|
proc = self._launch_process(cmd, capture=True)
|
193
216
|
item: DiffItem
|
194
217
|
for item in diff_stream_from_running_process(proc, src_slug=src, dst_slug=dst):
|
@@ -197,7 +220,11 @@ class Rclone:
|
|
197
220
|
yield item
|
198
221
|
|
199
222
|
def walk(
|
200
|
-
self,
|
223
|
+
self,
|
224
|
+
path: Dir | Remote | str,
|
225
|
+
max_depth: int = -1,
|
226
|
+
breadth_first: bool = True,
|
227
|
+
reverse: bool = False,
|
201
228
|
) -> Generator[DirListing, None, None]:
|
202
229
|
"""Walk through the given path recursively.
|
203
230
|
|
@@ -231,7 +258,9 @@ class Rclone:
|
|
231
258
|
dir_obj = Dir(path) # shut up pyright
|
232
259
|
assert f"Invalid type for path: {type(path)}"
|
233
260
|
|
234
|
-
yield from walk(
|
261
|
+
yield from walk(
|
262
|
+
dir_obj, max_depth=max_depth, breadth_first=breadth_first, reverse=reverse
|
263
|
+
)
|
235
264
|
|
236
265
|
def cleanup(
|
237
266
|
self, path: str, other_args: list[str] | None = None
|
@@ -248,7 +277,7 @@ class Rclone:
|
|
248
277
|
self,
|
249
278
|
src: File | str,
|
250
279
|
dst: File | str,
|
251
|
-
check=
|
280
|
+
check: bool | None = None,
|
252
281
|
other_args: list[str] | None = None,
|
253
282
|
) -> None:
|
254
283
|
"""Copy multiple files from source to destination.
|
@@ -258,6 +287,7 @@ class Rclone:
|
|
258
287
|
Args:
|
259
288
|
payload: Dictionary of source and destination file paths
|
260
289
|
"""
|
290
|
+
check = get_check(check)
|
261
291
|
src = src if isinstance(src, str) else str(src.path)
|
262
292
|
dst = dst if isinstance(dst, str) else str(dst.path)
|
263
293
|
cmd_list: list[str] = ["copyto", src, dst]
|
@@ -270,7 +300,7 @@ class Rclone:
|
|
270
300
|
src: str,
|
271
301
|
dst: str,
|
272
302
|
files: list[str],
|
273
|
-
check=
|
303
|
+
check: bool | None = None,
|
274
304
|
verbose: bool | None = None,
|
275
305
|
checkers: int | None = None,
|
276
306
|
transfers: int | None = None,
|
@@ -284,6 +314,7 @@ class Rclone:
|
|
284
314
|
Args:
|
285
315
|
payload: Dictionary of source and destination file paths
|
286
316
|
"""
|
317
|
+
check = get_check(check)
|
287
318
|
max_partition_workers = max_partition_workers or 1
|
288
319
|
low_level_retries = low_level_retries or 10
|
289
320
|
retries = retries or 3
|
@@ -398,13 +429,14 @@ class Rclone:
|
|
398
429
|
def delete_files(
|
399
430
|
self,
|
400
431
|
files: str | File | list[str] | list[File],
|
401
|
-
check=
|
432
|
+
check: bool | None = None,
|
402
433
|
rmdirs=False,
|
403
434
|
verbose: bool | None = None,
|
404
435
|
max_partition_workers: int | None = None,
|
405
436
|
other_args: list[str] | None = None,
|
406
437
|
) -> CompletedProcess:
|
407
438
|
"""Delete a directory"""
|
439
|
+
check = get_check(check)
|
408
440
|
verbose = get_verbose(verbose)
|
409
441
|
payload: list[str] = convert_to_filestr_list(files)
|
410
442
|
if len(payload) == 0:
|
@@ -494,7 +526,7 @@ class Rclone:
|
|
494
526
|
dst = convert_to_str(dst)
|
495
527
|
cmd_list: list[str] = ["check", str(src), str(dst)]
|
496
528
|
try:
|
497
|
-
self._run(cmd_list)
|
529
|
+
self._run(cmd_list, check=True)
|
498
530
|
return True
|
499
531
|
except subprocess.CalledProcessError:
|
500
532
|
return False
|
rclone_api/util.py
CHANGED
@@ -63,6 +63,13 @@ def get_verbose(verbose: bool | None) -> bool:
|
|
63
63
|
return bool(int(os.getenv("RCLONE_API_VERBOSE", "0")))
|
64
64
|
|
65
65
|
|
66
|
+
def get_check(check: bool | None) -> bool:
|
67
|
+
if check is not None:
|
68
|
+
return check
|
69
|
+
# get it from the environment
|
70
|
+
return bool(int(os.getenv("RCLONE_API_CHECK", "1")))
|
71
|
+
|
72
|
+
|
66
73
|
def get_rclone_exe(rclone_exe: Path | None) -> Path:
|
67
74
|
if rclone_exe is None:
|
68
75
|
|
rclone_api/walk.py
CHANGED
@@ -10,14 +10,17 @@ _MAX_OUT_QUEUE_SIZE = 50
|
|
10
10
|
|
11
11
|
|
12
12
|
def _walk_runner_breadth_first(
|
13
|
-
dir: Dir,
|
13
|
+
dir: Dir,
|
14
|
+
max_depth: int,
|
15
|
+
out_queue: Queue[DirListing | None],
|
16
|
+
reverse: bool = False,
|
14
17
|
) -> None:
|
15
18
|
queue: Queue[Dir] = Queue()
|
16
19
|
queue.put(dir)
|
17
20
|
try:
|
18
21
|
while not queue.empty():
|
19
22
|
current_dir = queue.get()
|
20
|
-
dirlisting = current_dir.ls()
|
23
|
+
dirlisting = current_dir.ls(max_depth=0, reverse=reverse)
|
21
24
|
out_queue.put(dirlisting)
|
22
25
|
dirs = dirlisting.dirs
|
23
26
|
|
@@ -38,20 +41,22 @@ def _walk_runner_breadth_first(
|
|
38
41
|
|
39
42
|
|
40
43
|
def _walk_runner_depth_first(
|
41
|
-
dir: Dir, max_depth: int, out_queue: Queue[DirListing | None]
|
44
|
+
dir: Dir, max_depth: int, out_queue: Queue[DirListing | None], reverse=False
|
42
45
|
) -> None:
|
43
46
|
try:
|
44
47
|
stack = [(dir, max_depth)]
|
45
48
|
while stack:
|
46
49
|
current_dir, depth = stack.pop()
|
47
50
|
dirlisting = current_dir.ls()
|
51
|
+
if reverse:
|
52
|
+
dirlisting.dirs.reverse()
|
48
53
|
if depth != 0:
|
49
|
-
for subdir in
|
50
|
-
dirlisting.dirs
|
51
|
-
): # Process deeper directories first
|
54
|
+
for subdir in dirlisting.dirs: # Process deeper directories first
|
52
55
|
# stack.append((child, depth - 1 if depth > 0 else depth))
|
53
56
|
next_depth = depth - 1 if depth > 0 else depth
|
54
|
-
_walk_runner_depth_first(
|
57
|
+
_walk_runner_depth_first(
|
58
|
+
subdir, next_depth, out_queue, reverse=reverse
|
59
|
+
)
|
55
60
|
out_queue.put(dirlisting)
|
56
61
|
out_queue.put(None)
|
57
62
|
except KeyboardInterrupt:
|
@@ -65,6 +70,7 @@ def walk(
|
|
65
70
|
dir: Dir | Remote,
|
66
71
|
breadth_first: bool,
|
67
72
|
max_depth: int = -1,
|
73
|
+
reverse: bool = False,
|
68
74
|
) -> Generator[DirListing, None, None]:
|
69
75
|
"""Walk through the given directory recursively.
|
70
76
|
|
@@ -88,7 +94,7 @@ def walk(
|
|
88
94
|
# Start worker thread
|
89
95
|
worker = Thread(
|
90
96
|
target=strategy,
|
91
|
-
args=(dir, max_depth, out_queue),
|
97
|
+
args=(dir, max_depth, out_queue, reverse),
|
92
98
|
daemon=True,
|
93
99
|
)
|
94
100
|
worker.start()
|
@@ -4,24 +4,24 @@ rclone_api/completed_process.py,sha256=Pp-hXnLgej0IGO5ee9Fmx64dGzIofbQFEUyXdFCvO
|
|
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
|
-
rclone_api/diff.py,sha256=
|
8
|
-
rclone_api/dir.py,sha256=
|
7
|
+
rclone_api/diff.py,sha256=36sXtAFDPoCVUN7E46OB2FywkaFauffsg2e9VsUMGhU,4200
|
8
|
+
rclone_api/dir.py,sha256=2-PFaDpjEs28z82DQ-TyaCgrm_OgpwlkwfTnx1-Wwpk,2194
|
9
9
|
rclone_api/dir_listing.py,sha256=9Qqf2SUswrOEkyqmaH23V51I18X6ePiXb9B1vUwRF5o,1571
|
10
10
|
rclone_api/exec.py,sha256=1ovvaMXDEfLiT7BrYZyE85u_yFhEUwUNW3jPOzqknR8,1023
|
11
11
|
rclone_api/file.py,sha256=YtR5Y6c0YfXTS-sReOy2UgiSnafcAeO6b2hnbojBQD4,1423
|
12
12
|
rclone_api/filelist.py,sha256=xbiusvNgaB_b_kQOZoHMJJxn6TWGtPrWd2J042BI28o,767
|
13
13
|
rclone_api/group_files.py,sha256=kOHh6ysFDkxjldSwvW6KqmiADUC1yFCdrZRY57TvbGY,5328
|
14
14
|
rclone_api/process.py,sha256=RrMfTe0bndmJ6gBK67ioqNvCstJ8aTC8RlGX1XBLlcw,4191
|
15
|
-
rclone_api/rclone.py,sha256=
|
15
|
+
rclone_api/rclone.py,sha256=XyA9_EOxdOMFv70KvMYjjjGz6ceBEO8xtZ2bKDTftRg,26388
|
16
16
|
rclone_api/remote.py,sha256=c9hlRKBCg1BFB9MCINaQIoCg10qyAkeqiS4brl8ce-8,343
|
17
17
|
rclone_api/rpath.py,sha256=8ZA_1wxWtskwcy0I8V2VbjKDmzPkiWd8Q2JQSvh-sYE,2586
|
18
|
-
rclone_api/util.py,sha256=
|
19
|
-
rclone_api/walk.py,sha256=
|
18
|
+
rclone_api/util.py,sha256=_cvmHcJPRl2yXw4zgZiop3z-riA_8Ek6S5NDPw8cqSY,4198
|
19
|
+
rclone_api/walk.py,sha256=UaNOE3ICd8k5ouSFZvkVEH4r2GnnrD9TxfwkFcQnayo,3170
|
20
20
|
rclone_api/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
|
21
21
|
rclone_api/cmd/list_files.py,sha256=x8FHODEilwKqwdiU1jdkeJbLwOqUkUQuDWPo2u_zpf0,741
|
22
|
-
rclone_api-1.0.
|
23
|
-
rclone_api-1.0.
|
24
|
-
rclone_api-1.0.
|
25
|
-
rclone_api-1.0.
|
26
|
-
rclone_api-1.0.
|
27
|
-
rclone_api-1.0.
|
22
|
+
rclone_api-1.0.60.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
|
23
|
+
rclone_api-1.0.60.dist-info/METADATA,sha256=lAw28UzFDwclf7UW2ke9ITEoOxicWHtrYChepRjklk0,4489
|
24
|
+
rclone_api-1.0.60.dist-info/WHEEL,sha256=9Hm2OB-j1QcCUq9Jguht7ayGIIZBRTdOXD1qg9cCgPM,109
|
25
|
+
rclone_api-1.0.60.dist-info/entry_points.txt,sha256=XUoTX3m7CWxdj2VAKhEuO0NMOfX2qf-OcEDFwdyk9ZE,72
|
26
|
+
rclone_api-1.0.60.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
|
27
|
+
rclone_api-1.0.60.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|