rclone-api 1.0.45__tar.gz → 1.0.47__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. {rclone_api-1.0.45 → rclone_api-1.0.47}/PKG-INFO +1 -1
  2. {rclone_api-1.0.45 → rclone_api-1.0.47}/pyproject.toml +1 -1
  3. rclone_api-1.0.47/src/rclone_api/completed_process.py +49 -0
  4. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api/exec.py +6 -2
  5. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api/rclone.py +58 -4
  6. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api/util.py +3 -1
  7. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api.egg-info/PKG-INFO +1 -1
  8. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api.egg-info/SOURCES.txt +1 -0
  9. rclone_api-1.0.47/tests/test_remote_control.py +82 -0
  10. rclone_api-1.0.45/src/rclone_api/completed_process.py +0 -21
  11. {rclone_api-1.0.45 → rclone_api-1.0.47}/.aiderignore +0 -0
  12. {rclone_api-1.0.45 → rclone_api-1.0.47}/.github/workflows/lint.yml +0 -0
  13. {rclone_api-1.0.45 → rclone_api-1.0.47}/.github/workflows/push_macos.yml +0 -0
  14. {rclone_api-1.0.45 → rclone_api-1.0.47}/.github/workflows/push_ubuntu.yml +0 -0
  15. {rclone_api-1.0.45 → rclone_api-1.0.47}/.github/workflows/push_win.yml +0 -0
  16. {rclone_api-1.0.45 → rclone_api-1.0.47}/.gitignore +0 -0
  17. {rclone_api-1.0.45 → rclone_api-1.0.47}/.pylintrc +0 -0
  18. {rclone_api-1.0.45 → rclone_api-1.0.47}/.vscode/launch.json +0 -0
  19. {rclone_api-1.0.45 → rclone_api-1.0.47}/.vscode/settings.json +0 -0
  20. {rclone_api-1.0.45 → rclone_api-1.0.47}/.vscode/tasks.json +0 -0
  21. {rclone_api-1.0.45 → rclone_api-1.0.47}/LICENSE +0 -0
  22. {rclone_api-1.0.45 → rclone_api-1.0.47}/MANIFEST.in +0 -0
  23. {rclone_api-1.0.45 → rclone_api-1.0.47}/README.md +0 -0
  24. {rclone_api-1.0.45 → rclone_api-1.0.47}/clean +0 -0
  25. {rclone_api-1.0.45 → rclone_api-1.0.47}/install +0 -0
  26. {rclone_api-1.0.45 → rclone_api-1.0.47}/lint +0 -0
  27. {rclone_api-1.0.45 → rclone_api-1.0.47}/requirements.testing.txt +0 -0
  28. {rclone_api-1.0.45 → rclone_api-1.0.47}/setup.cfg +0 -0
  29. {rclone_api-1.0.45 → rclone_api-1.0.47}/setup.py +0 -0
  30. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api/__init__.py +0 -0
  31. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api/assets/example.txt +0 -0
  32. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api/cli.py +0 -0
  33. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api/cmd/list_files.py +0 -0
  34. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api/config.py +0 -0
  35. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api/convert.py +0 -0
  36. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api/deprecated.py +0 -0
  37. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api/diff.py +0 -0
  38. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api/dir.py +0 -0
  39. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api/dir_listing.py +0 -0
  40. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api/file.py +0 -0
  41. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api/filelist.py +0 -0
  42. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api/group_files.py +0 -0
  43. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api/process.py +0 -0
  44. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api/remote.py +0 -0
  45. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api/rpath.py +0 -0
  46. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api/walk.py +0 -0
  47. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api.egg-info/dependency_links.txt +0 -0
  48. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api.egg-info/entry_points.txt +0 -0
  49. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api.egg-info/requires.txt +0 -0
  50. {rclone_api-1.0.45 → rclone_api-1.0.47}/src/rclone_api.egg-info/top_level.txt +0 -0
  51. {rclone_api-1.0.45 → rclone_api-1.0.47}/test +0 -0
  52. {rclone_api-1.0.45 → rclone_api-1.0.47}/tests/test_cmd_list_files.py +0 -0
  53. {rclone_api-1.0.45 → rclone_api-1.0.47}/tests/test_copy.py +0 -0
  54. {rclone_api-1.0.45 → rclone_api-1.0.47}/tests/test_diff.py +0 -0
  55. {rclone_api-1.0.45 → rclone_api-1.0.47}/tests/test_group_files.py +0 -0
  56. {rclone_api-1.0.45 → rclone_api-1.0.47}/tests/test_is_synced.py +0 -0
  57. {rclone_api-1.0.45 → rclone_api-1.0.47}/tests/test_ls.py +0 -0
  58. {rclone_api-1.0.45 → rclone_api-1.0.47}/tests/test_mount.py +0 -0
  59. {rclone_api-1.0.45 → rclone_api-1.0.47}/tests/test_mount_s3.py +0 -0
  60. {rclone_api-1.0.45 → rclone_api-1.0.47}/tests/test_mount_webdav.py +0 -0
  61. {rclone_api-1.0.45 → rclone_api-1.0.47}/tests/test_obscure.py +0 -0
  62. {rclone_api-1.0.45 → rclone_api-1.0.47}/tests/test_remotes.py +0 -0
  63. {rclone_api-1.0.45 → rclone_api-1.0.47}/tests/test_serve_webdav.py +0 -0
  64. {rclone_api-1.0.45 → rclone_api-1.0.47}/tests/test_walk.py +0 -0
  65. {rclone_api-1.0.45 → rclone_api-1.0.47}/tox.ini +0 -0
  66. {rclone_api-1.0.45 → rclone_api-1.0.47}/upload_package.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.0.45
