rclone-api 1.0.0__py2.py3-none-any.whl → 1.0.2__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 CHANGED
@@ -0,0 +1,4 @@
1
+ from .rclone import File, Rclone
2
+ from .types import Config, Remote
3
+
4
+ __all__ = ["Rclone", "File", "Config", "Remote"]
rclone_api/file.py ADDED
@@ -0,0 +1,77 @@
1
+ import json
2
+ from typing import Any
3
+
4
+
5
+ class File:
6
+ """Remote file dataclass."""
7
+
8
+ def __init__(
9
+ self,
10
+ path: str,
11
+ name: str,
12
+ size: int,
13
+ mime_type: str,
14
+ mod_time: str,
15
+ is_dir: bool,
16
+ ) -> None:
17
+ from rclone_api.rclone import Rclone
18
+
19
+ self.path = path
20
+ self.name = name
21
+ self.size = size
22
+ self.mime_type = mime_type
23
+ self.mod_time = mod_time
24
+ self.is_dir = is_dir
25
+ self.rclone: Rclone | None = None
26
+
27
+ def set_rclone(self, rclone: Any) -> None:
28
+ """Set the rclone object."""
29
+ from rclone_api.rclone import Rclone
30
+
31
+ assert isinstance(rclone, Rclone)
32
+ self.rclone = rclone
33
+
34
+ @staticmethod
35
+ def from_dict(data: dict) -> "File":
36
+ """Create a File from a dictionary."""
37
+ return File(
38
+ data["Path"],
39
+ data["Name"],
40
+ data["Size"],
41
+ data["MimeType"],
42
+ data["ModTime"],
43
+ data["IsDir"],
44
+ # data["IsBucket"],
45
+ )
46
+
47
+ @staticmethod
48
+ def from_array(data: list[dict]) -> list["File"]:
49
+ """Create a File from a dictionary."""
50
+ out: list[File] = []
51
+ for d in data:
52
+ file: File = File.from_dict(d)
53
+ out.append(file)
54
+ return out
55
+
56
+ @staticmethod
57
+ def from_json_str(json_str: str) -> list["File"]:
58
+ """Create a File from a JSON string."""
59
+ json_obj = json.loads(json_str)
60
+ if isinstance(json_obj, dict):
61
+ return [File.from_dict(json_obj)]
62
+ return File.from_array(json_obj)
63
+
64
+ def to_json(self) -> dict:
65
+ return {
66
+ "Path": self.path,
67
+ "Name": self.name,
68
+ "Size": self.size,
69
+ "MimeType": self.mime_type,
70
+ "ModTime": self.mod_time,
71
+ "IsDir": self.is_dir,
72
+ # "IsBucket": self.is_bucket,
73
+ }
74
+
75
+ def __str__(self) -> str:
76
+ out = self.to_json()
77
+ return json.dumps(out)
rclone_api/rclone.py CHANGED
@@ -2,159 +2,42 @@
2
2
  Unit test file.
