rclone-api 1.0.15__py2.py3-none-any.whl → 1.0.17__py2.py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
rclone_api/__init__.py CHANGED
@@ -3,6 +3,7 @@ from .dir import Dir
3
3
  from .dir_listing import DirListing
4
4
  from .file import File
5
5
  from .filelist import FileList
6
+ from .process import Process
6
7
  from .rclone import Rclone
7
8
  from .remote import Remote
8
9
  from .rpath import RPath
@@ -16,4 +17,5 @@ __all__ = [
16
17
  "RPath",
17
18
  "DirListing",
18
19
  "FileList",
20
+ "Process",
19
21
  ]
rclone_api/exec.py CHANGED
@@ -3,6 +3,7 @@ from dataclasses import dataclass
3
3
  from pathlib import Path
4
4
 
5
5
  from rclone_api.config import Config
6
+ from rclone_api.process import Process, ProcessArgs
6
7
 
7
8
 
8
9
  @dataclass
@@ -17,3 +18,15 @@ class RcloneExec:
17
18
  from rclone_api.util import rclone_execute
18
19
 
19
20
  return rclone_execute(cmd, self.rclone_config, self.rclone_exe, check=check)
21
+
22
+ def launch_process(self, cmd: list[str]) -> Process:
23
+ """Launch rclone process."""
24
+
25
+ args: ProcessArgs = ProcessArgs(
26
+ cmd=cmd,
27
+ rclone_conf=self.rclone_config,
28
+ rclone_exe=self.rclone_exe,
29
+ cmd_list=cmd,
30
+ )
31
+ process = Process(args)
32
+ return process
rclone_api/process.py ADDED
@@ -0,0 +1,124 @@
1
+ import os
2
+ import subprocess
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+ from tempfile import TemporaryDirectory
6
+ from typing import Any
7
+
8
+ from rclone_api.config import Config
9
+ from rclone_api.util import get_verbose
10
+
11
+ # def rclone_launch_process(
12
+ # cmd: list[str],
13
+ # rclone_conf: Path | Config,
14
+ # rclone_exe: Path,
15
+ # verbose: bool | None = None,
16
+ # ) -> subprocess.Popen:
17
+ # tempdir: TemporaryDirectory | None = None
18
+ # verbose = _get_verbose(verbose)
19
+ # assert verbose is not None
20
+
21
+ # try:
22
+ # if isinstance(rclone_conf, Config):
23
+ # tempdir = TemporaryDirectory()
24
+ # tmpfile = Path(tempdir.name) / "rclone.conf"
25
+ # tmpfile.write_text(rclone_conf.text, encoding="utf-8")
26
+ # rclone_conf = tmpfile
27
+ # cmd = (
28
+ # [str(rclone_exe.resolve())] + ["--config", str(rclone_conf.resolve())] + cmd
29
+ # )
30
+ # if verbose:
31
+ # cmd_str = subprocess.list2cmdline(cmd)
32
+ # print(f"Running: {cmd_str}")
33
+ # cp = subprocess.Popen(
34
+ # cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False
35
+ # )
36
+ # return cp
37
+ # finally:
38
+ # if tempdir:
39
+ # try:
40
+ # tempdir.cleanup()
41
+ # except Exception as e:
42
+ # print(f"Error cleaning up tempdir: {e}")
43
+
44
+
45
+ def _get_verbose(verbose: bool | None) -> bool:
46
+ if verbose is not None:
47
+ return verbose
48
+ # get it from the environment
49
+ return bool(int(os.getenv("RCLONE_API_VERBOSE", "0")))
50
+
51
+
52
+ @dataclass
53
+ class ProcessArgs:
54
+ cmd: list[str]
55
+ rclone_conf: Path | Config
56
+ rclone_exe: Path
57
+ cmd_list: list[str]
58
+ verbose: bool | None = None
59
+
60
+
61
+ class Process:
62
+ def __init__(self, args: ProcessArgs) -> None:
63
+ assert args.rclone_exe.exists()
64
+ self.args = args
65
+ self.tempdir: TemporaryDirectory | None = None
66
+ verbose = get_verbose(args.verbose)
67
+ if isinstance(args.rclone_conf, Config):
68
+ self.tempdir = TemporaryDirectory()
69
+ tmpfile = Path(self.tempdir.name) / "rclone.conf"
70
+ tmpfile.write_text(args.rclone_conf.text, encoding="utf-8")
71
+ rclone_conf = tmpfile
72
+ else:
73
+ rclone_conf = args.rclone_conf
74
+
75
+ assert rclone_conf.exists()
76
+
77
+ self.cmd = (
78
+ [str(args.rclone_exe.resolve())]
79
+ + ["--config", str(rclone_conf.resolve())]
80
+ + args.cmd
81
+ )
82
+ if verbose:
83
+ cmd_str = subprocess.list2cmdline(self.cmd)
84
+ print(f"Running: {cmd_str}")
85
+ self.process = subprocess.Popen(self.cmd, shell=False)
86
+
87
+ def cleanup(self) -> None:
88
+ if self.tempdir:
89
+ try:
90
+ self.tempdir.cleanup()
91
+ except Exception as e:
92
+ print(f"Error cleaning up tempdir: {e}")
93
+
94
+ def __del__(self) -> None:
95
+ self.cleanup()
96
+
97
+ def kill(self) -> None:
98
+ self.cleanup()
99
+ return self.process.kill()
100
+
101
+ def terminate(self) -> None:
102
+ self.cleanup()
103
+ return self.process.terminate()
104
+
105
+ @property
106
+ def returncode(self) -> int | None:
107
+ return self.process.returncode
108
+
109
+ @property
110
+ def stdout(self) -> Any:
111
+ return self.process.stdout
112
+
113
+ @property
114
+ def stderr(self) -> Any:
115
+ return self.process.stderr
116
+
117
+ def poll(self) -> int | None:
118
+ return self.process.poll()
119
+
120
+ def wait(self) -> int:
121
+ return self.process.wait()
122
+
123
+ def send_signal(self, signal: int) -> None:
124
+ return self.process.send_signal(signal)
rclone_api/rclone.py CHANGED
@@ -3,6 +3,7 @@ Unit test file.
3
3
  """
4
4
 
5
5
  import subprocess
6
+ import time
6
7
  from concurrent.futures import ThreadPoolExecutor
7
8
  from fnmatch import fnmatch
8
9
  from pathlib import Path
@@ -15,6 +16,7 @@ from rclone_api.dir_listing import DirListing
15
16
  from rclone_api.exec import RcloneExec
16
17
  from rclone_api.file import File
17
18
  from rclone_api.filelist import FileList
19
+ from rclone_api.process import Process
18
20
  from rclone_api.remote import Remote
19
21
  from rclone_api.rpath import RPath
20
22
  from rclone_api.util import get_rclone_exe, to_path
@@ -33,6 +35,9 @@ class Rclone:
33
35
  def _run(self, cmd: list[str], check: bool = True) -> subprocess.CompletedProcess:
34
36
  return self._exec.execute(cmd, check=check)
35
37
 
38
+ def _launch_process(self, cmd: list[str]) -> Process:
39
+ return self._exec.launch_process(cmd)
40
+
36
41
  def ls(
37
42
  self,
38
43
  path: Dir | Remote | str,
@@ -232,3 +237,37 @@ class Rclone:
232
237
  if args is not None:
233
238
  cmd_list += args
234
239
  return self._run(cmd_list)
240
+
241
+ def mount(
242
+ self, src: Remote | Dir | str, outdir: Path, allow_writes=False, use_links=True
243
+ ) -> Process:
244
+ """Mount a remote or directory to a local path.
245
+
246
+ Args:
247
+ src: Remote or directory to mount
248
+ outdir: Local path to mount to
249
+
250
+ Returns:
251
+ CompletedProcess from the mount command execution
252
+
253
+ Raises:
254
+ subprocess.CalledProcessError: If the mount operation fails
255
+ """
256
+ if outdir.exists():
257
+ is_empty = not list(outdir.iterdir())
258
+ if not is_empty:
259
+ raise ValueError(
260
+ f"Mount directory already exists and is not empty: {outdir}"
261
+ )
262
+ outdir.rmdir()
263
+ src_str = convert_to_str(src)
264
+ cmd_list: list[str] = ["mount", src_str, str(outdir)]
265
+ if not allow_writes:
266
+ cmd_list.append("--read-only")
267
+ if use_links:
268
+ cmd_list.append("--links")
269
+ proc = self._launch_process(cmd_list)
270
+ time.sleep(2) # give it a moment to mount
271
+ if proc.poll() is not None:
272
+ raise ValueError("Mount process failed to start")
273
+ return proc
rclone_api/util.py CHANGED
@@ -54,7 +54,7 @@ def to_path(item: Dir | Remote | str, rclone: Any) -> RPath:
54
54
  raise ValueError(f"Invalid type for item: {type(item)}")
55
55
 
56
56
 
57
- def _get_verbose(verbose: bool | None) -> bool:
57
+ def get_verbose(verbose: bool | None) -> bool:
58
58
  if verbose is not None:
59
59
  return verbose
60
60
  # get it from the environment
@@ -79,7 +79,7 @@ def rclone_execute(
79
79
  verbose: bool | None = None,
80
80
  ) -> subprocess.CompletedProcess:
81
81
  tempdir: TemporaryDirectory | None = None
82
- verbose = _get_verbose(verbose)
82
+ verbose = get_verbose(verbose)
83
83
  assert verbose is not None
84
84
 
85
85
  try:
@@ -0,0 +1,149 @@
1
+ Metadata-Version: 2.2
2
+ Name: rclone_api
3
+ Version: 1.0.17
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 a pretty low level api without the bells and whistles of other apis, but it will get the job done.
25
+
26
+ You will need to have rclone installed and on your path.
27
+
28
+ One of the benefits of this api is that it does not use `shell=True`, which can keep `rclone` running in some instances even when try to kill the process.
29
+
30
+ # Install
31
+
32
+ `pip install rclone-api`
33
+
34
+
35
+ # Examples
36
+
37
+ You can use env variables or use a `.env` file to store your secrets.
38
+
39
+
40
+ # Rclone API Usage Examples
41
+
42
+ This script demonstrates how to interact with DigitalOcean Spaces using `rclone_api`.
43
+
44
+ ## Setup & Usage
45
+
46
+ Ensure you have set the required environment variables:
47
+
48
+ - `BUCKET_NAME`
49
+ - `BUCKET_KEY_PUBLIC`
50
+ - `BUCKET_KEY_SECRET`
51
+ - `BUCKET_URL`
52
+
53
+ Then, run the following Python script:
54
+
55
+ ```python
56
+ import os
57
+ from rclone_api import Config, DirListing, File, Rclone, Remote
58
+
59
+ # Load environment variables
60
+ BUCKET_NAME = os.getenv("BUCKET_NAME")
61
+ BUCKET_KEY_PUBLIC = os.getenv("BUCKET_KEY_PUBLIC")
62
+ BUCKET_KEY_SECRET = os.getenv("BUCKET_KEY_SECRET")
63
+ BUCKET_URL = "sfo3.digitaloceanspaces.com"
64
+
65
+ # Generate Rclone Configuration
66
+ def generate_rclone_config() -> Config:
67
+ config_text = f"""
68
+ [dst]
69
+ type = s3
70
+ provider = DigitalOcean
71
+ access_key_id = {BUCKET_KEY_PUBLIC}
72
+ secret_access_key = {BUCKET_KEY_SECRET}
73
+ endpoint = {BUCKET_URL}
74
+ """
75
+ return Config(config_text)
76
+
77
+ rclone = Rclone(generate_rclone_config())
78
+
79
+ # List Available Remotes
80
+ print("\n=== Available Remotes ===")
81
+ remotes = rclone.listremotes()
82
+ for remote in remotes:
83
+ print(remote)
84
+
85
+ # List Contents of the Root Bucket
86
+ print("\n=== Listing Root Bucket ===")
87
+ listing = rclone.ls(f"dst:{BUCKET_NAME}", max_depth=-1)
88
+
89
+ print("\nDirectories:")
90
+ for dir in listing.dirs:
91
+ print(dir)
92
+
93
+ print("\nFiles:")
94
+ for file in listing.files:
95
+ print(file)
96
+
97
+ # List a Specific Subdirectory
98
+ print("\n=== Listing 'zachs_video' Subdirectory ===")
99
+ path = f"dst:{BUCKET_NAME}/zachs_video"
100
+ listing = rclone.ls(path)
101
+ print(listing)
102
+
103
+ # List PNG Files in a Subdirectory
104
+ print("\n=== Listing PNG Files ===")
105
+ listing = rclone.ls(path, glob="*.png")
106
+
107
+ if listing.files:
108
+ for file in listing.files:
109
+ print(file)
110
+
111
+ # Copy a File
112
+ print("\n=== Copying a File ===")
113
+ if listing.files:
114
+ file = listing.files[0]
115
+ new_path = f"dst:{BUCKET_NAME}/zachs_video/{file.name}_copy"
116
+ rclone.copyfile(file, new_path)
117
+ print(f"Copied {file.name} to {new_path}")
118
+
119
+ # Copy Multiple Files
120
+ print("\n=== Copying Multiple Files ===")
121
+ if listing.files:
122
+ file_mapping = {file.name: file.name + "_copy" for file in listing.files[:2]}
123
+ rclone.copyfiles(file_mapping)
124
+ print(f"Copied files: {file_mapping}")
125
+
126
+ # Delete a File
127
+ print("\n=== Deleting a File ===")
128
+ file_to_delete = f"dst:{BUCKET_NAME}/zachs_video/sample.png_copy"
129
+ rclone.deletefiles([file_to_delete])
130
+ print(f"Deleted {file_to_delete}")
131
+
132
+ # Walk Through a Directory
133
+ print("\n=== Walking Through a Directory ===")
134
+ for dirlisting in rclone.walk(f"dst:{BUCKET_NAME}", max_depth=1):
135
+ print(dirlisting)
136
+
137
+ print("Done.")
138
+ ```
139
+
140
+
141
+ To develop software, run `. ./activate`
142
+
143
+ # Windows
144
+
145
+ This environment requires you to use `git-bash`.
146
+
147
+ # Linting
148
+
149
+ Run `./lint`
@@ -1,20 +1,21 @@
1
- rclone_api/__init__.py,sha256=BqlqMsxNo5YkSo0ov7CRM5WsyAh2yJQa9Fpodc-rQ7I,344
1
+ rclone_api/__init__.py,sha256=UWQMbhE4WOQ3Skfb0LagFKW8PUKUGOm9_T3z--5FHiY,388
2
2
  rclone_api/cli.py,sha256=dibfAZIh0kXWsBbfp3onKLjyZXo54mTzDjUdzJlDlWo,231
3
3
  rclone_api/config.py,sha256=tP6cU9DnCCEIRc_KP9HPur1jFLLg2QGFSxNwFm6_MVw,118
4
4
  rclone_api/convert.py,sha256=Mx9Qo7zhkOedJd8LdhPvNGHp8znJzOk4f_2KWnoGc78,1012
5
5
  rclone_api/dir.py,sha256=xUV4i9_E7QHrqHld2IPBlon1CGaAp8qQYMKfJTyVvoQ,2088
6
6
  rclone_api/dir_listing.py,sha256=8t5Jx9ZVOJPqGKTJbWaES6bjgogUT2bnpbPVWwK1Fcs,1124
7
- rclone_api/exec.py,sha256=djQrWtziTIroEb5YJQxDyTJDGK39xN0fWtv0HJGnjgk,498
7
+ rclone_api/exec.py,sha256=9qSOpZo8YRYxv3hOvNr57ApnY2KbjxwT1QNr8OgcLM4,883
8
8
  rclone_api/file.py,sha256=D02iHJW1LhfOiM_R_yPHP8_ApnDiYrkuraVcrV8-qkw,1246
9
9
  rclone_api/filelist.py,sha256=xbiusvNgaB_b_kQOZoHMJJxn6TWGtPrWd2J042BI28o,767
10
- rclone_api/rclone.py,sha256=BQaT11O0eyCLxGrXDNJ7fXeVxgmg4j5UsDSjoTuRPfg,8267
10
+ rclone_api/process.py,sha256=QLI-65R4Jvov9oOJSVY5UpDOS2xj6m-9YCg8upR4-0M,3560
11
+ rclone_api/rclone.py,sha256=hvzo5zZmP5IQAtuwLcsTeeazDY0slqz76DdulV2mn3s,9640
11
12
  rclone_api/remote.py,sha256=c9hlRKBCg1BFB9MCINaQIoCg10qyAkeqiS4brl8ce-8,343
12
13
  rclone_api/rpath.py,sha256=8ZA_1wxWtskwcy0I8V2VbjKDmzPkiWd8Q2JQSvh-sYE,2586
13
- rclone_api/util.py,sha256=24rHFv6NqgXPaqhpy4spGyVcoN2GjrDwEWtoD7LRllk,3345
14
+ rclone_api/util.py,sha256=BDRJ2MIceDoKVTjUQBwyhjbA8UwPrZ-0ZSa9xyMJd0E,3343
14
15
  rclone_api/walk.py,sha256=J78-bY2AhNpt2ICsI5LqmXRE7oC6wVDoKoicIoU6XMg,1953
15
16
  rclone_api/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
16
- rclone_api-1.0.15.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
17
- rclone_api-1.0.15.dist-info/METADATA,sha256=WbUA2T_9meHMVxa5XtK14ypTSRDG8KxJbH2t3J4VDYM,1375
18
- rclone_api-1.0.15.dist-info/WHEEL,sha256=9Hm2OB-j1QcCUq9Jguht7ayGIIZBRTdOXD1qg9cCgPM,109
19
- rclone_api-1.0.15.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
20
- rclone_api-1.0.15.dist-info/RECORD,,
17
+ rclone_api-1.0.17.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
18
+ rclone_api-1.0.17.dist-info/METADATA,sha256=9_ahew4G4YXDjXQljGkoA8s1KZa76mPrv3TZ99c2Xo4,4421
19
+ rclone_api-1.0.17.dist-info/WHEEL,sha256=9Hm2OB-j1QcCUq9Jguht7ayGIIZBRTdOXD1qg9cCgPM,109
20
+ rclone_api-1.0.17.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
21
+ rclone_api-1.0.17.dist-info/RECORD,,
@@ -1,34 +0,0 @@
1
- Metadata-Version: 2.2
2
- Name: rclone_api
3
- Version: 1.0.15
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`