rclone-api 1.0.17__tar.gz → 1.0.18__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. {rclone_api-1.0.17 → rclone_api-1.0.18}/.gitignore +1 -1
  2. {rclone_api-1.0.17 → rclone_api-1.0.18}/PKG-INFO +1 -1
  3. {rclone_api-1.0.17 → rclone_api-1.0.18}/pyproject.toml +1 -1
  4. {rclone_api-1.0.17 → rclone_api-1.0.18}/requirements.testing.txt +1 -0
  5. {rclone_api-1.0.17 → rclone_api-1.0.18}/src/rclone_api/process.py +3 -1
  6. {rclone_api-1.0.17 → rclone_api-1.0.18}/src/rclone_api/rclone.py +84 -1
  7. {rclone_api-1.0.17 → rclone_api-1.0.18}/src/rclone_api.egg-info/PKG-INFO +1 -1
  8. {rclone_api-1.0.17 → rclone_api-1.0.18}/src/rclone_api.egg-info/SOURCES.txt +1 -0
  9. {rclone_api-1.0.17 → rclone_api-1.0.18}/test +1 -1
  10. {rclone_api-1.0.17 → rclone_api-1.0.18}/tests/test_ls.py +1 -0
  11. rclone_api-1.0.18/tests/test_serve_webdav.py +158 -0
  12. {rclone_api-1.0.17 → rclone_api-1.0.18}/.aiderignore +0 -0
  13. {rclone_api-1.0.17 → rclone_api-1.0.18}/.github/workflows/lint.yml +0 -0
  14. {rclone_api-1.0.17 → rclone_api-1.0.18}/.github/workflows/push_macos.yml +0 -0
  15. {rclone_api-1.0.17 → rclone_api-1.0.18}/.github/workflows/push_ubuntu.yml +0 -0
  16. {rclone_api-1.0.17 → rclone_api-1.0.18}/.github/workflows/push_win.yml +0 -0
  17. {rclone_api-1.0.17 → rclone_api-1.0.18}/.pylintrc +0 -0
  18. {rclone_api-1.0.17 → rclone_api-1.0.18}/.vscode/launch.json +0 -0
  19. {rclone_api-1.0.17 → rclone_api-1.0.18}/.vscode/settings.json +0 -0
  20. {rclone_api-1.0.17 → rclone_api-1.0.18}/.vscode/tasks.json +0 -0
  21. {rclone_api-1.0.17 → rclone_api-1.0.18}/LICENSE +0 -0
  22. {rclone_api-1.0.17 → rclone_api-1.0.18}/MANIFEST.in +0 -0
  23. {rclone_api-1.0.17 → rclone_api-1.0.18}/README.md +0 -0
  24. {rclone_api-1.0.17 → rclone_api-1.0.18}/clean +0 -0
  25. {rclone_api-1.0.17 → rclone_api-1.0.18}/install +0 -0
  26. {rclone_api-1.0.17 → rclone_api-1.0.18}/lint +0 -0
  27. {rclone_api-1.0.17 → rclone_api-1.0.18}/setup.cfg +0 -0
  28. {rclone_api-1.0.17 → rclone_api-1.0.18}/setup.py +0 -0
  29. {rclone_api-1.0.17 → rclone_api-1.0.18}/src/rclone_api/__init__.py +0 -0
  30. {rclone_api-1.0.17 → rclone_api-1.0.18}/src/rclone_api/assets/example.txt +0 -0
  31. {rclone_api-1.0.17 → rclone_api-1.0.18}/src/rclone_api/cli.py +0 -0
  32. {rclone_api-1.0.17 → rclone_api-1.0.18}/src/rclone_api/config.py +0 -0
  33. {rclone_api-1.0.17 → rclone_api-1.0.18}/src/rclone_api/convert.py +0 -0
  34. {rclone_api-1.0.17 → rclone_api-1.0.18}/src/rclone_api/dir.py +0 -0
  35. {rclone_api-1.0.17 → rclone_api-1.0.18}/src/rclone_api/dir_listing.py +0 -0
  36. {rclone_api-1.0.17 → rclone_api-1.0.18}/src/rclone_api/exec.py +0 -0
  37. {rclone_api-1.0.17 → rclone_api-1.0.18}/src/rclone_api/file.py +0 -0
  38. {rclone_api-1.0.17 → rclone_api-1.0.18}/src/rclone_api/filelist.py +0 -0
  39. {rclone_api-1.0.17 → rclone_api-1.0.18}/src/rclone_api/remote.py +0 -0
  40. {rclone_api-1.0.17 → rclone_api-1.0.18}/src/rclone_api/rpath.py +0 -0
  41. {rclone_api-1.0.17 → rclone_api-1.0.18}/src/rclone_api/util.py +0 -0
  42. {rclone_api-1.0.17 → rclone_api-1.0.18}/src/rclone_api/walk.py +0 -0
  43. {rclone_api-1.0.17 → rclone_api-1.0.18}/src/rclone_api.egg-info/dependency_links.txt +0 -0
  44. {rclone_api-1.0.17 → rclone_api-1.0.18}/src/rclone_api.egg-info/requires.txt +0 -0
  45. {rclone_api-1.0.17 → rclone_api-1.0.18}/src/rclone_api.egg-info/top_level.txt +0 -0
  46. {rclone_api-1.0.17 → rclone_api-1.0.18}/tests/test_copy.py +0 -0
  47. {rclone_api-1.0.17 → rclone_api-1.0.18}/tests/test_is_synced.py +0 -0
  48. {rclone_api-1.0.17 → rclone_api-1.0.18}/tests/test_mount.py +0 -0
  49. {rclone_api-1.0.17 → rclone_api-1.0.18}/tests/test_remotes.py +0 -0
  50. {rclone_api-1.0.17 → rclone_api-1.0.18}/tests/test_walk.py +0 -0
  51. {rclone_api-1.0.17 → rclone_api-1.0.18}/tox.ini +0 -0
  52. {rclone_api-1.0.17 → rclone_api-1.0.18}/upload_package.sh +0 -0
