rclone-api 1.0.12__tar.gz → 1.0.13__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. rclone_api-1.0.13/PKG-INFO +34 -0
  2. rclone_api-1.0.13/README.md +18 -0
  3. {rclone_api-1.0.12 → rclone_api-1.0.13}/pyproject.toml +44 -44
  4. {rclone_api-1.0.12 → rclone_api-1.0.13}/src/rclone_api/dir.py +7 -1
  5. {rclone_api-1.0.12 → rclone_api-1.0.13}/src/rclone_api/file.py +1 -4
  6. {rclone_api-1.0.12 → rclone_api-1.0.13}/src/rclone_api/rclone.py +97 -90
  7. {rclone_api-1.0.12 → rclone_api-1.0.13}/src/rclone_api/rpath.py +15 -8
  8. rclone_api-1.0.13/src/rclone_api/util.py +101 -0
  9. rclone_api-1.0.13/src/rclone_api.egg-info/PKG-INFO +34 -0
  10. {rclone_api-1.0.12 → rclone_api-1.0.13}/src/rclone_api.egg-info/SOURCES.txt +1 -0
  11. rclone_api-1.0.13/src/rclone_api.egg-info/requires.txt +1 -0
  12. rclone_api-1.0.13/tests/test_simple.py +119 -0
  13. rclone_api-1.0.12/PKG-INFO +0 -36
  14. rclone_api-1.0.12/README.md +0 -21
  15. rclone_api-1.0.12/src/rclone_api/util.py +0 -49
  16. rclone_api-1.0.12/src/rclone_api.egg-info/PKG-INFO +0 -36
  17. rclone_api-1.0.12/tests/test_simple.py +0 -16
  18. {rclone_api-1.0.12 → rclone_api-1.0.13}/.aiderignore +0 -0
  19. {rclone_api-1.0.12 → rclone_api-1.0.13}/.gitignore +0 -0
  20. {rclone_api-1.0.12 → rclone_api-1.0.13}/.pylintrc +0 -0
  21. {rclone_api-1.0.12 → rclone_api-1.0.13}/.vscode/launch.json +0 -0
  22. {rclone_api-1.0.12 → rclone_api-1.0.13}/.vscode/settings.json +0 -0
  23. {rclone_api-1.0.12 → rclone_api-1.0.13}/.vscode/tasks.json +0 -0
  24. {rclone_api-1.0.12 → rclone_api-1.0.13}/LICENSE +0 -0
  25. {rclone_api-1.0.12 → rclone_api-1.0.13}/MANIFEST.in +0 -0
  26. {rclone_api-1.0.12 → rclone_api-1.0.13}/clean +0 -0
  27. {rclone_api-1.0.12 → rclone_api-1.0.13}/install +0 -0
  28. {rclone_api-1.0.12 → rclone_api-1.0.13}/lint +0 -0
  29. {rclone_api-1.0.12 → rclone_api-1.0.13}/requirements.testing.txt +0 -0
  30. {rclone_api-1.0.12 → rclone_api-1.0.13}/setup.cfg +0 -0
  31. {rclone_api-1.0.12 → rclone_api-1.0.13}/setup.py +0 -0
  32. {rclone_api-1.0.12 → rclone_api-1.0.13}/src/rclone_api/__init__.py +0 -0
  33. {rclone_api-1.0.12 → rclone_api-1.0.13}/src/rclone_api/assets/example.txt +0 -0
  34. {rclone_api-1.0.12 → rclone_api-1.0.13}/src/rclone_api/cli.py +0 -0
  35. {rclone_api-1.0.12 → rclone_api-1.0.13}/src/rclone_api/config.py +0 -0
  36. {rclone_api-1.0.12 → rclone_api-1.0.13}/src/rclone_api/dir_listing.py +0 -0
  37. {rclone_api-1.0.12 → rclone_api-1.0.13}/src/rclone_api/remote.py +0 -0
  38. {rclone_api-1.0.12 → rclone_api-1.0.13}/src/rclone_api/types.py +0 -0
  39. {rclone_api-1.0.12 → rclone_api-1.0.13}/src/rclone_api/walk.py +0 -0
  40. {rclone_api-1.0.12 → rclone_api-1.0.13}/src/rclone_api.egg-info/dependency_links.txt +0 -0
  41. {rclone_api-1.0.12 → rclone_api-1.0.13}/src/rclone_api.egg-info/top_level.txt +0 -0
  42. {rclone_api-1.0.12 → rclone_api-1.0.13}/test +0 -0
  43. {rclone_api-1.0.12 → rclone_api-1.0.13}/tox.ini +0 -0
  44. {rclone_api-1.0.12 → rclone_api-1.0.13}/upload_package.sh +0 -0
