rclone-api 1.0.46__tar.gz → 1.0.48__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.46 → rclone_api-1.0.48}/PKG-INFO +1 -1
  2. {rclone_api-1.0.46 → rclone_api-1.0.48}/pyproject.toml +1 -1
  3. rclone_api-1.0.48/src/rclone_api/completed_process.py +49 -0
  4. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api/exec.py +6 -2
  5. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api/rclone.py +74 -13
  6. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api/util.py +3 -1
  7. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api.egg-info/PKG-INFO +1 -1
  8. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api.egg-info/SOURCES.txt +1 -0
  9. rclone_api-1.0.48/tests/test_remote_control.py +82 -0
  10. rclone_api-1.0.46/src/rclone_api/completed_process.py +0 -21
  11. {rclone_api-1.0.46 → rclone_api-1.0.48}/.aiderignore +0 -0
  12. {rclone_api-1.0.46 → rclone_api-1.0.48}/.github/workflows/lint.yml +0 -0
  13. {rclone_api-1.0.46 → rclone_api-1.0.48}/.github/workflows/push_macos.yml +0 -0
  14. {rclone_api-1.0.46 → rclone_api-1.0.48}/.github/workflows/push_ubuntu.yml +0 -0
  15. {rclone_api-1.0.46 → rclone_api-1.0.48}/.github/workflows/push_win.yml +0 -0
  16. {rclone_api-1.0.46 → rclone_api-1.0.48}/.gitignore +0 -0
  17. {rclone_api-1.0.46 → rclone_api-1.0.48}/.pylintrc +0 -0
  18. {rclone_api-1.0.46 → rclone_api-1.0.48}/.vscode/launch.json +0 -0
  19. {rclone_api-1.0.46 → rclone_api-1.0.48}/.vscode/settings.json +0 -0
  20. {rclone_api-1.0.46 → rclone_api-1.0.48}/.vscode/tasks.json +0 -0
  21. {rclone_api-1.0.46 → rclone_api-1.0.48}/LICENSE +0 -0
  22. {rclone_api-1.0.46 → rclone_api-1.0.48}/MANIFEST.in +0 -0
  23. {rclone_api-1.0.46 → rclone_api-1.0.48}/README.md +0 -0
  24. {rclone_api-1.0.46 → rclone_api-1.0.48}/clean +0 -0
  25. {rclone_api-1.0.46 → rclone_api-1.0.48}/install +0 -0
  26. {rclone_api-1.0.46 → rclone_api-1.0.48}/lint +0 -0
  27. {rclone_api-1.0.46 → rclone_api-1.0.48}/requirements.testing.txt +0 -0
  28. {rclone_api-1.0.46 → rclone_api-1.0.48}/setup.cfg +0 -0
  29. {rclone_api-1.0.46 → rclone_api-1.0.48}/setup.py +0 -0
  30. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api/__init__.py +0 -0
  31. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api/assets/example.txt +0 -0
  32. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api/cli.py +0 -0
  33. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api/cmd/list_files.py +0 -0
  34. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api/config.py +0 -0
  35. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api/convert.py +0 -0
  36. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api/deprecated.py +0 -0
  37. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api/diff.py +0 -0
  38. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api/dir.py +0 -0
  39. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api/dir_listing.py +0 -0
  40. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api/file.py +0 -0
  41. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api/filelist.py +0 -0
  42. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api/group_files.py +0 -0
  43. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api/process.py +0 -0
  44. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api/remote.py +0 -0
  45. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api/rpath.py +0 -0
  46. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api/walk.py +0 -0
  47. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api.egg-info/dependency_links.txt +0 -0
  48. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api.egg-info/entry_points.txt +0 -0
  49. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api.egg-info/requires.txt +0 -0
  50. {rclone_api-1.0.46 → rclone_api-1.0.48}/src/rclone_api.egg-info/top_level.txt +0 -0
  51. {rclone_api-1.0.46 → rclone_api-1.0.48}/test +0 -0
  52. {rclone_api-1.0.46 → rclone_api-1.0.48}/tests/test_cmd_list_files.py +0 -0
  53. {rclone_api-1.0.46 → rclone_api-1.0.48}/tests/test_copy.py +0 -0
  54. {rclone_api-1.0.46 → rclone_api-1.0.48}/tests/test_diff.py +0 -0
  55. {rclone_api-1.0.46 → rclone_api-1.0.48}/tests/test_group_files.py +0 -0
  56. {rclone_api-1.0.46 → rclone_api-1.0.48}/tests/test_is_synced.py +0 -0
  57. {rclone_api-1.0.46 → rclone_api-1.0.48}/tests/test_ls.py +0 -0
  58. {rclone_api-1.0.46 → rclone_api-1.0.48}/tests/test_mount.py +0 -0
  59. {rclone_api-1.0.46 → rclone_api-1.0.48}/tests/test_mount_s3.py +0 -0
  60. {rclone_api-1.0.46 → rclone_api-1.0.48}/tests/test_mount_webdav.py +0 -0
  61. {rclone_api-1.0.46 → rclone_api-1.0.48}/tests/test_obscure.py +0 -0
  62. {rclone_api-1.0.46 → rclone_api-1.0.48}/tests/test_remotes.py +0 -0
  63. {rclone_api-1.0.46 → rclone_api-1.0.48}/tests/test_serve_webdav.py +0 -0
  64. {rclone_api-1.0.46 → rclone_api-1.0.48}/tests/test_walk.py +0 -0
  65. {rclone_api-1.0.46 → rclone_api-1.0.48}/tox.ini +0 -0
  66. {rclone_api-1.0.46 → rclone_api-1.0.48}/upload_package.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.0.46