@@ -144,4 +144,4 @@ uv.lock
144
144
  !.aider.conf.yml
145
145
  !.aiderignore
146
146
 
147
- rclone.conf
147
+ rclone*.conf
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.0.17
3
+ Version: 1.0.18
4
4
  Summary: rclone api in python
5
5
  Home-page: https://github.com/zackees/rclone-api
6
6
  Maintainer: Zachary Vorhies
@@ -14,7 +14,7 @@ dependencies = [
14
14
  "python-dotenv>=1.0.0"
15
15
  ]
16
16
  # Change this with the version number bump.
17
- version = "1.0.17"
17
+ version = "1.0.18"
18
18
 
19
19
  [tool.setuptools]
20
20
  package-dir = {"" = "src"}
@@ -5,3 +5,4 @@ pytest
5
5
  tox
6
6
  ruff
7
7
  pytest-xdist
8
+ httpx
@@ -69,8 +69,10 @@ class Process:
69
69
  tmpfile = Path(self.tempdir.name) / "rclone.conf"
70
70
  tmpfile.write_text(args.rclone_conf.text, encoding="utf-8")
71
71
  rclone_conf = tmpfile
72
+ self.needs_cleanup = True
72
73
  else:
73
74
  rclone_conf = args.rclone_conf
75
+ self.needs_cleanup = False
74
76
 
75
77
  assert rclone_conf.exists()
76
78
 
@@ -85,7 +87,7 @@ class Process:
85
87
  self.process = subprocess.Popen(self.cmd, shell=False)
86
88
 
87
89
  def cleanup(self) -> None:
88
- if self.tempdir:
90
+ if self.tempdir and self.needs_cleanup:
89
91
  try:
90
92
  self.tempdir.cleanup()
91
93
  except Exception as e:
@@ -239,7 +239,13 @@ class Rclone:
239
239
  return self._run(cmd_list)
240
240
 
241
241
  def mount(
242
- self, src: Remote | Dir | str, outdir: Path, allow_writes=False, use_links=True
242
+ self,
243
+ src: Remote | Dir | str,
244
+ outdir: Path,
245
+ allow_writes=False,
246
+ use_links=True,
247
+ vfs_cache_mode="full",
248
+ other_cmds: list[str] | None = None,
243
249
  ) -> Process:
