rclone-api 1.0.58__py2.py3-none-any.whl → 1.0.60__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/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
|