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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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`