3
+ Version: 1.0.47
4
4
  Summary: rclone api in python
5
5
  Home-page: https://github.com/zackees/rclone-api
6
6
  Maintainer: Zachary Vorhies
@@ -15,7 +15,7 @@ dependencies = [
15
15
  "python-dotenv>=1.0.0",
16
16
  ]
17
17
  # Change this with the version number bump.
18
- version = "1.0.45"
18
+ version = "1.0.47"
19
19
 
20
20
  [tool.setuptools]
21
21
  package-dir = {"" = "src"}
@@ -0,0 +1,49 @@
1
+ import subprocess
2
+ from dataclasses import dataclass
3
+
4
+
5
+ @dataclass
6
+ class CompletedProcess:
7
+ completed: list[subprocess.CompletedProcess]
8
+
9
+ @property
10
+ def ok(self) -> bool:
11
+ return all([p.returncode == 0 for p in self.completed])
12
+
13
+ @staticmethod
14
+ def from_subprocess(process: subprocess.CompletedProcess) -> "CompletedProcess":
15
+ return CompletedProcess(completed=[process])
16
+
17
+ def failed(self) -> list[subprocess.CompletedProcess]:
18
+ return [p for p in self.completed if p.returncode != 0]
19
+
20
+ def successes(self) -> list[subprocess.CompletedProcess]:
21
+ return [p for p in self.completed if p.returncode == 0]
22
+
23
+ @property
24
+ def stdout(self) -> str:
25
+ tmp: list[str] = []
26
+ for cp in self.completed:
27
+ stdout = cp.stdout
28
+ if stdout is not None:
29
+ tmp.append(stdout)
30
+ return "\n".join(tmp)
31
+
32
+ @property
33
+ def stderr(self) -> str:
34
+ tmp: list[str] = []
35
+ for cp in self.completed:
36
+ stderr = cp.stderr
37
+ if stderr is not None:
38
+ tmp.append(stderr)
39
+ return "\n".join(tmp)
40
+
41
+ @property
42
+ def returncode(self) -> int | None:
43
+ for cp in self.completed:
44
+ rtn = cp.returncode
45
+ if rtn is None:
46
+ return None
47
+ if rtn != 0:
48
+ return rtn
49
+ return 0
@@ -13,11 +13,15 @@ class RcloneExec:
13
13
  rclone_config: Path | Config
14
14
  rclone_exe: Path
15
15
 
16
- def execute(self, cmd: list[str], check: bool) -> subprocess.CompletedProcess:
16
+ def execute(
17
+ self, cmd: list[str], check: bool, capture: bool | None = None
18
+ ) -> subprocess.CompletedProcess:
17
19
  """Execute rclone command."""
18
20
  from rclone_api.util import rclone_execute
19
21
 
20
- return rclone_execute(cmd, self.rclone_config, self.rclone_exe, check=check)
22
+ return rclone_execute(
23
+ cmd, self.rclone_config, self.rclone_exe, check=check, capture=capture
24
+ )
21
25
 
22
26
  def launch_process(self, cmd: list[str], capture: bool | None) -> Process:
23
27
  """Launch rclone process."""
@@ -57,12 +57,62 @@ class Rclone:
57
57
  raise ValueError(f"Rclone config file not found: {rclone_conf}")
58
58
  self._exec = RcloneExec(rclone_conf, get_rclone_exe(rclone_exe))
59
59
 
