rclone-api 1.0.6__tar.gz → 1.0.8__tar.gz
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-1.0.6/src/rclone_api.egg-info → rclone_api-1.0.8}/PKG-INFO +1 -1
- {rclone_api-1.0.6 → rclone_api-1.0.8}/pyproject.toml +1 -1
- {rclone_api-1.0.6 → rclone_api-1.0.8}/src/rclone_api/dir.py +2 -2
- rclone_api-1.0.8/src/rclone_api/dir_listing.py +14 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/src/rclone_api/rclone.py +1 -5
- rclone_api-1.0.8/src/rclone_api/walk.py +82 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8/src/rclone_api.egg-info}/PKG-INFO +1 -1
- {rclone_api-1.0.6 → rclone_api-1.0.8}/src/rclone_api.egg-info/SOURCES.txt +1 -0
- rclone_api-1.0.6/src/rclone_api/dir_listing.py +0 -12
- {rclone_api-1.0.6 → rclone_api-1.0.8}/.gitignore +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/.pylintrc +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/.vscode/launch.json +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/.vscode/settings.json +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/.vscode/tasks.json +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/LICENSE +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/MANIFEST.in +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/README.md +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/clean +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/install +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/lint +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/requirements.testing.txt +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/setup.cfg +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/setup.py +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/src/rclone_api/__init__.py +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/src/rclone_api/assets/example.txt +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/src/rclone_api/cli.py +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/src/rclone_api/config.py +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/src/rclone_api/file.py +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/src/rclone_api/remote.py +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/src/rclone_api/rpath.py +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/src/rclone_api/types.py +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/src/rclone_api/util.py +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/src/rclone_api.egg-info/dependency_links.txt +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/src/rclone_api.egg-info/top_level.txt +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/test +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/tests/test_simple.py +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/tox.ini +0 -0
- {rclone_api-1.0.6 → rclone_api-1.0.8}/upload_package.sh +0 -0
@@ -8,10 +8,10 @@ class Dir:
|
|
8
8
|
def __init__(self, path: RPath) -> None:
|
9
9
|
self.path = path
|
10
10
|
|
11
|
-
def ls(self) -> DirListing:
|
11
|
+
def ls(self, max_depth: int = 0) -> DirListing:
|
12
12
|
"""List files and directories in the given path."""
|
13
13
|
assert self.path.rclone is not None
|
14
|
-
return self.path.rclone.ls(self.path.path)
|
14
|
+
return self.path.rclone.ls(self.path.path, max_depth=max_depth)
|
15
15
|
|
16
16
|
def __str__(self) -> str:
|
17
17
|
return str(self.path)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# from rclone_api.dir import Dir
|
2
|
+
# from rclone_api.file import File
|
3
|
+
from rclone_api.rpath import RPath
|
4
|
+
|
5
|
+
|
6
|
+
class DirListing:
|
7
|
+
"""Remote file dataclass."""
|
8
|
+
|
9
|
+
def __init__(self, dirs_and_files: list[RPath]) -> None:
|
10
|
+
from rclone_api.dir import Dir
|
11
|
+
from rclone_api.file import File
|
12
|
+
|
13
|
+
self.dirs: list[Dir] = [Dir(d) for d in dirs_and_files if d.is_dir]
|
14
|
+
self.files: list[File] = [File(f) for f in dirs_and_files if not f.is_dir]
|
@@ -5,9 +5,7 @@ Unit test file.
|
|
5
5
|
import subprocess
|
6
6
|
from pathlib import Path
|
7
7
|
|
8
|
-
from rclone_api.dir import Dir
|
9
8
|
from rclone_api.dir_listing import DirListing
|
10
|
-
from rclone_api.file import File
|
11
9
|
from rclone_api.remote import Remote
|
12
10
|
from rclone_api.rpath import RPath
|
13
11
|
from rclone_api.types import Config, RcloneExec
|
@@ -46,9 +44,7 @@ class Rclone:
|
|
46
44
|
paths: list[RPath] = RPath.from_json_str(text)
|
47
45
|
for o in paths:
|
48
46
|
o.set_rclone(self)
|
49
|
-
|
50
|
-
files: list[File] = [File(o) for o in paths if not o.is_dir]
|
51
|
-
return DirListing(dirs=dirs, files=files)
|
47
|
+
return DirListing(paths)
|
52
48
|
|
53
49
|
def listremotes(self) -> list[Remote]:
|
54
50
|
cmd = ["listremotes"]
|
@@ -0,0 +1,82 @@
|
|
1
|
+
from concurrent.futures import ThreadPoolExecutor
|
2
|
+
from queue import Empty, Queue
|
3
|
+
from typing import Generator
|
4
|
+
|
5
|
+
from rclone_api import Dir
|
6
|
+
from rclone_api.dir_listing import DirListing
|
7
|
+
|
8
|
+
|
9
|
+
def walk(
|
10
|
+
dir: Dir, max_depth: int = -1, max_workers: int = 4
|
11
|
+
) -> Generator[DirListing, None, None]:
|
12
|
+
"""Walk through the given directory recursively.
|
13
|
+
|
14
|
+
Args:
|
15
|
+
dir: Directory to walk through
|
16
|
+
max_depth: Maximum depth to traverse (-1 for unlimited)
|
17
|
+
|
18
|
+
Yields:
|
19
|
+
DirListing: Directory listing for each directory encountered
|
20
|
+
"""
|
21
|
+
pending: Queue[tuple[Dir | None, int]] = Queue()
|
22
|
+
results: Queue[DirListing | Exception] = Queue()
|
23
|
+
|
24
|
+
def worker():
|
25
|
+
while True:
|
26
|
+
try:
|
27
|
+
# Add timeout to allow checking for sentinel value
|
28
|
+
try:
|
29
|
+
current_dir, depth = pending.get(timeout=0.1)
|
30
|
+
except Empty:
|
31
|
+
continue
|
32
|
+
|
33
|
+
# Check for sentinel value
|
34
|
+
if current_dir is None:
|
35
|
+
pending.task_done()
|
36
|
+
break
|
37
|
+
|
38
|
+
listing = current_dir.ls()
|
39
|
+
results.put(listing)
|
40
|
+
|
41
|
+
if max_depth == -1 or depth < max_depth:
|
42
|
+
for d in listing.dirs:
|
43
|
+
pending.put((d, depth + 1))
|
44
|
+
|
45
|
+
pending.task_done()
|
46
|
+
except Exception as e:
|
47
|
+
results.put(e)
|
48
|
+
pending.task_done()
|
49
|
+
break
|
50
|
+
return None
|
51
|
+
|
52
|
+
# Start workers
|
53
|
+
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
54
|
+
workers = [executor.submit(worker) for _ in range(max_workers)]
|
55
|
+
|
56
|
+
# Start walking
|
57
|
+
pending.put((dir, 0))
|
58
|
+
|
59
|
+
# Process results while workers are running
|
60
|
+
completed = 0
|
61
|
+
while completed < max_workers:
|
62
|
+
try:
|
63
|
+
result = results.get(timeout=0.1)
|
64
|
+
if isinstance(result, Exception):
|
65
|
+
# Propagate exception
|
66
|
+
raise result
|
67
|
+
yield result
|
68
|
+
except Empty:
|
69
|
+
# Check if any workers have completed
|
70
|
+
completed = sum(1 for w in workers if w.done())
|
71
|
+
continue
|
72
|
+
|
73
|
+
# Signal workers to stop
|
74
|
+
for _ in range(max_workers):
|
75
|
+
pending.put((None, 0))
|
76
|
+
|
77
|
+
# Drain any remaining results
|
78
|
+
while not results.empty():
|
79
|
+
result = results.get()
|
80
|
+
if isinstance(result, Exception):
|
81
|
+
raise result
|
82
|
+
yield result
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|