3
3
  """
4
4
 
5
- import json
6
- import shutil
7
5
  import subprocess
8
- from dataclasses import dataclass
9
6
  from pathlib import Path
10
- from tempfile import TemporaryDirectory
11
7
 
12
-
13
- @dataclass
14
- class RcloneConfig:
15
- """Rclone configuration dataclass."""
16
-
17
- text: str
18
-
19
-
20
- def _rclone_execute(
21
- cmd: list[str],
22
- rclone_conf: Path | RcloneConfig,
23
- rclone_exe: Path,
24
- verbose: bool = False,
25
- ) -> subprocess.CompletedProcess:
26
- print(subprocess.list2cmdline(cmd))
27
- tempdir: TemporaryDirectory | None = None
28
-
29
- try:
30
- if isinstance(rclone_conf, RcloneConfig):
31
- tempdir = TemporaryDirectory()
32
- tmpfile = Path(tempdir.name) / "rclone.conf"
33
- tmpfile.write_text(rclone_conf.text, encoding="utf-8")
34
- rclone_conf = tmpfile
35
- cmd = (
36
- [str(rclone_exe.resolve())] + ["--config", str(rclone_conf.resolve())] + cmd
37
- )
38
- if verbose:
39
- cmd_str = subprocess.list2cmdline(cmd)
40
- print(f"Running: {cmd_str}")
41
- cp = subprocess.run(
42
- cmd, capture_output=True, encoding="utf-8", check=True, shell=False
43
- )
44
- return cp
45
- finally:
46
- if tempdir:
47
- try:
48
- tempdir.cleanup()
49
- except Exception as e:
50
- print(f"Error cleaning up tempdir: {e}")
51
-
52
-
53
- @dataclass
54
- class RcloneExec:
55
- """Rclone execution dataclass."""
56
-
57
- rclone_config: Path | RcloneConfig
58
- rclone_exe: Path
59
-
60
- def execute(self, cmd: list[str]) -> subprocess.CompletedProcess:
61
- """Execute rclone command."""
62
- return _rclone_execute(cmd, self.rclone_config, self.rclone_exe)
63
-
64
-
65
- @dataclass
66
- class RemoteFile:
67
- """Remote file dataclass."""
68
-
69
- path: str
70
- name: str
71
- size: int
72
- mime_type: str
73
- mod_time: str
74
- is_dir: bool
75
- # is_bucket: bool
76
-
77
- @staticmethod
78
- def from_dict(data: dict) -> "RemoteFile":
79
- """Create a RemoteFile from a dictionary."""
80
- return RemoteFile(
81
- data["Path"],
82
- data["Name"],
83
- data["Size"],
84
- data["MimeType"],
85
- data["ModTime"],
86
- data["IsDir"],
87
- # data["IsBucket"],
88
- )
89
-
90
- @staticmethod
91
- def from_array(data: list[dict]) -> list["RemoteFile"]:
92
- """Create a RemoteFile from a dictionary."""
93
- out: list[RemoteFile] = []
94
- for d in data:
95
- file: RemoteFile = RemoteFile.from_dict(d)
96
- out.append(file)
97
- return out
98
-
99
- @staticmethod
100
- def from_json_str(json_str: str) -> list["RemoteFile"]:
101
- """Create a RemoteFile from a JSON string."""
102
- json_obj = json.loads(json_str)
103
- if isinstance(json_obj, dict):
104
- return [RemoteFile.from_dict(json_obj)]
105
- return RemoteFile.from_array(json_obj)
106
-
107
- def to_json(self) -> dict:
108
- return {
109
- "Path": self.path,
110
- "Name": self.name,
111
- "Size": self.size,
112
- "MimeType": self.mime_type,
113
- "ModTime": self.mod_time,
114
- "IsDir": self.is_dir,
115
- # "IsBucket": self.is_bucket,
116
- }
117
-
118
- def __str__(self) -> str:
119
- out = self.to_json()
120
- return json.dumps(out)
121
-
122
-
123
- def _get_rclone_exe(rclone_exe: Path | None) -> Path:
124
- if rclone_exe is None:
125
-
126
- rclone_which_path = shutil.which("rclone")
127
- if rclone_which_path is None:
128
- raise ValueError("rclone executable not found")
129
- return Path(rclone_which_path)
130
- return rclone_exe
8
+ from rclone_api.file import File
9
+ from rclone_api.types import Config, RcloneExec, Remote
10
+ from rclone_api.util import get_rclone_exe
131
11
 
132
12
 
133
13
  class Rclone:
134
14
  def __init__(
135
- self, rclone_conf: Path | RcloneConfig, rclone_exe: Path | None = None
15
+ self, rclone_conf: Path | Config, rclone_exe: Path | None = None
136
16
  ) -> None:
137
17
  if isinstance(rclone_conf, Path):
138
18
  if not rclone_conf.exists():
139
19
  raise ValueError(f"Rclone config file not found: {rclone_conf}")
140
- self._exec = RcloneExec(rclone_conf, _get_rclone_exe(rclone_exe))
20
+ self._exec = RcloneExec(rclone_conf, get_rclone_exe(rclone_exe))
141
21
 
142
22
  def _run(self, cmd: list[str]) -> subprocess.CompletedProcess:
143
23
  return self._exec.execute(cmd)
144
24
 
145
- def ls(self, path: str) -> list[RemoteFile]:
146
- cmd = ["lsjson", path]
25
+ def ls(self, path: str | Remote) -> list[File]:
26
+ cmd = ["lsjson", str(path)]
147
27
  cp = self._run(cmd)
148
28
  text = cp.stdout
149
- out: list[RemoteFile] = RemoteFile.from_json_str(text)
29
+ out: list[File] = File.from_json_str(text)
30
+ for o in out:
31
+ o.set_rclone(self)
150
32
  return out
151
33
 
152
- def listremotes(self) -> list[str]:
34
+ def listremotes(self) -> list[Remote]:
153
35
  cmd = ["listremotes"]
154
36
  cp = self._run(cmd)
155
- text = cp.stdout
156
- out = text.splitlines()
157
- out = [o.strip() for o in out]
37
+ text: str = cp.stdout
38
+ tmp = text.splitlines()
39
+ tmp = [t.strip() for t in tmp]
158
40
  # strip out ":" from the end
159
- out = [o.replace(":", "") for o in out]
41
+ tmp = [t.replace(":", "") for t in tmp]
42
+ out = [Remote(name=t) for t in tmp]
160
43
  return out
rclone_api/types.py ADDED
@@ -0,0 +1,34 @@
1
+ import subprocess
2
+ from dataclasses import dataclass
3
+ from pathlib import Path
4
+
5
+
6
+ @dataclass
7
+ class Config:
8
+ """Rclone configuration dataclass."""
9
+
10
+ text: str
11
+
12
+
13
+ @dataclass
14
+ class RcloneExec:
15
+ """Rclone execution dataclass."""
16
+
17
+ rclone_config: Path | Config
18
+ rclone_exe: Path
19
+
20
+ def execute(self, cmd: list[str]) -> subprocess.CompletedProcess:
21
+ """Execute rclone command."""
22
+ from rclone_api.util import rclone_execute
23
+
24
+ return rclone_execute(cmd, self.rclone_config, self.rclone_exe)
25
+
26
+
27
+ class Remote:
28
+ """Remote dataclass."""
29
+
30
+ def __init__(self, name: str) -> None:
31
+ self.name = name
32
+
33
+ def __str__(self) -> str:
34
+ return f"{self.name}:"
rclone_api/util.py ADDED
@@ -0,0 +1,49 @@
1
+ import shutil
2
+ import subprocess
3
+ from pathlib import Path
4
+ from tempfile import TemporaryDirectory
5
+
6
+ from rclone_api.types import Config
7
+
8
+
9
+ def get_rclone_exe(rclone_exe: Path | None) -> Path:
10
+ if rclone_exe is None:
11
+
12
+ rclone_which_path = shutil.which("rclone")
13
+ if rclone_which_path is None:
14
+ raise ValueError("rclone executable not found")
15
+ return Path(rclone_which_path)
16
+ return rclone_exe
17
+
18
+
19
+ def rclone_execute(
20
+ cmd: list[str],
21
+ rclone_conf: Path | Config,
22
+ rclone_exe: Path,
23
+ verbose: bool = False,
24
+ ) -> subprocess.CompletedProcess:
25
+ print(subprocess.list2cmdline(cmd))
26
+ tempdir: TemporaryDirectory | None = None
27
+
28
+ try:
29
+ if isinstance(rclone_conf, Config):
30
+ tempdir = TemporaryDirectory()
31
+ tmpfile = Path(tempdir.name) / "rclone.conf"
32
+ tmpfile.write_text(rclone_conf.text, encoding="utf-8")
33
+ rclone_conf = tmpfile
34
+ cmd = (
35
+ [str(rclone_exe.resolve())] + ["--config", str(rclone_conf.resolve())] + cmd
36
+ )
37
+ if verbose:
38
+ cmd_str = subprocess.list2cmdline(cmd)
39
+ print(f"Running: {cmd_str}")
40
+ cp = subprocess.run(
41
+ cmd, capture_output=True, encoding="utf-8", check=True, shell=False
42
+ )
43
+ return cp
44
+ finally:
45
+ if tempdir:
46
+ try:
47
+ tempdir.cleanup()
48
+ except Exception as e:
49
+ print(f"Error cleaning up tempdir: {e}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Summary: rclone api in python
5
5
  Home-page: https://github.com/zackees/rclone-api
6
6
  Maintainer: Zachary Vorhies
@@ -0,0 +1,12 @@
1
+ rclone_api/__init__.py,sha256=be1jguNsC4mSSJRrHeCJoC0rN8OnI4kEVpZmmKlq-1w,117
2
+ rclone_api/cli.py,sha256=dibfAZIh0kXWsBbfp3onKLjyZXo54mTzDjUdzJlDlWo,231
3
+ rclone_api/file.py,sha256=7XirDJx3k37EBX78j_Y13rCs2lnGB8ID7A-s5kztQ2E,1999
4
+ rclone_api/rclone.py,sha256=KXnIPbl-tvj8ZiG3HVhahCbZdPHEJG-5tbrN7eazX_g,1283
5
+ rclone_api/types.py,sha256=I_7WSHF0INm-XA7jEWMsFiGkpXPNmij6B5exsFUKggI,693
6
+ rclone_api/util.py,sha256=EXnijLLdFHqwoZvHrZdqeM8r6T7ad9-pN7qBN1b0I_g,1465
7
+ rclone_api/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
8
+ rclone_api-1.0.2.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
9
+ rclone_api-1.0.2.dist-info/METADATA,sha256=4sPbtb1DX5n0gQaDoPDLHELayret5vUzeWl902l_lWE,1244
10
+ rclone_api-1.0.2.dist-info/WHEEL,sha256=9Hm2OB-j1QcCUq9Jguht7ayGIIZBRTdOXD1qg9cCgPM,109
11
+ rclone_api-1.0.2.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
12
+ rclone_api-1.0.2.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- rclone_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- rclone_api/cli.py,sha256=dibfAZIh0kXWsBbfp3onKLjyZXo54mTzDjUdzJlDlWo,231
3
- rclone_api/rclone.py,sha256=FszyEwR-z4OSiztS-nhAXlXKLpmjP5LG4fm2YHCTWvo,4439
4
- rclone_api/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
5
- rclone_api-1.0.0.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
6
- rclone_api-1.0.0.dist-info/METADATA,sha256=dIogBa5fkcBwMguFEXnLPGLxgj_X0LtbFkpQUEzBZew,1244
7
- rclone_api-1.0.0.dist-info/WHEEL,sha256=9Hm2OB-j1QcCUq9Jguht7ayGIIZBRTdOXD1qg9cCgPM,109
8
- rclone_api-1.0.0.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
9
- rclone_api-1.0.0.dist-info/RECORD,,