rclone-api 1.0.11__py2.py3-none-any.whl → 1.0.13__py2.py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
rclone_api/dir.py CHANGED
@@ -3,12 +3,15 @@ from typing import Generator
3
3
  from rclone_api.dir_listing import DirListing
4
4
  from rclone_api.remote import Remote
5
5
  from rclone_api.rpath import RPath
6
- from rclone_api.walk import walk
7
6
 
8
7
 
9
8
  class Dir:
10
9
  """Remote file dataclass."""
11
10
 
11
+ @property
12
+ def remote(self) -> Remote:
13
+ return self.path.remote
14
+
12
15
  def __init__(self, path: RPath | Remote) -> None:
13
16
  """Initialize Dir with either an RPath or Remote.
14
17
 
@@ -18,6 +21,7 @@ class Dir:
18
21
  if isinstance(path, Remote):
19
22
  # Need to create an RPath for the Remote's root
20
23
  self.path = RPath(
24
+ remote=path,
21
25
  path=str(path),
22
26
  name=str(path),
23
27
  size=0,
@@ -33,10 +37,13 @@ class Dir:
33
37
  def ls(self, max_depth: int = 0) -> DirListing:
34
38
  """List files and directories in the given path."""
35
39
  assert self.path.rclone is not None
36
- 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)
37
42
 
38
43
  def walk(self, max_depth: int = -1) -> Generator[DirListing, None, None]:
39
44
  """List files and directories in the given path."""
45
+ from rclone_api.walk import walk
46
+
40
47
  assert self.path.rclone is not None
41
48
  return walk(self, max_depth=max_depth)
42
49
 
rclone_api/file.py CHANGED
@@ -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)
rclone_api/rclone.py CHANGED
@@ -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)
rclone_api/rpath.py CHANGED
@@ -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}"
rclone_api/util.py CHANGED
@@ -1,10 +1,61 @@
1
+ import os
1
2
  import shutil
2
3
  import subprocess
3
4
  from pathlib import Path
4
5
  from tempfile import TemporaryDirectory
6
+ from typing import Any
5
7
 
8
+ from rclone_api.dir import Dir
9
+ from rclone_api.remote import Remote
10
+ from rclone_api.rpath import RPath
6
11
  from rclone_api.types import Config
7
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
+
8
59
 
9
60
  def get_rclone_exe(rclone_exe: Path | None) -> Path:
10
61
  if rclone_exe is None:
@@ -20,10 +71,11 @@ def rclone_execute(
20
71
  cmd: list[str],
21
72
  rclone_conf: Path | Config,
22
73
  rclone_exe: Path,
23
- verbose: bool = False,
74
+ verbose: bool | None = None,
24
75
  ) -> subprocess.CompletedProcess:
25
- print(subprocess.list2cmdline(cmd))
26
76
  tempdir: TemporaryDirectory | None = None
77
+ verbose = _get_verbose(verbose)
78
+ assert verbose is not None
27
79
 
28
80
  try:
29
81
  if isinstance(rclone_conf, Config):
@@ -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`
@@ -1,18 +1,18 @@
1
1
  rclone_api/__init__.py,sha256=YH7KQaPwUiJWJiRf0NRKD7XHhMXsxWdXDjt9WLSwdjA,265
2
2
  rclone_api/cli.py,sha256=dibfAZIh0kXWsBbfp3onKLjyZXo54mTzDjUdzJlDlWo,231
3
3
  rclone_api/config.py,sha256=UujjDNcpwgipgdyFPMhC3zikvlsrurUvvZiwXm5NlPg,471
4
- rclone_api/dir.py,sha256=6rOjqBkITzA0nZE9aIZ15HbCbVDgxluM-LxhSrYrNXU,1487
4
+ rclone_api/dir.py,sha256=tu0-Yy1s3O1MVY7Nkb6NOh6t9dXJ9DQvqOZVlWIexrI,1629
5
5
  rclone_api/dir_listing.py,sha256=NWleKHCCRW7_eh9JfRwE6r3QbjmiHD5ZEGQcd2vm4uY,458
6
- rclone_api/file.py,sha256=409neYPfCUKQX2w7Uw7_D0wR2PtH42srNe9u_nnOLxM,925
7
- rclone_api/rclone.py,sha256=EUsLW5qcipI5RzhpHUd0PQqQow43m2uKJmNgVR8ZBuU,2859
6
+ rclone_api/file.py,sha256=nJwJT7v2KwY_8g7UB3Y7O6pFfHn3nri9dJ3GS-cN3EE,874
7
+ rclone_api/rclone.py,sha256=upMqUxYKC3LCjHlhI66_IdOD1t5PKo7V64sSdw0vAlk,3074
8
8
  rclone_api/remote.py,sha256=c9hlRKBCg1BFB9MCINaQIoCg10qyAkeqiS4brl8ce-8,343
9
- rclone_api/rpath.py,sha256=_k59z-O75hAhpPyUSH4glPEoiYtIS3RUQb7ltU9Ianw,2009
9
+ rclone_api/rpath.py,sha256=QHD5zDjDIkivs3kCRYtTL8mP-DspXIvBVYWHSf_Y6Rg,2263
10
10
  rclone_api/types.py,sha256=Yp15HrjwZonSQhE323R0WP7fA4NWqKjAfM7z3OwHpWI,518
11
- rclone_api/util.py,sha256=EXnijLLdFHqwoZvHrZdqeM8r6T7ad9-pN7qBN1b0I_g,1465
11
+ rclone_api/util.py,sha256=xYUj0B9W5HDauTHh7Z8cnEQsTEuXvRcVNLtk3ZanlGM,2894
12
12
  rclone_api/walk.py,sha256=3GKnu9P5aPNiftfoi52U7wo1wixZxwxdqqS0_IlMGCE,2816
13
13
  rclone_api/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
14
- rclone_api-1.0.11.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
15
- rclone_api-1.0.11.dist-info/METADATA,sha256=kS-htI-Jrl56FEYxndEQm7JRmSWxHP5UpgDG9FL14_w,1245
16
- rclone_api-1.0.11.dist-info/WHEEL,sha256=9Hm2OB-j1QcCUq9Jguht7ayGIIZBRTdOXD1qg9cCgPM,109
17
- rclone_api-1.0.11.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
18
- rclone_api-1.0.11.dist-info/RECORD,,
14
+ rclone_api-1.0.13.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
15
+ rclone_api-1.0.13.dist-info/METADATA,sha256=yEL4GNzAc-CwJt-rJVmDAl08L13RqytSSs1ZQR4KMA0,1375
16
+ rclone_api-1.0.13.dist-info/WHEEL,sha256=9Hm2OB-j1QcCUq9Jguht7ayGIIZBRTdOXD1qg9cCgPM,109
17
+ rclone_api-1.0.13.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
18
+ rclone_api-1.0.13.dist-info/RECORD,,
@@ -1,36 +0,0 @@
1
- Metadata-Version: 2.2
2
- Name: rclone_api
3
- Version: 1.0.11
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`.