rclone-api 1.0.12__py2.py3-none-any.whl → 1.0.13__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/dir.py +7 -1
- rclone_api/file.py +1 -4
- rclone_api/rclone.py +97 -90
- rclone_api/rpath.py +15 -8
- rclone_api/util.py +54 -2
- rclone_api-1.0.13.dist-info/METADATA +34 -0
- {rclone_api-1.0.12.dist-info → rclone_api-1.0.13.dist-info}/RECORD +10 -10
- rclone_api-1.0.12.dist-info/METADATA +0 -36
- {rclone_api-1.0.12.dist-info → rclone_api-1.0.13.dist-info}/LICENSE +0 -0
- {rclone_api-1.0.12.dist-info → rclone_api-1.0.13.dist-info}/WHEEL +0 -0
- {rclone_api-1.0.12.dist-info → rclone_api-1.0.13.dist-info}/top_level.txt +0 -0
rclone_api/dir.py
CHANGED
@@ -8,6 +8,10 @@ from rclone_api.rpath import RPath
|
|
8
8
|
class Dir:
|
9
9
|
"""Remote file dataclass."""
|
10
10
|
|
11
|
+
@property
|
12
|
+
def remote(self) -> Remote:
|
13
|
+
return self.path.remote
|
14
|
+
|
11
15
|
def __init__(self, path: RPath | Remote) -> None:
|
12
16
|
"""Initialize Dir with either an RPath or Remote.
|
13
17
|
|
@@ -17,6 +21,7 @@ class Dir:
|
|
17
21
|
if isinstance(path, Remote):
|
18
22
|
# Need to create an RPath for the Remote's root
|
19
23
|
self.path = RPath(
|
24
|
+
remote=path,
|
20
25
|
path=str(path),
|
21
26
|
name=str(path),
|
22
27
|
size=0,
|
@@ -32,7 +37,8 @@ class Dir:
|
|
32
37
|
def ls(self, max_depth: int = 0) -> DirListing:
|
33
38
|
"""List files and directories in the given path."""
|
34
39
|
assert self.path.rclone is not None
|
35
|
-
|
40
|
+
dir = Dir(self.path)
|
41
|
+
return self.path.rclone.ls(dir, max_depth=max_depth)
|
36
42
|
|
37
43
|
def walk(self, max_depth: int = -1) -> Generator[DirListing, None, None]:
|
38
44
|
"""List files and directories in the given path."""
|
rclone_api/file.py
CHANGED
rclone_api/rclone.py
CHANGED
@@ -1,90 +1,97 @@
|
|
1
|
-
"""
|
2
|
-
Unit test file.
|
3
|
-
"""
|
4
|
-
|
5
|
-
import subprocess
|
6
|
-
from pathlib import Path
|
7
|
-
from typing import Generator
|
8
|
-
|
9
|
-
from rclone_api import Dir
|
10
|
-
from rclone_api.dir_listing import DirListing
|
11
|
-
from rclone_api.remote import Remote
|
12
|
-
from rclone_api.rpath import RPath
|
13
|
-
from rclone_api.types import Config, RcloneExec
|
14
|
-
from rclone_api.util import get_rclone_exe
|
15
|
-
from rclone_api.walk import walk
|
16
|
-
|
17
|
-
|
18
|
-
class Rclone:
|
19
|
-
def __init__(
|
20
|
-
self, rclone_conf: Path | Config, rclone_exe: Path | None = None
|
21
|
-
) -> None:
|
22
|
-
if isinstance(rclone_conf, Path):
|
23
|
-
if not rclone_conf.exists():
|
24
|
-
raise ValueError(f"Rclone config file not found: {rclone_conf}")
|
25
|
-
self._exec = RcloneExec(rclone_conf, get_rclone_exe(rclone_exe))
|
26
|
-
|
27
|
-
def _run(self, cmd: list[str]) -> subprocess.CompletedProcess:
|
28
|
-
return self._exec.execute(cmd)
|
29
|
-
|
30
|
-
def ls(self, path:
|
31
|
-
"""List files in the given path.
|
32
|
-
|
33
|
-
Args:
|
34
|
-
path: Remote path or Remote object to list
|
35
|
-
max_depth: Maximum recursion depth (0 means no recursion)
|
36
|
-
|
37
|
-
Returns:
|
38
|
-
List of File objects found at the path
|
39
|
-
"""
|
40
|
-
cmd = ["lsjson"]
|
41
|
-
if max_depth
|
42
|
-
cmd.
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
1
|
+
"""
|
2
|
+
Unit test file.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import subprocess
|
6
|
+
from pathlib import Path
|
7
|
+
from typing import Generator
|
8
|
+
|
9
|
+
from rclone_api import Dir
|
10
|
+
from rclone_api.dir_listing import DirListing
|
11
|
+
from rclone_api.remote import Remote
|
12
|
+
from rclone_api.rpath import RPath
|
13
|
+
from rclone_api.types import Config, RcloneExec
|
14
|
+
from rclone_api.util import get_rclone_exe
|
15
|
+
from rclone_api.walk import walk
|
16
|
+
|
17
|
+
|
18
|
+
class Rclone:
|
19
|
+
def __init__(
|
20
|
+
self, rclone_conf: Path | Config, rclone_exe: Path | None = None
|
21
|
+
) -> None:
|
22
|
+
if isinstance(rclone_conf, Path):
|
23
|
+
if not rclone_conf.exists():
|
24
|
+
raise ValueError(f"Rclone config file not found: {rclone_conf}")
|
25
|
+
self._exec = RcloneExec(rclone_conf, get_rclone_exe(rclone_exe))
|
26
|
+
|
27
|
+
def _run(self, cmd: list[str]) -> subprocess.CompletedProcess:
|
28
|
+
return self._exec.execute(cmd)
|
29
|
+
|
30
|
+
def ls(self, path: Dir | Remote | str, max_depth: int | None = None) -> DirListing:
|
31
|
+
"""List files in the given path.
|
32
|
+
|
33
|
+
Args:
|
34
|
+
path: Remote path or Remote object to list
|
35
|
+
max_depth: Maximum recursion depth (0 means no recursion)
|
36
|
+
|
37
|
+
Returns:
|
38
|
+
List of File objects found at the path
|
39
|
+
"""
|
40
|
+
cmd = ["lsjson"]
|
41
|
+
if max_depth is not None:
|
42
|
+
cmd.append("--recursive")
|
43
|
+
if max_depth > -1:
|
44
|
+
cmd.append("--max-depth")
|
45
|
+
cmd.append(str(max_depth))
|
46
|
+
cmd.append(str(path))
|
47
|
+
remote = path.remote if isinstance(path, Dir) else path
|
48
|
+
assert isinstance(remote, Remote)
|
49
|
+
|
50
|
+
cp = self._run(cmd)
|
51
|
+
text = cp.stdout
|
52
|
+
paths: list[RPath] = RPath.from_json_str(text, remote)
|
53
|
+
for o in paths:
|
54
|
+
o.set_rclone(self)
|
55
|
+
return DirListing(paths)
|
56
|
+
|
57
|
+
def listremotes(self) -> list[Remote]:
|
58
|
+
cmd = ["listremotes"]
|
59
|
+
cp = self._run(cmd)
|
60
|
+
text: str = cp.stdout
|
61
|
+
tmp = text.splitlines()
|
62
|
+
tmp = [t.strip() for t in tmp]
|
63
|
+
# strip out ":" from the end
|
64
|
+
tmp = [t.replace(":", "") for t in tmp]
|
65
|
+
out = [Remote(name=t, rclone=self) for t in tmp]
|
66
|
+
return out
|
67
|
+
|
68
|
+
def walk(
|
69
|
+
self, path: Dir | Remote, max_depth: int = -1
|
70
|
+
) -> Generator[DirListing, None, None]:
|
71
|
+
"""Walk through the given path recursively.
|
72
|
+
|
73
|
+
Args:
|
74
|
+
path: Remote path or Remote object to walk through
|
75
|
+
max_depth: Maximum depth to traverse (-1 for unlimited)
|
76
|
+
|
77
|
+
Yields:
|
78
|
+
DirListing: Directory listing for each directory encountered
|
79
|
+
"""
|
80
|
+
if isinstance(path, Dir):
|
81
|
+
# Create a Remote object for the path
|
82
|
+
remote = path.remote
|
83
|
+
rpath = RPath(
|
84
|
+
remote=remote,
|
85
|
+
path=path.path.path,
|
86
|
+
name=path.path.name,
|
87
|
+
size=0,
|
88
|
+
mime_type="inode/directory",
|
89
|
+
mod_time="",
|
90
|
+
is_dir=True,
|
91
|
+
)
|
92
|
+
rpath.set_rclone(self)
|
93
|
+
dir_obj = Dir(rpath)
|
94
|
+
else:
|
95
|
+
dir_obj = Dir(path)
|
96
|
+
|
97
|
+
yield from walk(dir_obj, max_depth=max_depth)
|
rclone_api/rpath.py
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
import json
|
2
2
|
from typing import Any
|
3
3
|
|
4
|
+
from rclone_api.remote import Remote
|
5
|
+
|
4
6
|
|
5
7
|
class RPath:
|
6
8
|
"""Remote file dataclass."""
|
7
9
|
|
8
10
|
def __init__(
|
9
11
|
self,
|
12
|
+
remote: Remote,
|
10
13
|
path: str,
|
11
14
|
name: str,
|
12
15
|
size: int,
|
@@ -16,6 +19,10 @@ class RPath:
|
|
16
19
|
) -> None:
|
17
20
|
from rclone_api.rclone import Rclone
|
18
21
|
|
22
|
+
if "dst:" in path:
|
23
|
+
raise ValueError(f"Invalid path: {path}")
|
24
|
+
|
25
|
+
self.remote = remote
|
19
26
|
self.path = path
|
20
27
|
self.name = name
|
21
28
|
self.size = size
|
@@ -32,9 +39,10 @@ class RPath:
|
|
32
39
|
self.rclone = rclone
|
33
40
|
|
34
41
|
@staticmethod
|
35
|
-
def from_dict(data: dict) -> "RPath":
|
42
|
+
def from_dict(data: dict, remote: Remote) -> "RPath":
|
36
43
|
"""Create a File from a dictionary."""
|
37
44
|
return RPath(
|
45
|
+
remote,
|
38
46
|
data["Path"],
|
39
47
|
data["Name"],
|
40
48
|
data["Size"],
|
@@ -45,21 +53,21 @@ class RPath:
|
|
45
53
|
)
|
46
54
|
|
47
55
|
@staticmethod
|
48
|
-
def from_array(data: list[dict]) -> list["RPath"]:
|
56
|
+
def from_array(data: list[dict], remote: Remote) -> list["RPath"]:
|
49
57
|
"""Create a File from a dictionary."""
|
50
58
|
out: list[RPath] = []
|
51
59
|
for d in data:
|
52
|
-
file: RPath = RPath.from_dict(d)
|
60
|
+
file: RPath = RPath.from_dict(d, remote)
|
53
61
|
out.append(file)
|
54
62
|
return out
|
55
63
|
|
56
64
|
@staticmethod
|
57
|
-
def from_json_str(json_str: str) -> list["RPath"]:
|
65
|
+
def from_json_str(json_str: str, remote: Remote) -> list["RPath"]:
|
58
66
|
"""Create a File from a JSON string."""
|
59
67
|
json_obj = json.loads(json_str)
|
60
68
|
if isinstance(json_obj, dict):
|
61
|
-
return [RPath.from_dict(json_obj)]
|
62
|
-
return RPath.from_array(json_obj)
|
69
|
+
return [RPath.from_dict(json_obj, remote)]
|
70
|
+
return RPath.from_array(json_obj, remote)
|
63
71
|
|
64
72
|
def to_json(self) -> dict:
|
65
73
|
return {
|
@@ -73,5 +81,4 @@ class RPath:
|
|
73
81
|
}
|
74
82
|
|
75
83
|
def __str__(self) -> str:
|
76
|
-
|
77
|
-
return json.dumps(out)
|
84
|
+
return f"{self.remote.name}:{self.path}"
|
rclone_api/util.py
CHANGED
@@ -1,10 +1,61 @@
|
|
1
|
+
import os
|
1
2
|
import shutil
|
2
3
|
import subprocess
|
3
4
|
from pathlib import Path
|
4
5
|
from tempfile import TemporaryDirectory
|
6
|
+
from typing import Any
|
5
7
|
|
8
|
+
from rclone_api.dir import Dir
|
9
|
+
from rclone_api.remote import Remote
|
10
|
+
from rclone_api.rpath import RPath
|
6
11
|
from rclone_api.types import Config
|
7
12
|
|
13
|
+
# from .rclone import Rclone
|
14
|
+
|
15
|
+
|
16
|
+
def to_path(item: Dir | Remote | str, rclone: Any) -> RPath:
|
17
|
+
from rclone_api.rclone import Rclone
|
18
|
+
|
19
|
+
assert isinstance(rclone, Rclone)
|
20
|
+
# if str then it will be remote:path
|
21
|
+
if isinstance(item, str):
|
22
|
+
# return RPath(item)
|
23
|
+
# remote_name: str = item.split(":")[0]
|
24
|
+
parts = item.split(":")
|
25
|
+
remote_name = parts[0]
|
26
|
+
path = ":".join(parts[1:])
|
27
|
+
remote = Remote(name=remote_name, rclone=rclone)
|
28
|
+
return RPath(
|
29
|
+
remote=remote,
|
30
|
+
path=path,
|
31
|
+
name="",
|
32
|
+
size=0,
|
33
|
+
mime_type="",
|
34
|
+
mod_time="",
|
35
|
+
is_dir=True,
|
36
|
+
)
|
37
|
+
elif isinstance(item, Dir):
|
38
|
+
return item.path
|
39
|
+
elif isinstance(item, Remote):
|
40
|
+
return RPath(
|
41
|
+
remote=item,
|
42
|
+
path=str(item),
|
43
|
+
name=str(item),
|
44
|
+
size=0,
|
45
|
+
mime_type="inode/directory",
|
46
|
+
mod_time="",
|
47
|
+
is_dir=True,
|
48
|
+
)
|
49
|
+
else:
|
50
|
+
raise ValueError(f"Invalid type for item: {type(item)}")
|
51
|
+
|
52
|
+
|
53
|
+
def _get_verbose(verbose: bool | None) -> bool:
|
54
|
+
if verbose is not None:
|
55
|
+
return verbose
|
56
|
+
# get it from the environment
|
57
|
+
return bool(int(os.getenv("RCLONE_API_VERBOSE", "0")))
|
58
|
+
|
8
59
|
|
9
60
|
def get_rclone_exe(rclone_exe: Path | None) -> Path:
|
10
61
|
if rclone_exe is None:
|
@@ -20,10 +71,11 @@ def rclone_execute(
|
|
20
71
|
cmd: list[str],
|
21
72
|
rclone_conf: Path | Config,
|
22
73
|
rclone_exe: Path,
|
23
|
-
verbose: bool =
|
74
|
+
verbose: bool | None = None,
|
24
75
|
) -> subprocess.CompletedProcess:
|
25
|
-
print(subprocess.list2cmdline(cmd))
|
26
76
|
tempdir: TemporaryDirectory | None = None
|
77
|
+
verbose = _get_verbose(verbose)
|
78
|
+
assert verbose is not None
|
27
79
|
|
28
80
|
try:
|
29
81
|
if isinstance(rclone_conf, Config):
|
@@ -0,0 +1,34 @@
|
|
1
|
+
Metadata-Version: 2.2
|
2
|
+
Name: rclone_api
|
3
|
+
Version: 1.0.13
|
4
|
+
Summary: rclone api in python
|
5
|
+
Home-page: https://github.com/zackees/rclone-api
|
6
|
+
Maintainer: Zachary Vorhies
|
7
|
+
License: BSD 3-Clause License
|
8
|
+
Keywords: template-python-cmd
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
10
|
+
Requires-Python: >=3.10
|
11
|
+
Description-Content-Type: text/markdown
|
12
|
+
License-File: LICENSE
|
13
|
+
Requires-Dist: python-dotenv>=1.0.0
|
14
|
+
Dynamic: home-page
|
15
|
+
Dynamic: maintainer
|
16
|
+
|
17
|
+
# rclone-api
|
18
|
+
|
19
|
+
[](https://github.com/zackees/rclone-api/actions/workflows/lint.yml)
|
20
|
+
[](https://github.com/zackees/rclone-api/actions/workflows/push_macos.yml)
|
21
|
+
[](https://github.com/zackees/rclone-api/actions/workflows/push_ubuntu.yml)
|
22
|
+
[](https://github.com/zackees/rclone-api/actions/workflows/push_win.yml)
|
23
|
+
|
24
|
+
Api version of rclone. It's well tested. It's just released so this readme is a little to be desired.
|
25
|
+
|
26
|
+
To develop software, run `. ./activate`
|
27
|
+
|
28
|
+
# Windows
|
29
|
+
|
30
|
+
This environment requires you to use `git-bash`.
|
31
|
+
|
32
|
+
# Linting
|
33
|
+
|
34
|
+
Run `./lint`
|
@@ -1,18 +1,18 @@
|
|
1
1
|
rclone_api/__init__.py,sha256=YH7KQaPwUiJWJiRf0NRKD7XHhMXsxWdXDjt9WLSwdjA,265
|
2
2
|
rclone_api/cli.py,sha256=dibfAZIh0kXWsBbfp3onKLjyZXo54mTzDjUdzJlDlWo,231
|
3
3
|
rclone_api/config.py,sha256=UujjDNcpwgipgdyFPMhC3zikvlsrurUvvZiwXm5NlPg,471
|
4
|
-
rclone_api/dir.py,sha256=
|
4
|
+
rclone_api/dir.py,sha256=tu0-Yy1s3O1MVY7Nkb6NOh6t9dXJ9DQvqOZVlWIexrI,1629
|
5
5
|
rclone_api/dir_listing.py,sha256=NWleKHCCRW7_eh9JfRwE6r3QbjmiHD5ZEGQcd2vm4uY,458
|
6
|
-
rclone_api/file.py,sha256=
|
7
|
-
rclone_api/rclone.py,sha256=
|
6
|
+
rclone_api/file.py,sha256=nJwJT7v2KwY_8g7UB3Y7O6pFfHn3nri9dJ3GS-cN3EE,874
|
7
|
+
rclone_api/rclone.py,sha256=upMqUxYKC3LCjHlhI66_IdOD1t5PKo7V64sSdw0vAlk,3074
|
8
8
|
rclone_api/remote.py,sha256=c9hlRKBCg1BFB9MCINaQIoCg10qyAkeqiS4brl8ce-8,343
|
9
|
-
rclone_api/rpath.py,sha256=
|
9
|
+
rclone_api/rpath.py,sha256=QHD5zDjDIkivs3kCRYtTL8mP-DspXIvBVYWHSf_Y6Rg,2263
|
10
10
|
rclone_api/types.py,sha256=Yp15HrjwZonSQhE323R0WP7fA4NWqKjAfM7z3OwHpWI,518
|
11
|
-
rclone_api/util.py,sha256=
|
11
|
+
rclone_api/util.py,sha256=xYUj0B9W5HDauTHh7Z8cnEQsTEuXvRcVNLtk3ZanlGM,2894
|
12
12
|
rclone_api/walk.py,sha256=3GKnu9P5aPNiftfoi52U7wo1wixZxwxdqqS0_IlMGCE,2816
|
13
13
|
rclone_api/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
|
14
|
-
rclone_api-1.0.
|
15
|
-
rclone_api-1.0.
|
16
|
-
rclone_api-1.0.
|
17
|
-
rclone_api-1.0.
|
18
|
-
rclone_api-1.0.
|
14
|
+
rclone_api-1.0.13.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
|
15
|
+
rclone_api-1.0.13.dist-info/METADATA,sha256=yEL4GNzAc-CwJt-rJVmDAl08L13RqytSSs1ZQR4KMA0,1375
|
16
|
+
rclone_api-1.0.13.dist-info/WHEEL,sha256=9Hm2OB-j1QcCUq9Jguht7ayGIIZBRTdOXD1qg9cCgPM,109
|
17
|
+
rclone_api-1.0.13.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
|
18
|
+
rclone_api-1.0.13.dist-info/RECORD,,
|
@@ -1,36 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.2
|
2
|
-
Name: rclone_api
|
3
|
-
Version: 1.0.12
|
4
|
-
Summary: rclone api in python
|
5
|
-
Home-page: https://github.com/zackees/rclone-api
|
6
|
-
Maintainer: Zachary Vorhies
|
7
|
-
License: BSD 3-Clause License
|
8
|
-
Keywords: template-python-cmd
|
9
|
-
Classifier: Programming Language :: Python :: 3
|
10
|
-
Requires-Python: >=3.7
|
11
|
-
Description-Content-Type: text/markdown
|
12
|
-
License-File: LICENSE
|
13
|
-
Dynamic: home-page
|
14
|
-
Dynamic: maintainer
|
15
|
-
|
16
|
-
# template-python-cmd
|
17
|
-
A template for quickly making a python lib that has a command line program attached
|
18
|
-
|
19
|
-
[](../../actions/workflows/lint.yml)
|
20
|
-
|
21
|
-
[](../../actions/workflows/push_macos.yml)
|
22
|
-
[](../../actions/workflows/push_ubuntu.yml)
|
23
|
-
[](../../actions/workflows/push_win.yml)
|
24
|
-
|
25
|
-
Replace `template-python-cmd` and `template_python_cmd` with your command. Run tox until it's
|
26
|
-
correct.
|
27
|
-
|
28
|
-
To develop software, run `. ./activate.sh`
|
29
|
-
|
30
|
-
# Windows
|
31
|
-
|
32
|
-
This environment requires you to use `git-bash`.
|
33
|
-
|
34
|
-
# Linting
|
35
|
-
|
36
|
-
Run `./lint.sh` to find linting errors using `pylint`, `flake8` and `mypy`.
|
File without changes
|
File without changes
|
File without changes
|