rclone-api 1.4.15__py2.py3-none-any.whl → 1.4.19__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/util.py CHANGED
@@ -5,10 +5,13 @@ import shutil
5
5
  import signal
6
6
  import subprocess
7
7
  import warnings
8
+ import weakref
8
9
  from pathlib import Path
9
10
  from threading import Lock
10
11
  from typing import Any
11
12
 
13
+ import psutil
14
+
12
15
  from rclone_api.config import Config
13
16
  from rclone_api.dir import Dir
14
17
  from rclone_api.remote import Remote
@@ -167,51 +170,109 @@ def rclone_execute(
167
170
  tmpfile: Path | None = None
168
171
  verbose = get_verbose(verbose)
169
172
 
170
- # Handle the Path case for capture
171
- output_file = None
173
+ # Handle the Path case for capture.
174
+ output_file: Path | None = None
172
175
  if isinstance(capture, Path):
173
176
  output_file = capture
174
- capture = False # Don't capture to memory when redirecting to file
177
+ capture = False # When redirecting to file, don't capture to memory.
175
178
  else:
176
179
  capture = capture if isinstance(capture, bool) else True
177
180
 
178
- assert verbose is not None
179
-
180
181
  try:
182
+ # Create a temporary config file if needed.
181
183
  if isinstance(rclone_conf, Config):
182
184
  tmpfile = make_temp_config_file()
183
185
  tmpfile.write_text(rclone_conf.text, encoding="utf-8")
184
186
  rclone_conf = tmpfile
185
- cmd = (
187
+
188
+ # Build the command line.
189
+ full_cmd = (
186
190
  [str(rclone_exe.resolve())] + ["--config", str(rclone_conf.resolve())] + cmd
187
191
  )
188
192
  if verbose:
189
- cmd_str = subprocess.list2cmdline(cmd)
193
+ cmd_str = subprocess.list2cmdline(full_cmd)
190
194
  print(f"\nRunning: {cmd_str}")
191
195
 
192
- # If output_file is set, redirect output to that file
196
+ # Prepare subprocess parameters.
197
+ proc_kwargs: dict[str, Any] = {
198
+ "encoding": "utf-8",
199
+ "shell": False,
200
+ "stderr": subprocess.PIPE,
201
+ }
202
+ file_handle = None
193
203
  if output_file:
194
- with open(output_file, "w", encoding="utf-8") as f:
195
- cp = subprocess.run(
196
- cmd,
197
- stdout=f,
198
- stderr=subprocess.PIPE,
199
- encoding="utf-8",
200
- check=False,
201
- shell=False,
202
- )
204
+ # Open the file for writing.
205
+ file_handle = open(output_file, "w", encoding="utf-8")
206
+ proc_kwargs["stdout"] = file_handle
203
207
  else:
204
- cp = subprocess.run(
205
- cmd, capture_output=capture, encoding="utf-8", check=False, shell=False
206
- )
208
+ proc_kwargs["stdout"] = subprocess.PIPE if capture else None
209
+
210
+ # Start the process.
211
+ process = subprocess.Popen(full_cmd, **proc_kwargs)
212
+
213
+ # Register an atexit callback that uses psutil to kill the process tree.
214
+ proc_ref = weakref.ref(process)
215
+
216
+ def cleanup():
217
+ proc = proc_ref()
218
+ if proc is None:
219
+ return
220
+ try:
221
+ parent = psutil.Process(proc.pid)
222
+ except psutil.NoSuchProcess:
223
+ return
224
+ # Terminate all child processes first.
225
+ children = parent.children(recursive=True)
226
+ if children:
227
+ print(f"Terminating {len(children)} child process(es)...")
228
+ for child in children:
229
+ try:
230
+ child.terminate()
231
+ except Exception as e:
232
+ print(f"Error terminating child {child.pid}: {e}")
233
+ psutil.wait_procs(children, timeout=2)
234
+ for child in children:
235
+ if child.is_running():
236
+ try:
237
+ child.kill()
238
+ except Exception as e:
239
+ print(f"Error killing child {child.pid}: {e}")
240
+ # Now terminate the parent process.
241
+ if parent.is_running():
242
+ try:
243
+ parent.terminate()
244
+ parent.wait(timeout=3)
245
+ except (psutil.TimeoutExpired, Exception):
246
+ try:
247
+ parent.kill()
248
+ except Exception as e:
249
+ print(f"Error killing process {parent.pid}: {e}")
250
+
251
+ atexit.register(cleanup)
252
+
253
+ # Wait for the process to complete.
254
+ out, err = process.communicate()
255
+ # Close the file handle if used.
256
+ if file_handle:
257
+ file_handle.close()
258
+
259
+ cp: subprocess.CompletedProcess = subprocess.CompletedProcess(
260
+ args=full_cmd,
261
+ returncode=process.returncode,
262
+ stdout=out,
263
+ stderr=err,
264
+ )
265
+
266
+ # Warn or raise if return code is non-zero.
207
267
  if cp.returncode != 0:
208
- cmd_str = subprocess.list2cmdline(cmd)
268
+ cmd_str = subprocess.list2cmdline(full_cmd)
209
269
  warnings.warn(
210
- f"Error running: {cmd_str}, returncode: {cp.returncode}\n{cp.stdout}\n{cp.stderr}"
270
+ f"Error running: {cmd_str}, returncode: {cp.returncode}\n"
271
+ f"{cp.stdout}\n{cp.stderr}"
211
272
  )
212
273
  if check:
213
274
  raise subprocess.CalledProcessError(
214
- cp.returncode, cmd, cp.stdout, cp.stderr
275
+ cp.returncode, full_cmd, cp.stdout, cp.stderr
215
276
  )
216
277
  return cp
217
278
  finally:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.4.15
3
+ Version: 1.4.19
4
4
  Summary: rclone api in python
5
5
  Home-page: https://github.com/zackees/rclone-api
6
6
  License: BSD 3-Clause License
@@ -17,23 +17,23 @@ rclone_api/group_files.py,sha256=H92xPW9lQnbNw5KbtZCl00bD6iRh9yRbCuxku4j_3dg,803
17
17
  rclone_api/http_server.py,sha256=3fPBV6l50erTe32DyeJBNmsDrn5KuujsbmEAbx13T-c,8720
18
18
  rclone_api/log.py,sha256=VZHM7pNSXip2ZLBKMP7M1u-rp_F7zoafFDuR8CPUoKI,1271
19
19
  rclone_api/mount.py,sha256=TE_VIBMW7J1UkF_6HRCt8oi_jGdMov4S51bm2OgxFAM,10045
20
- rclone_api/process.py,sha256=I7B4arAIbFcTBsek27cZ0t-l5YRWVHJJPji7G6ZLGjQ,4989
20
+ rclone_api/process.py,sha256=tGooS5NLdPuqHh7hCH8SfK44A6LGftPQCPQUNgSo0a0,5714
21
21
  rclone_api/rclone_impl.py,sha256=xTTriz6-zn_aSrkY8B7wzT-zRXax7Og7ns6xu6-7O6g,48769
22
22
  rclone_api/remote.py,sha256=mTgMTQTwxUmbLjTpr-AGTId2ycXKI9mLX5L7PPpDIoc,520
23
23
  rclone_api/rpath.py,sha256=Y1JjQWcie39EgQrq-UtbfDz5yDLCwwfu27W7AQXllSE,2860
24
24
  rclone_api/scan_missing_folders.py,sha256=-8NCwpCaHeHrX-IepCoAEsX1rl8S-GOCxcIhTr_w3gA,4747
25
25
  rclone_api/types.py,sha256=HkpEZgZWhr5Gb04iHq5NxMRXxieWoN-PKmOfJFrg5Qg,12155
26
- rclone_api/util.py,sha256=9w_m6W62l_X42Jw5q8p_p30h-QoxAqufvnCLI4PTMOE,7056
26
+ rclone_api/util.py,sha256=yY72YKpmpT_ZM7AleVtPpl0YZZYQPTwTdqKn9qPwm8Y,9290
27
27
  rclone_api/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
28
28
  rclone_api/cmd/analyze.py,sha256=RHbvk1G5ZUc3qLqlm1AZEyQzd_W_ZjcbCNDvW4YpTKQ,1252
29
29
  rclone_api/cmd/copy_large_s3.py,sha256=B17GliDQyAauNglJCpsey0d3eArT2DAcT9g684TMQk8,3514
30
- rclone_api/cmd/copy_large_s3_finish.py,sha256=6fP95UU4ueBkGmkhX4YDu1ze0HIJlgYhCGF5z9IFc3k,5015
30
+ rclone_api/cmd/copy_large_s3_finish.py,sha256=Q_8uvC2J3V_jGQj8ualMlejWEPpkFn267bUKLC3Sp2M,2050
31
31
  rclone_api/cmd/list_files.py,sha256=x8FHODEilwKqwdiU1jdkeJbLwOqUkUQuDWPo2u_zpf0,741
32
32
  rclone_api/cmd/save_to_db.py,sha256=ylvnhg_yzexM-m6Zr7XDiswvoDVSl56ELuFAdb9gqBY,1957
33
33
  rclone_api/db/__init__.py,sha256=OSRUdnSWUlDTOHmjdjVmxYTUNpTbtaJ5Ll9sl-PfZg0,40
34
34
  rclone_api/db/db.py,sha256=YRnYrCaXHwytQt07uEZ_mMpvPHo9-0IWcOb95fVOOfs,10086
35
35
  rclone_api/db/models.py,sha256=v7qaXUehvsDvU51uk69JI23fSIs9JFGcOa-Tv1c_wVs,1600
36
- rclone_api/detail/copy_file_parts.py,sha256=dpqZ0d7l195dZg6Vob2Ty43Uah1v0ozQu5kMtblGqYc,16175
36
+ rclone_api/detail/copy_file_parts.py,sha256=1h-5JJmZdB0_TuVcuYMIClHqAgCXUI4eLyZHbdRiCHg,16280
37
37
  rclone_api/detail/walk.py,sha256=-54NVE8EJcCstwDoaC_UtHm73R2HrZwVwQmsnv55xNU,3369
38
38
  rclone_api/experimental/flags.py,sha256=qCVD--fSTmzlk9hloRLr0q9elzAOFzPsvVpKM3aB1Mk,2739
39
39
  rclone_api/experimental/flags_base.py,sha256=ajU_czkTcAxXYU-SlmiCfHY7aCQGHvpCLqJ-Z8uZLk0,2102
@@ -41,16 +41,17 @@ rclone_api/s3/api.py,sha256=owoQ1H-R0hXcUozxC6sl53D7NmMOewHk2pUxK-ye8ms,4061
41
41
  rclone_api/s3/basic_ops.py,sha256=hK3366xhVEzEcjz9Gk_8lFx6MRceAk72cax6mUrr6ko,2104
42
42
  rclone_api/s3/chunk_task.py,sha256=waEYe-iYQ1_BR3NCS4BrzVrK9UANvH1EcbXx2I6Z_NM,6839
43
43
  rclone_api/s3/create.py,sha256=_Q-faQ4Zl8XKTB28gireRxVXWP-YNxoAK4bligxDtiI,3998
44
- rclone_api/s3/s3_multipart_uploader_by_copy.py,sha256=JjMSG9XHiBlKnihPKw43SBYsh-NBqg5FZ4rnGU7j48s,8703
44
+ rclone_api/s3/merge_state.py,sha256=ziTB9CYV-OWaky5C1fOT9hifSY2zgUrk5HmX1Xeu2UA,4978
45
+ rclone_api/s3/s3_multipart_uploader_by_copy.py,sha256=iuxnRaRDHvIMCu-9YDZzirRMc1R4gZgjigkM5u_MFJg,17188
45
46
  rclone_api/s3/types.py,sha256=cYI5MbXRNdT-ps5kGIRQaYrseHyx_ozT4AcwBABTKwk,1616
46
47
  rclone_api/s3/upload_file_multipart.py,sha256=V7syKjFyVIe4U9Ahl5XgqVTzt9akiew3MFjGmufLo2w,12503
47
48
  rclone_api/s3/multipart/file_info.py,sha256=8v_07_eADo0K-Nsv7F0Ac1wcv3lkIsrR3MaRCmkYLTQ,105
48
- rclone_api/s3/multipart/finished_piece.py,sha256=9nMWnVZ8S99wi2VFQsm1h1ZHqmebkhMGgd2s56wNj9w,1331
49
+ rclone_api/s3/multipart/finished_piece.py,sha256=LtlX_mm6_hsADR8FxgfC2_pcO5Wou_20-jE34IcRXew,1633
49
50
  rclone_api/s3/multipart/upload_info.py,sha256=d6_OfzFR_vtDzCEegFfzCfWi2kUBUV4aXZzqAEVp1c4,1874
50
51
  rclone_api/s3/multipart/upload_state.py,sha256=f-Aq2NqtAaMUMhYitlICSNIxCKurWAl2gDEUVizLIqw,6019
51
- rclone_api-1.4.15.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
52
- rclone_api-1.4.15.dist-info/METADATA,sha256=yC0hOJ09OMTVwf98k6NEfy7-gmlwu4IqWmWdnfUP67Q,4628
53
- rclone_api-1.4.15.dist-info/WHEEL,sha256=rF4EZyR2XVS6irmOHQIJx2SUqXLZKRMUrjsg8UwN-XQ,109
54
- rclone_api-1.4.15.dist-info/entry_points.txt,sha256=fJteOlYVwgX3UbNuL9jJ0zUTuX2O79JFAeNgK7Sw7EQ,255
55
- rclone_api-1.4.15.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
56
- rclone_api-1.4.15.dist-info/RECORD,,
52
+ rclone_api-1.4.19.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
53
+ rclone_api-1.4.19.dist-info/METADATA,sha256=RpqVhvMVSL_isjprjoRJJWMStau5WxyiiyO1OkHBydo,4628
54
+ rclone_api-1.4.19.dist-info/WHEEL,sha256=rF4EZyR2XVS6irmOHQIJx2SUqXLZKRMUrjsg8UwN-XQ,109
55
+ rclone_api-1.4.19.dist-info/entry_points.txt,sha256=fJteOlYVwgX3UbNuL9jJ0zUTuX2O79JFAeNgK7Sw7EQ,255
56
+ rclone_api-1.4.19.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
57
+ rclone_api-1.4.19.dist-info/RECORD,,