3
+ Version: 1.0.48
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.46"
18
+ version = "1.0.48"
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,8 +57,10 @@ 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)
@@ -70,6 +72,47 @@ class Rclone:
70
72
  cmd += other_args
71
73
  return self._launch_process(cmd, capture=False)
72
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
+
73
116
  def obscure(self, password: str) -> str:
74
117
  """Obscure a password for use in rclone config files."""
75
118
  cmd_list: list[str] = ["obscure", password]
@@ -218,7 +261,13 @@ class Rclone:
218
261
  cmd_list: list[str] = ["copyto", src, dst]
219
262
  self._run(cmd_list)
220
263
 
221
- def copy_to(self, src: File | str, dst: File | str) -> None:
264
+ def copy_to(
265
+ self,
266
+ src: File | str,
267
+ dst: File | str,
268
+ check=True,
269
+ other_args: list[str] | None = None,
270
+ ) -> None:
222
271
  """Copy multiple files from source to destination.
223
272
 
224
273
  Warning - slow.
@@ -229,9 +278,16 @@ class Rclone:
229
278
  src = str(src)
230
279
  dst = str(dst)
231
280
  cmd_list: list[str] = ["copyto", src, dst]
232
- self._run(cmd_list)
281
+ if other_args is not None:
282
+ cmd_list += other_args
283
+ self._run(cmd_list, check=check)
233
284
 
234
- def copyfiles(self, files: str | File | list[str] | list[File], check=True) -> None:
285
+ def copyfiles(
286
+ self,
287
+ files: str | File | list[str] | list[File],
288
+ check=True,
289
+ other_args: list[str] | None = None,
290
+ ) -> list[CompletedProcess]:
235
291
  """Copy multiple files from source to destination.
236
292
 
237
293
  Warning - slow.
@@ -241,10 +297,11 @@ class Rclone:
241
297
  """
242
298
  payload: list[str] = convert_to_filestr_list(files)
243
299
  if len(payload) == 0:
244
- return
300
+ return []
245
301
 
246
302
  datalists: dict[str, list[str]] = group_files(payload)
247
- out: subprocess.CompletedProcess | None = None
303
+ # out: subprocess.CompletedProcess | None = None
304
+ out: list[CompletedProcess] = []
248
305
 
249
306
  futures: list[Future] = []
250
307
 
@@ -257,7 +314,7 @@ class Rclone:
257
314
 
258
315
  # print(include_files_txt)
259
316
  cmd_list: list[str] = [
260
- "delete",
317
+ "copy",
261
318
  remote,
262
319
  "--files-from",
263
320
  str(include_files_txt),
@@ -266,19 +323,23 @@ class Rclone:
266
323
  "--transfers",
267
324
  "1000",
268
325
  ]
326
+ if other_args is not None:
327
+ cmd_list += other_args
269
328
  out = self._run(cmd_list)
270
329
  return out
271
330
 
272
331
  fut: Future = EXECUTOR.submit(_task)
273
332
  futures.append(fut)
274
333
  for fut in futures:
275
- out = fut.result()
276
- assert out is not None
277
- if out.returncode != 0:
334
+ cp: subprocess.CompletedProcess = fut.result()
335
+ assert cp is not None
336
+ out.append(CompletedProcess.from_subprocess(cp))
337
+ if cp.returncode != 0:
278
338
  if check:
279
- raise ValueError(f"Error deleting files: {out.stderr}")
339
+ raise ValueError(f"Error deleting files: {cp.stderr}")
280
340
  else:
281
- warnings.warn(f"Error deleting files: {out.stderr}")
341
+ warnings.warn(f"Error deleting files: {cp.stderr}")
342
+ return out
282
343
 
283
344
  def copy(self, src: Dir | str, dst: Dir | str) -> CompletedProcess:
284
345
  """Copy files from source to destination.
@@ -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.46
3
+ Version: 1.0.48
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