60
- def _run(self, cmd: list[str], check: bool = True) -> subprocess.CompletedProcess:
61
- return self._exec.execute(cmd, check=check)
60
+ def _run(
61
+ self, cmd: list[str], check: bool = True, capture: bool | None = None
62
+ ) -> subprocess.CompletedProcess:
63
+ return self._exec.execute(cmd, check=check, capture=capture)
62
64
 
63
65
  def _launch_process(self, cmd: list[str], capture: bool | None = None) -> Process:
64
66
  return self._exec.launch_process(cmd, capture=capture)
65
67
 
68
+ def webgui(self, other_args: list[str] | None = None) -> Process:
69
+ """Launch the Rclone web GUI."""
70
+ cmd = ["rcd", "--rc-web-gui"]
71
+ if other_args:
72
+ cmd += other_args
73
+ return self._launch_process(cmd, capture=False)
74
+
75
+ def launch_server(
76
+ self,
77
+ addr: str | None = None,
78
+ user: str | None = None,
79
+ password: str | None = None,
80
+ other_args: list[str] | None = None,
81
+ ) -> Process:
82
+ """Launch the Rclone server so it can receive commands"""
83
+ cmd = ["rcd"]
84
+ if addr is not None:
85
+ cmd += ["--rc-addr", addr]
86
+ if user is not None:
87
+ cmd += ["--rc-user", user]
88
+ if password is not None:
89
+ cmd += ["--rc-pass", password]
90
+ if other_args:
91
+ cmd += other_args
92
+ out = self._launch_process(cmd, capture=False)
93
+ time.sleep(1) # Give it some time to launch
94
+ return out
95
+
96
+ def remote_control(
97
+ self,
98
+ addr: str,
99
+ user: str | None = None,
100
+ password: str | None = None,
101
+ capture: bool | None = None,
102
+ other_args: list[str] | None = None,
103
+ ) -> CompletedProcess:
104
+ cmd = ["rc"]
105
+ if addr:
106
+ cmd += ["--rc-addr", addr]
107
+ if user is not None:
108
+ cmd += ["--rc-user", user]
109
+ if password is not None:
110
+ cmd += ["--rc-pass", password]
111
+ if other_args:
112
+ cmd += other_args
113
+ cp = self._run(cmd, capture=capture)
114
+ return CompletedProcess.from_subprocess(cp)
115
+
66
116
  def obscure(self, password: str) -> str:
67
117
  """Obscure a password for use in rclone config files."""
68
118
  cmd_list: list[str] = ["obscure", password]
@@ -185,10 +235,14 @@ class Rclone:
185
235
 
186
236
  yield from walk(dir_obj, max_depth=max_depth, breadth_first=breadth_first)
187
237
 
188
- def cleanup(self, path: str) -> CompletedProcess:
238
+ def cleanup(
239
+ self, path: str, other_args: list[str] | None = None
240
+ ) -> CompletedProcess:
189
241
  """Cleanup any resources used by the Rclone instance."""
190
242
  # rclone cleanup remote:path [flags]
191
243
  cmd = ["cleanup", path]
244
+ if other_args:
245
+ cmd += other_args
192
246
  out = self._run(cmd)
193
247
  return CompletedProcess.from_subprocess(out)
194
248
 
@@ -246,7 +300,7 @@ class Rclone:
246
300
 
247
301
  # print(include_files_txt)