244
250
  """Mount a remote or directory to a local path.
245
251
 
@@ -266,8 +272,85 @@ class Rclone:
266
272
  cmd_list.append("--read-only")
267
273
  if use_links:
268
274
  cmd_list.append("--links")
275
+ if vfs_cache_mode:
276
+ cmd_list.append("--vfs-cache-mode")
277
+ cmd_list.append(vfs_cache_mode)
278
+ if other_cmds:
279
+ cmd_list += other_cmds
269
280
  proc = self._launch_process(cmd_list)
270
281
  time.sleep(2) # give it a moment to mount
271
282
  if proc.poll() is not None:
272
283
  raise ValueError("Mount process failed to start")
273
284
  return proc
285
+
286
+ def mount_webdav(
287
+ self,
288
+ url: str,
289
+ outdir: Path,
290
+ vfs_cache_mode="full",
291
+ other_cmds: list[str] | None = None,
292
+ ) -> Process:
293
+ """Mount a remote or directory to a local path.
294
+
295
+ Args:
296
+ src: Remote or directory to mount
297
+ outdir: Local path to mount to
298
+
299
+ Returns:
300
+ CompletedProcess from the mount command execution
301
+
302
+ Raises:
303
+ subprocess.CalledProcessError: If the mount operation fails
304
+ """
305
+ if outdir.exists():
306
+ is_empty = not list(outdir.iterdir())
307
+ if not is_empty:
308
+ raise ValueError(
309
+ f"Mount directory already exists and is not empty: {outdir}"
310
+ )
311
+ outdir.rmdir()
312
+
313
+ src_str = url
314
+ cmd_list: list[str] = ["mount", src_str, str(outdir)]
315
+ cmd_list.append("--vfs-cache-mode")
316
+ cmd_list.append(vfs_cache_mode)
317
+ if other_cmds:
318
+ cmd_list += other_cmds
319
+ proc = self._launch_process(cmd_list)
320
+ # proc = rclone_exec.launch_process(cmd_list)
321
+ time.sleep(2)
322
+ if proc.poll() is not None:
323
+ raise ValueError("Mount process failed to start")
324
+ return proc
325
+
326
+ def serve_webdav(
327
+ self,
328
+ src: Remote | Dir | str,
329
+ user: str,
330
+ password: str,
331
+ addr: str = "localhost:2049",
332
+ allow_other: bool = False,
333
+ ) -> Process:
334
+ """Serve a remote or directory via NFS.
335
+
336
+ Args:
337
+ src: Remote or directory to serve
338
+ addr: Network address and port to serve on (default: localhost:2049)
339
+ allow_other: Allow other users to access the share
340
+
341
+ Returns:
342
+ Process: The running NFS server process
343
+
344
+ Raises:
345
+ ValueError: If the NFS server fails to start
346
+ """
347
+ src_str = convert_to_str(src)
348
+ cmd_list: list[str] = ["serve", "webdav", "--addr", addr, src_str]
349
+ cmd_list.extend(["--user", user, "--pass", password])
350
+ if allow_other:
351
+ cmd_list.append("--allow-other")
352
+ proc = self._launch_process(cmd_list)
353
+ time.sleep(2) # give it a moment to start
354
+ if proc.poll() is not None:
355
+ raise ValueError("NFS serve process failed to start")
356
+ return proc
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.0.17
3
+ Version: 1.0.18
4
4
  Summary: rclone api in python
5
5
  Home-page: https://github.com/zackees/rclone-api
6
6
  Maintainer: Zachary Vorhies
@@ -46,4 +46,5 @@ tests/test_is_synced.py
46
46
  tests/test_ls.py
47
47
  tests/test_mount.py
48
48
  tests/test_remotes.py
49
+ tests/test_serve_webdav.py
49
50
  tests/test_walk.py
@@ -2,4 +2,4 @@
2
2
 
3
3
  set -e
4
4
  echo "Running unittests"
5
- uv run pytest -n auto tests -v
5
+ uv run pytest tests -v
@@ -31,6 +31,7 @@ provider = DigitalOcean
31
31
  access_key_id = {BUCKET_KEY_PUBLIC}
32
32
  secret_access_key = {BUCKET_KEY_SECRET}
33
33
  endpoint = {BUCKET_URL}
34
+ bucket = {BUCKET_NAME}
34
35
  """
35
36
 
36
37
  out = Config(config_text)
