rclone-api 1.1.14__tar.gz → 1.1.16__tar.gz
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-1.1.14 → rclone_api-1.1.16}/PKG-INFO +1 -1
- {rclone_api-1.1.14 → rclone_api-1.1.16}/pyproject.toml +1 -1
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/process.py +50 -34
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/rclone.py +3 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api.egg-info/PKG-INFO +1 -1
- {rclone_api-1.1.14 → rclone_api-1.1.16}/.aiderignore +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/.github/workflows/lint.yml +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/.github/workflows/push_macos.yml +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/.github/workflows/push_ubuntu.yml +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/.github/workflows/push_win.yml +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/.gitignore +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/.pylintrc +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/.vscode/launch.json +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/.vscode/settings.json +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/.vscode/tasks.json +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/LICENSE +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/MANIFEST.in +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/README.md +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/clean +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/install +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/lint +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/requirements.testing.txt +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/setup.cfg +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/setup.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/__init__.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/assets/example.txt +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/cli.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/cmd/copy_large_s3.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/cmd/list_files.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/completed_process.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/config.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/convert.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/deprecated.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/diff.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/dir.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/dir_listing.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/exec.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/experimental/flags.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/experimental/flags_base.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/file.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/filelist.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/group_files.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/mount.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/remote.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/rpath.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/s3/api.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/s3/basic_ops.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/s3/chunk_file.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/s3/chunk_types.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/s3/chunk_uploader.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/s3/create.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/s3/types.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/scan_missing_folders.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/types.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/util.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api/walk.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api.egg-info/SOURCES.txt +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api.egg-info/dependency_links.txt +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api.egg-info/entry_points.txt +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api.egg-info/requires.txt +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/src/rclone_api.egg-info/top_level.txt +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/test +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/tests/archive/test_paramiko.py.disabled +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/tests/test_cmd_list_files.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/tests/test_copy.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/tests/test_copy_files.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/tests/test_diff.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/tests/test_group_files.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/tests/test_is_synced.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/tests/test_ls.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/tests/test_mount.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/tests/test_mount_s3.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/tests/test_mounted_ranged_download.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/tests/test_obscure.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/tests/test_rclone_config.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/tests/test_remote_control.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/tests/test_remotes.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/tests/test_s3.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/tests/test_scan_missing_folders.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/tests/test_size_files.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/tests/test_size_suffix.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/tests/test_walk.py +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/tox.ini +0 -0
- {rclone_api-1.1.14 → rclone_api-1.1.16}/upload_package.sh +0 -0
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import atexit
|
|
1
2
|
import os
|
|
2
3
|
import subprocess
|
|
4
|
+
import threading
|
|
5
|
+
import time
|
|
6
|
+
import weakref
|
|
3
7
|
from dataclasses import dataclass
|
|
4
8
|
from pathlib import Path
|
|
5
9
|
from tempfile import TemporaryDirectory
|
|
@@ -8,44 +12,10 @@ from typing import Any
|
|
|
8
12
|
from rclone_api.config import Config
|
|
9
13
|
from rclone_api.util import get_verbose
|
|
10
14
|
|
|
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
15
|
|
|
45
16
|
def _get_verbose(verbose: bool | None) -> bool:
|
|
46
17
|
if verbose is not None:
|
|
47
18
|
return verbose
|
|
48
|
-
# get it from the environment
|
|
49
19
|
return bool(int(os.getenv("RCLONE_API_VERBOSE", "0")))
|
|
50
20
|
|
|
51
21
|
|
|
@@ -93,6 +63,17 @@ class Process:
|
|
|
93
63
|
|
|
94
64
|
self.process = subprocess.Popen(self.cmd, **kwargs) # type: ignore
|
|
95
65
|
|
|
66
|
+
# Register an atexit callback using a weak reference to avoid
|
|
67
|
+
# keeping the Process instance alive solely due to the callback.
|
|
68
|
+
self_ref = weakref.ref(self)
|
|
69
|
+
|
|
70
|
+
def exit_cleanup():
|
|
71
|
+
obj = self_ref()
|
|
72
|
+
if obj is not None:
|
|
73
|
+
obj._atexit_terminate()
|
|
74
|
+
|
|
75
|
+
atexit.register(exit_cleanup)
|
|
76
|
+
|
|
96
77
|
def cleanup(self) -> None:
|
|
97
78
|
if self.tempdir and self.needs_cleanup:
|
|
98
79
|
try:
|
|
@@ -100,6 +81,41 @@ class Process:
|
|
|
100
81
|
except Exception as e:
|
|
101
82
|
print(f"Error cleaning up tempdir: {e}")
|
|
102
83
|
|
|
84
|
+
def _atexit_terminate(self) -> None:
|
|
85
|
+
"""
|
|
86
|
+
Registered via atexit, this method attempts to gracefully terminate the process.
|
|
87
|
+
If the process does not exit within a short timeout, it is aggressively killed.
|
|
88
|
+
"""
|
|
89
|
+
if self.process.poll() is None: # Process is still running
|
|
90
|
+
|
|
91
|
+
def terminate_sequence():
|
|
92
|
+
try:
|
|
93
|
+
# Try to terminate gracefully.
|
|
94
|
+
self.process.terminate()
|
|
95
|
+
except Exception as e:
|
|
96
|
+
print(f"Error calling terminate on process {self.process.pid}: {e}")
|
|
97
|
+
# Allow time for graceful shutdown.
|
|
98
|
+
timeout = 2 # seconds
|
|
99
|
+
start = time.time()
|
|
100
|
+
while self.process.poll() is None and (time.time() - start) < timeout:
|
|
101
|
+
time.sleep(0.1)
|
|
102
|
+
# If still running, kill aggressively.
|
|
103
|
+
if self.process.poll() is None:
|
|
104
|
+
try:
|
|
105
|
+
self.process.kill()
|
|
106
|
+
except Exception as e:
|
|
107
|
+
print(f"Error calling kill on process {self.process.pid}: {e}")
|
|
108
|
+
# Optionally wait briefly for termination.
|
|
109
|
+
try:
|
|
110
|
+
self.process.wait(timeout=1)
|
|
111
|
+
except Exception:
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
# Run the termination sequence in a separate daemon thread.
|
|
115
|
+
t = threading.Thread(target=terminate_sequence, daemon=True)
|
|
116
|
+
t.start()
|
|
117
|
+
t.join(timeout=3)
|
|
118
|
+
|
|
103
119
|
@property
|
|
104
120
|
def pid(self) -> int:
|
|
105
121
|
return self.process.pid
|
|
@@ -711,6 +711,9 @@ class Rclone:
|
|
|
711
711
|
vfs_disk_space_total_size.as_str(),
|
|
712
712
|
]
|
|
713
713
|
other_args += ["--read-only"]
|
|
714
|
+
other_args += ["--direct-io"]
|
|
715
|
+
# --vfs-cache-max-size
|
|
716
|
+
other_args += ["--vfs-cache-max-size", vfs_disk_space_total_size.as_str()]
|
|
714
717
|
mount_path = mount_path or Path("tmp_mnts") / random_str(12)
|
|
715
718
|
src_path = Path(src)
|
|
716
719
|
name = src_path.name
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|