@@ -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
+ [![Linting](https://github.com/zackees/rclone-api/actions/workflows/lint.yml/badge.svg)](https://github.com/zackees/rclone-api/actions/workflows/lint.yml)
20
+ [![MacOS_Tests](https://github.com/zackees/rclone-api/actions/workflows/push_macos.yml/badge.svg)](https://github.com/zackees/rclone-api/actions/workflows/push_macos.yml)
21
+ [![Ubuntu_Tests](https://github.com/zackees/rclone-api/actions/workflows/push_ubuntu.yml/badge.svg)](https://github.com/zackees/rclone-api/actions/workflows/push_ubuntu.yml)
22
+ [![Win_Tests](https://github.com/zackees/rclone-api/actions/workflows/push_win.yml/badge.svg)](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`
@@ -0,0 +1,18 @@
1
+ # rclone-api
2
+
3
+ [![Linting](https://github.com/zackees/rclone-api/actions/workflows/lint.yml/badge.svg)](https://github.com/zackees/rclone-api/actions/workflows/lint.yml)
4
+ [![MacOS_Tests](https://github.com/zackees/rclone-api/actions/workflows/push_macos.yml/badge.svg)](https://github.com/zackees/rclone-api/actions/workflows/push_macos.yml)
5
+ [![Ubuntu_Tests](https://github.com/zackees/rclone-api/actions/workflows/push_ubuntu.yml/badge.svg)](https://github.com/zackees/rclone-api/actions/workflows/push_ubuntu.yml)
6
+ [![Win_Tests](https://github.com/zackees/rclone-api/actions/workflows/push_win.yml/badge.svg)](https://github.com/zackees/rclone-api/actions/workflows/push_win.yml)
7
+
8
+ Api version of rclone. It's well tested. It's just released so this readme is a little to be desired.
9
+
10
+ To develop software, run `. ./activate`
11
+
12
+ # Windows
13
+
14
+ This environment requires you to use `git-bash`.
15
+
16
+ # Linting
17
+
18
+ Run `./lint`
@@ -1,44 +1,44 @@
1
- [build-system]
2
- requires = ["setuptools>=65.5.1", "setuptools-scm", "wheel"]
3
- build-backend = "setuptools.build_meta"
4
-
5
- [project]
6
- name = "rclone_api"
7
- readme = "README.md"
8
- description = "rclone api in python"
9
- requires-python = ">=3.7"
10
- keywords = ["template-python-cmd"]
11
- license = { text = "BSD 3-Clause License" }
12
- classifiers = ["Programming Language :: Python :: 3"]
13
- dependencies = [
14
-
15
- ]
16
- # Change this with the version number bump.
17
- version = "1.0.12"
18
-
19
- [tool.setuptools]
20
- package-dir = {"" = "src"}
21
-
22
- [tool.ruff]
23
- line-length = 200
24
-
25
- [tool.pylint."MESSAGES CONTROL"]
26
- good-names = [
27
- "c",
28
- "i",
29
- "ok",
30
- "id",
31
- "e",
32
- "f"
33
- ]
34
- disable = [
35
- "missing-function-docstring",
36
- "missing-module-docstring"
37
- ]
38
-
39
- [tool.isort]
40
- profile = "black"
41
-
42
- [tool.mypy]
43
- ignore_missing_imports = true
44
- disable_error_code = ["import-untyped"]
1
+ [build-system]
2
+ requires = ["setuptools>=65.5.1", "setuptools-scm", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "rclone_api"
7
+ readme = "README.md"
8
+ description = "rclone api in python"
9
+ requires-python = ">=3.10"
10
+ keywords = ["template-python-cmd"]
11
+ license = { text = "BSD 3-Clause License" }
12
+ classifiers = ["Programming Language :: Python :: 3"]
13
+ dependencies = [
14
+ "python-dotenv>=1.0.0"
15
+ ]
16
+ # Change this with the version number bump.
17
+ version = "1.0.13"
18
+
19
+ [tool.setuptools]
20
+ package-dir = {"" = "src"}
21
+
22
+ [tool.ruff]
23
+ line-length = 200
24
+
25
+ [tool.pylint."MESSAGES CONTROL"]
26
+ good-names = [
27
+ "c",
28
+ "i",
29
+ "ok",
30
+ "id",
31
+ "e",
32
+ "f"
33
+ ]
34
+ disable = [
35
+ "missing-function-docstring",
36
+ "missing-module-docstring"
37
+ ]
38
+
39
+ [tool.isort]
40
+ profile = "black"
41
+
42
+ [tool.mypy]
43
+ ignore_missing_imports = true
44
+ disable_error_code = ["import-untyped"]
@@ -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
- return self.path.rclone.ls(self.path.path, max_depth=max_depth)
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."""
@@ -1,5 +1,3 @@
1
- import json
2
-
3
1
  from rclone_api.rpath import RPath
4
2
 
5
3
 
@@ -31,5 +29,4 @@ class File:
31
29
  return result.stdout
32
30
 
33
31
  def __str__(self) -> str:
34
- out = self.path.to_json()
35
- return json.dumps(out)
32
+ return str(self.path)
@@ -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: str | Remote, max_depth: int = 0) -> 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 > 0:
42
- cmd.extend(["--recursive", "--max-depth", str(max_depth)])
43
- cmd.append(str(path))
44
-
45
- cp = self._run(cmd)
46
- text = cp.stdout
47
- paths: list[RPath] = RPath.from_json_str(text)
48
- for o in paths:
49
- o.set_rclone(self)
50
- return DirListing(paths)
51
-
52
- def listremotes(self) -> list[Remote]:
53
- cmd = ["listremotes"]
54
- cp = self._run(cmd)
55
- text: str = cp.stdout
56
- tmp = text.splitlines()
57
- tmp = [t.strip() for t in tmp]
58
- # strip out ":" from the end
59
- tmp = [t.replace(":", "") for t in tmp]
60
- out = [Remote(name=t, rclone=self) for t in tmp]
61
- return out
62
-
63
- def walk(
64
- self, path: str | Remote, max_depth: int = -1
65
- ) -> Generator[DirListing, None, None]:
66
- """Walk through the given path recursively.
67
-
68
- Args:
69
- path: Remote path or Remote object to walk through
70
- max_depth: Maximum depth to traverse (-1 for unlimited)
71
-
72
- Yields:
73
- DirListing: Directory listing for each directory encountered
74
- """
75
- if isinstance(path, str):
76
- # Create a Remote object for the path
77
- rpath = RPath(
78
- path=path,
79
- name=path,
80
- size=0,
81
- mime_type="inode/directory",
82
- mod_time="",
83
- is_dir=True,
84
- )
85
- rpath.set_rclone(self)
86
- dir_obj = Dir(rpath)
87
- else:
88
- dir_obj = Dir(path)
89
-
90
- yield from walk(dir_obj, max_depth=max_depth)
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)
@@ -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
- out = self.to_json()
77
- return json.dumps(out)
84
+ return f"{self.remote.name}:{self.path}"
@@ -0,0 +1,101 @@
1
+ import os
2
+ import shutil
3
+ import subprocess
4
+ from pathlib import Path
5
+ from tempfile import TemporaryDirectory
6
+ from typing import Any
7
+
8
+ from rclone_api.dir import Dir
9
+ from rclone_api.remote import Remote
10
+ from rclone_api.rpath import RPath
11
+ from rclone_api.types import Config
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
+
59
+
60
+ def get_rclone_exe(rclone_exe: Path | None) -> Path:
61
+ if rclone_exe is None:
62
+
63
+ rclone_which_path = shutil.which("rclone")
64
+ if rclone_which_path is None:
65
+ raise ValueError("rclone executable not found")
66
+ return Path(rclone_which_path)
67
+ return rclone_exe
68
+
69
+
70
+ def rclone_execute(
71
+ cmd: list[str],
72
+ rclone_conf: Path | Config,
73
+ rclone_exe: Path,
74
+ verbose: bool | None = None,
75
+ ) -> subprocess.CompletedProcess:
76
+ tempdir: TemporaryDirectory | None = None
77
+ verbose = _get_verbose(verbose)
78
+ assert verbose is not None
79
+
80
+ try:
81
+ if isinstance(rclone_conf, Config):
82
+ tempdir = TemporaryDirectory()
83
+ tmpfile = Path(tempdir.name) / "rclone.conf"
84
+ tmpfile.write_text(rclone_conf.text, encoding="utf-8")
85
+ rclone_conf = tmpfile
86
+ cmd = (
87
+ [str(rclone_exe.resolve())] + ["--config", str(rclone_conf.resolve())] + cmd
88
+ )
89
+ if verbose:
90
+ cmd_str = subprocess.list2cmdline(cmd)
91
+ print(f"Running: {cmd_str}")
92
+ cp = subprocess.run(
93
+ cmd, capture_output=True, encoding="utf-8", check=True, shell=False
94
+ )
95
+ return cp
96
+ finally:
97
+ if tempdir:
98
+ try:
99
+ tempdir.cleanup()
100
+ except Exception as e:
101
+ print(f"Error cleaning up tempdir: {e}")
@@ -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
+ [![Linting](https://github.com/zackees/rclone-api/actions/workflows/lint.yml/badge.svg)](https://github.com/zackees/rclone-api/actions/workflows/lint.yml)
20
+ [![MacOS_Tests](https://github.com/zackees/rclone-api/actions/workflows/push_macos.yml/badge.svg)](https://github.com/zackees/rclone-api/actions/workflows/push_macos.yml)
21
+ [![Ubuntu_Tests](https://github.com/zackees/rclone-api/actions/workflows/push_ubuntu.yml/badge.svg)](https://github.com/zackees/rclone-api/actions/workflows/push_ubuntu.yml)
22
+ [![Win_Tests](https://github.com/zackees/rclone-api/actions/workflows/push_win.yml/badge.svg)](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`
@@ -31,6 +31,7 @@ src/rclone_api/walk.py
31
31
  src/rclone_api.egg-info/PKG-INFO
32
32
  src/rclone_api.egg-info/SOURCES.txt
33
33
  src/rclone_api.egg-info/dependency_links.txt
34
+ src/rclone_api.egg-info/requires.txt
34
35
  src/rclone_api.egg-info/top_level.txt
35
36
  src/rclone_api/assets/example.txt
36
37
  tests/test_simple.py
@@ -0,0 +1 @@
1
+ python-dotenv>=1.0.0
@@ -0,0 +1,119 @@
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, Dir, DirListing, File, Rclone, Remote
11
+ from rclone_api.util import to_path
12
+
13
+ load_dotenv()
14
+
15
+
16
+ def _generate_rclone_config() -> Config:
17
+
18
+ # BUCKET_NAME = os.getenv("BUCKET_NAME", "TorrentBooks") # Default if not in .env
19
+
20
+ # Load additional environment variables
21
+ BUCKET_KEY_SECRET = os.getenv("BUCKET_KEY_SECRET")
22
+ BUCKET_KEY_PUBLIC = os.getenv("BUCKET_KEY_PUBLIC")
23
+ # BUCKET_URL = os.getenv("BUCKET_URL")
24
+ BUCKET_URL = "sfo3.digitaloceanspaces.com"
25
+
26
+ config_text = f"""
27
+ [dst]
28
+ type = s3
29
+ provider = DigitalOcean
30
+ access_key_id = {BUCKET_KEY_PUBLIC}
31
+ secret_access_key = {BUCKET_KEY_SECRET}
32
+ endpoint = {BUCKET_URL}
33
+ """
34
+
35
+ out = Config(config_text)
36
+ return out
37
+
38
+
39
+ class RcloneTests(unittest.TestCase):
40
+ """Test rclone functionality."""
41
+
42
+ def setUp(self) -> None:
43
+ """Check if all required environment variables are set before running tests."""
44
+ required_vars = [
45
+ "BUCKET_NAME",
46
+ "BUCKET_KEY_SECRET",
47
+ "BUCKET_KEY_PUBLIC",
48
+ "BUCKET_URL",
49
+ ]
50
+ missing = [var for var in required_vars if not os.getenv(var)]
51
+ if missing:
52
+ self.skipTest(
53
+ f"Missing required environment variables: {', '.join(missing)}"
54
+ )
55
+ os.environ["RCLONE_API_VERBOSE"] = "1"
56
+
57
+ def test_list_remotes(self) -> None:
58
+ rclone = Rclone(_generate_rclone_config())
59
+
60
+ remotes: list[Remote] = rclone.listremotes()
61
+ self.assertGreater(len(remotes), 0)
62
+ for remote in remotes:
63
+ self.assertIsInstance(remote, Remote)
64
+ print(remote)
65
+ print("done")
66
+
67
+ def test_ls_root(self) -> None:
68
+ """Test listing the root directory of the bucket.
69
+
70
+ Verifies that we can:
71
+ 1. Connect to the bucket
72
+ 2. List its contents
73
+ 3. Get both directories and files as proper types
74
+ """
75
+ BUCKET_NAME = os.getenv("BUCKET_NAME") # Default if not in .env
76
+ self.assertIsNotNone(BUCKET_NAME)
77
+ rclone = Rclone(_generate_rclone_config())
78
+ path = to_path(f"dst:{BUCKET_NAME}", rclone)
79
+ dir = Dir(path)
80
+ listing: DirListing = rclone.ls(dir, max_depth=-1)
81
+
82
+ # Verify we got a valid listing
83
+ self.assertIsInstance(listing, DirListing)
84
+ self.assertGreater(len(listing.dirs), 0)
85
+ self.assertGreater(len(listing.files), 0)
86
+
87
+ # Verify dirs are properly typed
88
+ for dir in listing.dirs:
89
+ self.assertIsInstance(dir, Dir)
90
+ print(dir)
91
+
92
+ # Verify files are properly typed
93
+ for file in listing.files:
94
+ self.assertIsInstance(file, File)
95
+ print(file)
96
+
97
+ print("done")
98
+
99
+ # def test_zlib1(self) -> None:
100
+ # rclone = Rclone(_RCLONE_CONFIG)
101
+ # path = f"dst:{BUCKET_NAME}/zlib1"
102
+ # listing: DirListing = rclone.ls(path)
103
+ # print("dirs:")
104
+ # for dir in listing.dirs:
105
+ # print(dir)
106
+ # print("files:")
107
+ # for file in listing.files:
108
+ # print(file)
109
+
110
+ # def test_zlib_walk(self) -> None:
111
+ # rclone = Rclone(_RCLONE_CONFIG)
112
+ # # rclone.walk
113
+ # dirlisting: DirListing
114
+ # for dirlisting in rclone.walk(f"dst:{BUCKET_NAME}", max_depth=1):
115
+ # print(dirlisting)
116
+
117
+
118
+ if __name__ == "__main__":
119
+ unittest.main()
@@ -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
- [![Linting](../../actions/workflows/lint.yml/badge.svg)](../../actions/workflows/lint.yml)
20
-
21
- [![MacOS_Tests](../../actions/workflows/push_macos.yml/badge.svg)](../../actions/workflows/push_macos.yml)
22
- [![Ubuntu_Tests](../../actions/workflows/push_ubuntu.yml/badge.svg)](../../actions/workflows/push_ubuntu.yml)
23
- [![Win_Tests](../../actions/workflows/push_win.yml/badge.svg)](../../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`.
@@ -1,21 +0,0 @@
1
- # template-python-cmd
2
- A template for quickly making a python lib that has a command line program attached
3
-
4
- [![Linting](../../actions/workflows/lint.yml/badge.svg)](../../actions/workflows/lint.yml)
5
-
6
- [![MacOS_Tests](../../actions/workflows/push_macos.yml/badge.svg)](../../actions/workflows/push_macos.yml)
7
- [![Ubuntu_Tests](../../actions/workflows/push_ubuntu.yml/badge.svg)](../../actions/workflows/push_ubuntu.yml)
8
- [![Win_Tests](../../actions/workflows/push_win.yml/badge.svg)](../../actions/workflows/push_win.yml)
9
-
10
- Replace `template-python-cmd` and `template_python_cmd` with your command. Run tox until it's
11
- correct.
12
-
13
- To develop software, run `. ./activate.sh`
14
-
15
- # Windows
16
-
17
- This environment requires you to use `git-bash`.
18
-
19
- # Linting
20
-
21
- Run `./lint.sh` to find linting errors using `pylint`, `flake8` and `mypy`.
@@ -1,49 +0,0 @@
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,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
- [![Linting](../../actions/workflows/lint.yml/badge.svg)](../../actions/workflows/lint.yml)
20
-
21
- [![MacOS_Tests](../../actions/workflows/push_macos.yml/badge.svg)](../../actions/workflows/push_macos.yml)
22
- [![Ubuntu_Tests](../../actions/workflows/push_ubuntu.yml/badge.svg)](../../actions/workflows/push_ubuntu.yml)
23
- [![Win_Tests](../../actions/workflows/push_win.yml/badge.svg)](../../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`.
@@ -1,16 +0,0 @@
1
- """
2
- Unit test file.
3
- """
4
-
5
- import unittest
6
-
7
-
8
- class RcloneTests(unittest.TestCase):
9
- """Test rclone functionality."""
10
-
11
- def test_list_remotes(self) -> None:
12
- pass
13
-
14
-
15
- if __name__ == "__main__":
16
- 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
File without changes