@@ -0,0 +1,158 @@
1
+ """
2
+ Unit test file.
3
+ """
4
+
5
+ import os
6
+ import unittest
7
+ from pathlib import Path
8
+
9
+ import httpx
10
+ from dotenv import load_dotenv
11
+
12
+ from rclone_api import Process, Rclone, Remote
13
+
14
+ load_dotenv()
15
+
16
+ BUCKET_NAME = os.getenv("BUCKET_NAME") # Default if not in .env
17
+
18
+
19
+ def _generate_rclone_config() -> Path:
20
+
21
+ # BUCKET_NAME = os.getenv("BUCKET_NAME", "TorrentBooks") # Default if not in .env
22
+
23
+ # Load additional environment variables
24
+ BUCKET_KEY_SECRET = os.getenv("BUCKET_KEY_SECRET")
25
+ BUCKET_KEY_PUBLIC = os.getenv("BUCKET_KEY_PUBLIC")
26
+ # BUCKET_URL = os.getenv("BUCKET_URL")
27
+ BUCKET_URL = "sfo3.digitaloceanspaces.com"
28
+
29
+ config_text = f"""
30
+ [dst]
31
+ type = s3
32
+ provider = DigitalOcean
33
+ access_key_id = {BUCKET_KEY_PUBLIC}
34
+ secret_access_key = {BUCKET_KEY_SECRET}
35
+ endpoint = {BUCKET_URL}
36
+ bucket = {BUCKET_NAME}
37
+
38
+
39
+ [webdav]
40
+ type = webdav
41
+ user = guest
42
+ # encrypted password for "1234"
43
+ pass = d4IbQLV9W0JhI2tm5Zp88hpMtEg
44
+ url = http://localhost:8089
45
+ vendor = rclone
46
+ """
47
+
48
+ # out = Config(config_text)
49
+ out = Path("rclone_nfs.conf")
50
+ out.write_text(config_text, encoding="utf-8")
51
+ return out
52
+
53
+
54
+ class RcloneNfsServeTest(unittest.TestCase):
55
+ """Test rclone functionality."""
56
+
57
+ def setUp(self) -> None:
58
+ """Check if all required environment variables are set before running tests."""
59
+ required_vars = [
60
+ "BUCKET_NAME",
61
+ "BUCKET_KEY_SECRET",
62
+ "BUCKET_KEY_PUBLIC",
63
+ "BUCKET_URL",
64
+ ]
65
+ missing = [var for var in required_vars if not os.getenv(var)]
66
+ if missing:
67
+ self.skipTest(
68
+ f"Missing required environment variables: {', '.join(missing)}"
69
+ )
70
+ os.environ["RCLONE_API_VERBOSE"] = "1"
71
+ self.config = _generate_rclone_config()
72
+ self.rclone = Rclone(self.config)
73
+
74
+ def tearDown(self):
75
+ if self.config.exists():
76
+ self.config.unlink()
77
+
78
+ def test_serve_webdav(self) -> None:
79
+ """Test basic NFS serve functionality."""
80
+ config = _generate_rclone_config()
81
+ rclone = Rclone(config)
82
+
83
+ # Start NFS server for the remote
84
+ remote = Remote("dst", rclone=rclone)
85
+ # serve = Remote("webdav", rclone=rclone)
86
+ test_addr = "localhost:8089"
87
+ user = "guest"
88
+ password = "1234"
89
+
90
+ process = rclone.serve_webdav(
91
+ f"{remote.name}:{BUCKET_NAME}", addr=test_addr, user=user, password=password
92
+ )
93
+ mount_proc: Process | None = None
94
+
95
+ try:
96
+ # Verify process is running
97
+ self.assertIsNone(process.poll())
98
+ response = httpx.get(f"http://{test_addr}/", auth=(user, password))
99
+ # Note that windows is kinda broken and returns internal server error
100
+ is_serving = response.status_code == 200
101
+ self.assertTrue(is_serving)
102
+
103
+ finally:
104
+ # Clean up
105
+ process.terminate()
106
+ process.wait()
107
+ if mount_proc:
108
+ mount_proc.terminate()
109
+ mount_proc.wait()
110
+
111
+ # Verify process terminated
112
+ self.assertIsNotNone(process.poll())
113
+
114
+ def test_serve_webdav_and_mount(self) -> None:
115
+ """Test basic NFS serve functionality."""
116
+ config = _generate_rclone_config()
117
+ rclone = Rclone(config)
118
+
119
+ # Start NFS server for the remote
120
+ remote = Remote("dst", rclone=rclone)
121
+ # serve = Remote("webdav", rclone=rclone)
122
+ test_addr = "localhost:8089"
123
+ user = "guest"
124
+ password = "1234"
125
+
126
+ process = rclone.serve_webdav(
127
+ f"{remote.name}:{BUCKET_NAME}", addr=test_addr, user=user, password=password
128
+ )
129
+ mount_proc: Process | None = None
130
+
131
+ try:
132
+ # Verify process is running
133
+ self.assertIsNone(process.poll())
134
+ mount_point = Path("test_mount2")
135
+ mount_proc = rclone.mount_webdav("webdav:", mount_point)
136
+ # test that the mount point exists
137
+ self.assertTrue(mount_point.exists())
138
+ # test the folder is not empty
139
+ dirs = next(mount_point.iterdir())
140
+ self.assertTrue(dirs.is_dir())
141
+
142
+ finally:
143
+ # Clean up
144
+ if mount_proc:
145
+ mount_proc.terminate()
146
+ mount_proc.wait()
147
+ process.terminate()
148
+ process.wait()
149
+ if mount_proc:
150
+ mount_proc.terminate()
151
+ mount_proc.wait()
152
+
153
+ # Verify process terminated
154
+ self.assertIsNotNone(process.poll())
155
+
156
+
157
+ if __name__ == "__main__":
158
+ unittest.main()
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