248
302
  cmd_list: list[str] = [
249
- "delete",
303
+ "copy",
250
304
  remote,
251
305
  "--files-from",
252
306
  str(include_files_txt),
@@ -78,10 +78,12 @@ def rclone_execute(
78
78
  rclone_conf: Path | Config,
79
79
  rclone_exe: Path,
80
80
  check: bool,
81
+ capture: bool | None = None,
81
82
  verbose: bool | None = None,
82
83
  ) -> subprocess.CompletedProcess:
83
84
  tempdir: TemporaryDirectory | None = None
84
85
  verbose = get_verbose(verbose)
86
+ capture = capture if isinstance(capture, bool) else True
85
87
  assert verbose is not None
86
88
 
87
89
  try:
@@ -97,7 +99,7 @@ def rclone_execute(
97
99
  cmd_str = subprocess.list2cmdline(cmd)
98
100
  print(f"Running: {cmd_str}")
99
101
  cp = subprocess.run(
100
- cmd, capture_output=True, encoding="utf-8", check=False, shell=False
102
+ cmd, capture_output=capture, encoding="utf-8", check=False, shell=False
101
103
  )
102
104
  if cp.returncode != 0:
103
105
  cmd_str = subprocess.list2cmdline(cmd)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.0.45
3
+ Version: 1.0.47
4
4
  Summary: rclone api in python
5
5
  Home-page: https://github.com/zackees/rclone-api
6
6
  Maintainer: Zachary Vorhies
@@ -57,6 +57,7 @@ tests/test_mount.py
57
57
  tests/test_mount_s3.py
58
58
  tests/test_mount_webdav.py
59
59
  tests/test_obscure.py
60
+ tests/test_remote_control.py
60
61
  tests/test_remotes.py
61
62
  tests/test_serve_webdav.py
62
63
  tests/test_walk.py
@@ -0,0 +1,82 @@
1
+ """
2
+ Unit test file.
3
+ """
4
+
5
+ import os
6
+ import unittest
7
+
8
+ from dotenv import load_dotenv
9
+
10
+ from rclone_api import Config, Rclone
11
+
12
+ load_dotenv()
13
+
14
+ BUCKET_NAME = os.getenv("BUCKET_NAME") # Default if not in .env
15
+
16
+
17
+ def _generate_rclone_config() -> Config:
18
+
19
+ # BUCKET_NAME = os.getenv("BUCKET_NAME", "TorrentBooks") # Default if not in .env
20
+
21
+ # Load additional environment variables
22
+ BUCKET_KEY_SECRET = os.getenv("BUCKET_KEY_SECRET")
23
+ BUCKET_KEY_PUBLIC = os.getenv("BUCKET_KEY_PUBLIC")
24
+ # BUCKET_URL = os.getenv("BUCKET_URL")
25
+ BUCKET_URL = "sfo3.digitaloceanspaces.com"
26
+
27
+ config_text = f"""
28
+ [dst]
29
+ type = s3
30
+ provider = DigitalOcean
31
+ access_key_id = {BUCKET_KEY_PUBLIC}
32
+ secret_access_key = {BUCKET_KEY_SECRET}
33
+ endpoint = {BUCKET_URL}
34
+ bucket = {BUCKET_NAME}
35
+ """
36
+
37
+ out = Config(config_text)
38
+ return out
39
+
40
+
41
+ class RcloneRemoteControlTests(unittest.TestCase):
42
+ """Test rclone functionality."""
43
+
44
+ def setUp(self) -> None:
45
+ """Check if all required environment variables are set before running tests."""
46
+ required_vars = [
47
+ "BUCKET_NAME",
48
+ "BUCKET_KEY_SECRET",
49
+ "BUCKET_KEY_PUBLIC",
50
+ "BUCKET_URL",
51
+ ]
52
+ missing = [var for var in required_vars if not os.getenv(var)]
53
+ if missing:
54
+ self.skipTest(
55
+ f"Missing required environment variables: {', '.join(missing)}"
56
+ )
57
+ os.environ["RCLONE_API_VERBOSE"] = "1"
58
+
59
+ def test_server_launch(self) -> None:
60
+ rclone = Rclone(_generate_rclone_config())
61
+ proc = rclone.launch_server(addr="localhost:8888")
62
+ try:
63
+ self.assertTrue(proc.returncode is None)
64
+ finally:
65
+ proc.kill()
66
+
67
+ def test_launch_server_and_control_it(self) -> None:
68
+ rclone = Rclone(_generate_rclone_config())
69
+ proc = rclone.launch_server(addr="localhost:8889")
70
+ try:
71
+ self.assertTrue(proc.returncode is None)
72
+ # test the server
73
+ cp = rclone.remote_control(addr="localhost:8889")
74
+ # print(cp)
75
+ print(cp.stdout)
76
+ print("done")
77
+ finally:
78
+ proc.kill()
79
+
80
+
81
+ if __name__ == "__main__":
82
+ unittest.main()
@@ -1,21 +0,0 @@
1
- import subprocess
2
- from dataclasses import dataclass
3
-
4
-
5
- @dataclass
6
- class CompletedProcess:
7
- completed: list[subprocess.CompletedProcess]
8
-
9
- @property
10
- def ok(self) -> bool:
11
- return all([p.returncode == 0 for p in self.completed])
12
-
13
- @staticmethod
14
- def from_subprocess(process: subprocess.CompletedProcess) -> "CompletedProcess":
15
- return CompletedProcess(completed=[process])
16
-
17
- def failed(self) -> list[subprocess.CompletedProcess]:
18
- return [p for p in self.completed if p.returncode != 0]
19
-
20
- def successes(self) -> list[subprocess.CompletedProcess]:
21
- return [p for p in self.completed if p.returncode == 0]
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