rclone-api 1.3.4__py2.py3-none-any.whl → 1.3.6__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.
@@ -258,9 +258,6 @@ def upload_file_multipart(
258
258
  try:
259
259
  while True:
260
260
  file_chunk: FilePart | EndOfStream = queue_upload.get()
261
- if file_chunk is EndOfStream:
262
- break
263
-
264
261
  if isinstance(file_chunk, EndOfStream):
265
262
  break
266
263
 
@@ -291,6 +288,12 @@ def upload_file_multipart(
291
288
  if not upload_state.is_done():
292
289
  upload_state.save()
293
290
  return MultiUploadResult.SUSPENDED
291
+ ######################## COMPLETE UPLOAD #######################
292
+ # Final part now is to complete the upload
293
+ msg = "\n########################################"
294
+ msg += f"# Upload complete, sorting {len(upload_state.parts)} parts to complete upload"
295
+ msg += "########################################\n"
296
+ locked_print(msg)
294
297
  parts: list[FinishedPiece] = [
295
298
  p for p in upload_state.parts if not isinstance(p, EndOfStream)
296
299
  ]
rclone_api/util.py CHANGED
@@ -1,162 +1,183 @@
1
- import os
2
- import shutil
3
- import subprocess
4
- import warnings
5
- from pathlib import Path
6
- from tempfile import TemporaryDirectory
7
- from threading import Lock
8
- from typing import Any
9
-
10
- from rclone_api.config import Config
11
- from rclone_api.dir import Dir
12
- from rclone_api.remote import Remote
13
- from rclone_api.rpath import RPath
14
- from rclone_api.types import S3PathInfo
15
-
16
- # from .rclone import Rclone
17
-
18
- _PRINT_LOCK = Lock()
19
-
20
-
21
- def locked_print(*args, **kwargs):
22
- with _PRINT_LOCK:
23
- print(*args, **kwargs)
24
-
25
-
26
- def to_path(item: Dir | Remote | str, rclone: Any) -> RPath:
27
- from rclone_api.rclone import Rclone
28
-
29
- assert isinstance(rclone, Rclone)
30
- # if str then it will be remote:path
31
- if isinstance(item, str):
32
- # return RPath(item)
33
- # remote_name: str = item.split(":")[0]
34
- parts = item.split(":")
35
- remote_name = parts[0]
36
- path = ":".join(parts[1:])
37
- remote = Remote(name=remote_name, rclone=rclone)
38
- out = RPath(
39
- remote=remote,
40
- path=path,
41
- name="",
42
- size=0,
43
- mime_type="",
44
- mod_time="",
45
- is_dir=True,
46
- )
47
- out.set_rclone(rclone)
48
- return out
49
- elif isinstance(item, Dir):
50
- return item.path
51
- elif isinstance(item, Remote):
52
- out = RPath(
53
- remote=item,
54
- path=str(item),
55
- name=str(item),
56
- size=0,
57
- mime_type="inode/directory",
58
- mod_time="",
59
- is_dir=True,
60
- )
61
- out.set_rclone(rclone)
62
- return out
63
- else:
64
- raise ValueError(f"Invalid type for item: {type(item)}")
65
-
66
-
67
- def get_verbose(verbose: bool | None) -> bool:
68
- if verbose is not None:
69
- return verbose
70
- # get it from the environment
71
- return bool(int(os.getenv("RCLONE_API_VERBOSE", "0")))
72
-
73
-
74
- def get_check(check: bool | None) -> bool:
75
- if check is not None:
76
- return check
77
- # get it from the environment
78
- return bool(int(os.getenv("RCLONE_API_CHECK", "1")))
79
-
80
-
81
- def get_rclone_exe(rclone_exe: Path | None) -> Path:
82
- if rclone_exe is None:
83
-
84
- rclone_which_path = shutil.which("rclone")
85
- if rclone_which_path is None:
86
- raise ValueError("rclone executable not found")
87
- return Path(rclone_which_path)
88
- return rclone_exe
89
-
90
-
91
- def rclone_execute(
92
- cmd: list[str],
93
- rclone_conf: Path | Config,
94
- rclone_exe: Path,
95
- check: bool,
96
- capture: bool | None = None,
97
- verbose: bool | None = None,
98
- ) -> subprocess.CompletedProcess:
99
- tempdir: TemporaryDirectory | None = None
100
- verbose = get_verbose(verbose)
101
- capture = capture if isinstance(capture, bool) else True
102
- assert verbose is not None
103
-
104
- try:
105
- if isinstance(rclone_conf, Config):
106
- tempdir = TemporaryDirectory()
107
- tmpfile = Path(tempdir.name) / "rclone.conf"
108
- tmpfile.write_text(rclone_conf.text, encoding="utf-8")
109
- rclone_conf = tmpfile
110
- cmd = (
111
- [str(rclone_exe.resolve())] + ["--config", str(rclone_conf.resolve())] + cmd
112
- )
113
- if verbose:
114
- cmd_str = subprocess.list2cmdline(cmd)
115
- print(f"\nRunning: {cmd_str}")
116
- cp = subprocess.run(
117
- cmd, capture_output=capture, encoding="utf-8", check=False, shell=False
118
- )
119
- if cp.returncode != 0:
120
- cmd_str = subprocess.list2cmdline(cmd)
121
- warnings.warn(
122
- f"Error running: {cmd_str}, returncode: {cp.returncode}\n{cp.stdout}\n{cp.stderr}"
123
- )
124
- if check:
125
- raise subprocess.CalledProcessError(
126
- cp.returncode, cmd, cp.stdout, cp.stderr
127
- )
128
- return cp
129
- finally:
130
- if tempdir:
131
- try:
132
- tempdir.cleanup()
133
- except Exception as e:
134
- print(f"Error cleaning up tempdir: {e}")
135
-
136
-
137
- def split_s3_path(path: str) -> S3PathInfo:
138
- if ":" not in path:
139
- raise ValueError(f"Invalid S3 path: {path}")
140
-
141
- prts = path.split(":", 1)
142
- remote = prts[0]
143
- path = prts[1]
144
- parts: list[str] = []
145
- for part in path.split("/"):
146
- part = part.strip()
147
- if part:
148
- parts.append(part)
149
- if len(parts) < 2:
150
- raise ValueError(f"Invalid S3 path: {path}")
151
- bucket = parts[0]
152
- key = "/".join(parts[1:])
153
- assert bucket
154
- assert key
155
- return S3PathInfo(remote=remote, bucket=bucket, key=key)
156
-
157
-
158
- def random_str(length: int) -> str:
159
- import random
160
- import string
161
-
162
- return "".join(random.choices(string.ascii_lowercase + string.digits, k=length))
1
+ import os
2
+ import shutil
3
+ import subprocess
4
+ import warnings
5
+ from pathlib import Path
6
+ from tempfile import TemporaryDirectory
7
+ from threading import Lock
8
+ from typing import Any
9
+
10
+ from rclone_api.config import Config
11
+ from rclone_api.dir import Dir
12
+ from rclone_api.remote import Remote
13
+ from rclone_api.rpath import RPath
14
+ from rclone_api.types import S3PathInfo
15
+
16
+ # from .rclone import Rclone
17
+
18
+ _PRINT_LOCK = Lock()
19
+
20
+
21
+ def locked_print(*args, **kwargs):
22
+ with _PRINT_LOCK:
23
+ print(*args, **kwargs)
24
+
25
+
26
+ def to_path(item: Dir | Remote | str, rclone: Any) -> RPath:
27
+ from rclone_api.rclone import Rclone
28
+
29
+ assert isinstance(rclone, Rclone)
30
+ # if str then it will be remote:path
31
+ if isinstance(item, str):
32
+ # return RPath(item)
33
+ # remote_name: str = item.split(":")[0]
34
+ parts = item.split(":")
35
+ remote_name = parts[0]
36
+ path = ":".join(parts[1:])
37
+ remote = Remote(name=remote_name, rclone=rclone)
38
+ out = RPath(
39
+ remote=remote,
40
+ path=path,
41
+ name="",
42
+ size=0,
43
+ mime_type="",
44
+ mod_time="",
45
+ is_dir=True,
46
+ )
47
+ out.set_rclone(rclone)
48
+ return out
49
+ elif isinstance(item, Dir):
50
+ return item.path
51
+ elif isinstance(item, Remote):
52
+ out = RPath(
53
+ remote=item,
54
+ path=str(item),
55
+ name=str(item),
56
+ size=0,
57
+ mime_type="inode/directory",
58
+ mod_time="",
59
+ is_dir=True,
60
+ )
61
+ out.set_rclone(rclone)
62
+ return out
63
+ else:
64
+ raise ValueError(f"Invalid type for item: {type(item)}")
65
+
66
+
67
+ def get_verbose(verbose: bool | None) -> bool:
68
+ if verbose is not None:
69
+ return verbose
70
+ # get it from the environment
71
+ return bool(int(os.getenv("RCLONE_API_VERBOSE", "0")))
72
+
73
+
74
+ def get_check(check: bool | None) -> bool:
75
+ if check is not None:
76
+ return check
77
+ # get it from the environment
78
+ return bool(int(os.getenv("RCLONE_API_CHECK", "1")))
79
+
80
+
81
+ def get_rclone_exe(rclone_exe: Path | None) -> Path:
82
+ if rclone_exe is None:
83
+
84
+ rclone_which_path = shutil.which("rclone")
85
+ if rclone_which_path is None:
86
+ raise ValueError("rclone executable not found")
87
+ return Path(rclone_which_path)
88
+ return rclone_exe
89
+
90
+
91
+ def rclone_execute(
92
+ cmd: list[str],
93
+ rclone_conf: Path | Config,
94
+ rclone_exe: Path,
95
+ check: bool,
96
+ capture: bool | Path | None = None,
97
+ verbose: bool | None = None,
98
+ ) -> subprocess.CompletedProcess:
99
+ tempdir: TemporaryDirectory | None = None
100
+ verbose = get_verbose(verbose)
101
+
102
+ # Handle the Path case for capture
103
+ output_file = None
104
+ if isinstance(capture, Path):
105
+ output_file = capture
106
+ capture = False # Don't capture to memory when redirecting to file
107
+ else:
108
+ capture = capture if isinstance(capture, bool) else True
109
+
110
+ assert verbose is not None
111
+
112
+ try:
113
+ if isinstance(rclone_conf, Config):
114
+ tempdir = TemporaryDirectory()
115
+ tmpfile = Path(tempdir.name) / "rclone.conf"
116
+ tmpfile.write_text(rclone_conf.text, encoding="utf-8")
117
+ rclone_conf = tmpfile
118
+ cmd = (
119
+ [str(rclone_exe.resolve())] + ["--config", str(rclone_conf.resolve())] + cmd
120
+ )
121
+ if verbose:
122
+ cmd_str = subprocess.list2cmdline(cmd)
123
+ print(f"\nRunning: {cmd_str}")
124
+
125
+ # If output_file is set, redirect output to that file
126
+ if output_file:
127
+ with open(output_file, "w", encoding="utf-8") as f:
128
+ cp = subprocess.run(
129
+ cmd,
130
+ stdout=f,
131
+ stderr=subprocess.PIPE,
132
+ encoding="utf-8",
133
+ check=False,
134
+ shell=False,
135
+ )
136
+ else:
137
+ cp = subprocess.run(
138
+ cmd, capture_output=capture, encoding="utf-8", check=False, shell=False
139
+ )
140
+ if cp.returncode != 0:
141
+ cmd_str = subprocess.list2cmdline(cmd)
142
+ warnings.warn(
143
+ f"Error running: {cmd_str}, returncode: {cp.returncode}\n{cp.stdout}\n{cp.stderr}"
144
+ )
145
+ if check:
146
+ raise subprocess.CalledProcessError(
147
+ cp.returncode, cmd, cp.stdout, cp.stderr
148
+ )
149
+ return cp
150
+ finally:
151
+ if tempdir:
152
+ try:
153
+ tempdir.cleanup()
154
+ except Exception as e:
155
+ print(f"Error cleaning up tempdir: {e}")
156
+
157
+
158
+ def split_s3_path(path: str) -> S3PathInfo:
159
+ if ":" not in path:
160
+ raise ValueError(f"Invalid S3 path: {path}")
161
+
162
+ prts = path.split(":", 1)
163
+ remote = prts[0]
164
+ path = prts[1]
165
+ parts: list[str] = []
166
+ for part in path.split("/"):
167
+ part = part.strip()
168
+ if part:
169
+ parts.append(part)
170
+ if len(parts) < 2:
171
+ raise ValueError(f"Invalid S3 path: {path}")
172
+ bucket = parts[0]
173
+ key = "/".join(parts[1:])
174
+ assert bucket
175
+ assert key
176
+ return S3PathInfo(remote=remote, bucket=bucket, key=key)
177
+
178
+
179
+ def random_str(length: int) -> str:
180
+ import random
181
+ import string
182
+
183
+ return "".join(random.choices(string.ascii_lowercase + string.digits, k=length))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.3.4
3
+ Version: 1.3.6
4
4
  Summary: rclone api in python
5
5
  Home-page: https://github.com/zackees/rclone-api
6
6
  License: BSD 3-Clause License
@@ -7,37 +7,37 @@ rclone_api/deprecated.py,sha256=qWKpnZdYcBK7YQZKuVoWWXDwi-uqiAtbjgPcci_efow,590
7
7
  rclone_api/diff.py,sha256=tMoJMAGmLSE6Q_7QhPf6PnCzb840djxMZtDmhc2GlGQ,5227
8
8
  rclone_api/dir.py,sha256=i4h7LX5hB_WmVixxDRWL_l1nifvscrdWct_8Wx7wHZc,3540
9
9
  rclone_api/dir_listing.py,sha256=GoziW8Sne6FY90MLNcb2aO3aaa3jphB6H8ExYrV0Ryo,1882
10
- rclone_api/exec.py,sha256=Pd7pUBd8ib5MzqvMybG2DQISPRbDRu20VjVRL2mLAVY,1076
10
+ rclone_api/exec.py,sha256=Bq0gkyZ10mEY0FRyzNZgdN4FaWP9vpeCk1kjpg-gN_8,1083
11
11
  rclone_api/file.py,sha256=EP5yT2dZ0H2p7CY5n0y5k5pHhIliV25pm8KOwBklUTk,1863
12
12
  rclone_api/filelist.py,sha256=xbiusvNgaB_b_kQOZoHMJJxn6TWGtPrWd2J042BI28o,767
13
13
  rclone_api/group_files.py,sha256=H92xPW9lQnbNw5KbtZCl00bD6iRh9yRbCuxku4j_3dg,8036
14
14
  rclone_api/log.py,sha256=VZHM7pNSXip2ZLBKMP7M1u-rp_F7zoafFDuR8CPUoKI,1271
15
15
  rclone_api/mount.py,sha256=TE_VIBMW7J1UkF_6HRCt8oi_jGdMov4S51bm2OgxFAM,10045
16
- rclone_api/mount_read_chunker.py,sha256=Pz4s6AGigB_-vTAEdLK9Fv116j87yN4Qg0Uz1COwPtQ,4504
16
+ rclone_api/mount_read_chunker.py,sha256=bi4N-VkyPdM4RWUYhIqJ71lQrXanck4PpF3pY8k2xnQ,4722
17
17
  rclone_api/process.py,sha256=rBj_S86jC6nqCYop-jq8r9eMSteKeObxUrJMgH8LZvI,5084
18
- rclone_api/rclone.py,sha256=DOLiBeRSnHE1Q0MTnhzqfm--PhN4PJebcj_U0HsGg2Q,46647
18
+ rclone_api/rclone.py,sha256=mwHEkPhofQ6BAnE0x54W5E5kTb5pChxFa965DCxViJM,49175
19
19
  rclone_api/remote.py,sha256=O9WDUFQy9f6oT1HdUbTixK2eg0xtBBm8k4Xl6aa6K00,431
20
20
  rclone_api/rpath.py,sha256=8ZA_1wxWtskwcy0I8V2VbjKDmzPkiWd8Q2JQSvh-sYE,2586
21
21
  rclone_api/scan_missing_folders.py,sha256=Kulca2Q6WZodt00ATFHkmqqInuoPvBkhTcS9703y6po,4740
22
22
  rclone_api/types.py,sha256=XiiWoGXAzNxTEfRcszw3BeRF7ZATXHIAPFg2-aJzUfo,9926
23
- rclone_api/util.py,sha256=_Z-GUMVXnHYOGdo2dy2ie2P5fGgyg8KdGjHKicx68Ko,4573
23
+ rclone_api/util.py,sha256=F9Q3zbWRsgPF4NG6OWB63cZ7GVq82lsraP47gmmDohU,5416
24
24
  rclone_api/walk.py,sha256=-54NVE8EJcCstwDoaC_UtHm73R2HrZwVwQmsnv55xNU,3369
25
25
  rclone_api/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
26
- rclone_api/cmd/copy_large_s3.py,sha256=fYHyHq2YZT_dfMbS7SCpEeLCaWD-BU-jcpKP9eKf1jk,3388
26
+ rclone_api/cmd/copy_large_s3.py,sha256=nOpAUAQN1mJnf4EIZCh4OVCW7Q4_EXJeLFVe6r_9rZA,3369
27
27
  rclone_api/cmd/list_files.py,sha256=x8FHODEilwKqwdiU1jdkeJbLwOqUkUQuDWPo2u_zpf0,741
28
28
  rclone_api/experimental/flags.py,sha256=qCVD--fSTmzlk9hloRLr0q9elzAOFzPsvVpKM3aB1Mk,2739
29
29
  rclone_api/experimental/flags_base.py,sha256=ajU_czkTcAxXYU-SlmiCfHY7aCQGHvpCLqJ-Z8uZLk0,2102
30
- rclone_api/profile/mount_copy_bytes.py,sha256=63o7-RGSK8Egr04Uyt0A9KeAa_vRpBfLX9Dz0q4IQzk,9072
30
+ rclone_api/profile/mount_copy_bytes.py,sha256=nZtqMukLhSzHq64Pn1I8pXwjoraqWjCKey3WLAeubx0,9069
31
31
  rclone_api/s3/api.py,sha256=PafsIEyWDpLWAXsZAjFm9CY14vJpsDr9lOsn0kGRLZ0,4009
32
32
  rclone_api/s3/basic_ops.py,sha256=hK3366xhVEzEcjz9Gk_8lFx6MRceAk72cax6mUrr6ko,2104
33
33
  rclone_api/s3/chunk_task.py,sha256=plrQyjuHFdJfV55kKH4wqo6QGll9n4_7BO4B1A7j6kY,7121
34
34
  rclone_api/s3/chunk_types.py,sha256=oSWv8No9V3BeM7IcGnowyR2a7YrszdAXzEJlxaeZcp0,8852
35
35
  rclone_api/s3/create.py,sha256=wgfkapv_j904CfKuWyiBIWJVxfAx_ftemFSUV14aT68,3149
36
36
  rclone_api/s3/types.py,sha256=Elmh__gvZJyJyElYwMmvYZIBIunDJiTRAbEg21GmsRU,1604
37
- rclone_api/s3/upload_file_multipart.py,sha256=UVMTSeP98fSQdOYcCdi9tV5ZjOxRDuhZbiBbVaf-rCM,11385
38
- rclone_api-1.3.4.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
39
- rclone_api-1.3.4.dist-info/METADATA,sha256=Y8c6Pc-Eol77oS-SRF9gPRb5Fg5R3HT6KOim71lR5W4,4536
40
- rclone_api-1.3.4.dist-info/WHEEL,sha256=rF4EZyR2XVS6irmOHQIJx2SUqXLZKRMUrjsg8UwN-XQ,109
41
- rclone_api-1.3.4.dist-info/entry_points.txt,sha256=TV8kwP3FRzYwUEr0RLC7aJh0W03SAefIJNXTJ-FdMIQ,200
42
- rclone_api-1.3.4.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
43
- rclone_api-1.3.4.dist-info/RECORD,,
37
+ rclone_api/s3/upload_file_multipart.py,sha256=d8ZWqO8n9wsqRF6JjmvAFmG1aCkFqdSB1L8yxe_5qiY,11669
38
+ rclone_api-1.3.6.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
39
+ rclone_api-1.3.6.dist-info/METADATA,sha256=SF9aoNhS9WW6bj-aESO1uOrKC29tmJCcZvjfJ-D4CZI,4536
40
+ rclone_api-1.3.6.dist-info/WHEEL,sha256=rF4EZyR2XVS6irmOHQIJx2SUqXLZKRMUrjsg8UwN-XQ,109
41
+ rclone_api-1.3.6.dist-info/entry_points.txt,sha256=TV8kwP3FRzYwUEr0RLC7aJh0W03SAefIJNXTJ-FdMIQ,200
42
+ rclone_api-1.3.6.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
43
+ rclone_api-1.3.6.dist-info/RECORD,,