rclone-api 1.5.6__py2.py3-none-any.whl → 1.5.7__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/mount.py CHANGED
@@ -1,49 +1,8 @@
1
- import atexit
2
- import os
3
- import platform
4
- import shutil
5
- import subprocess
6
- import time
7
- import warnings
8
- import weakref
9
- from concurrent.futures import ThreadPoolExecutor
10
1
  from dataclasses import dataclass
11
2
  from pathlib import Path
12
- from typing import Any
13
3
 
14
4
  from rclone_api.process import Process
15
5
 
16
- _SYSTEM = platform.system() # "Linux", "Darwin", "Windows", etc.
17
-
18
- _MOUNTS_FOR_GC: weakref.WeakSet = weakref.WeakSet()
19
-
20
-
21
- def _add_mount_for_gc(mount: "Mount") -> None:
22
- # weak reference to avoid circular references
23
- _MOUNTS_FOR_GC.add(mount)
24
-
25
-
26
- def _remove_mount_for_gc(mount: "Mount") -> None:
27
- _MOUNTS_FOR_GC.discard(mount)
28
-
29
-
30
- def _cleanup_mounts() -> None:
31
- with ThreadPoolExecutor() as executor:
32
- mount: Mount
33
- for mount in _MOUNTS_FOR_GC:
34
- executor.submit(mount.close)
35
-
36
-
37
- def _cache_dir_delete_on_exit(cache_dir: Path) -> None:
38
- if cache_dir.exists():
39
- try:
40
- shutil.rmtree(cache_dir)
41
- except Exception as e:
42
- warnings.warn(f"Error removing cache directory {cache_dir}: {e}")
43
-
44
-
45
- atexit.register(_cleanup_mounts)
46
-
47
6
 
48
7
  @dataclass
49
8
  class Mount:
@@ -58,21 +17,29 @@ class Mount:
58
17
  _closed: bool = False
59
18
 
60
19
  def __post_init__(self):
20
+ from rclone_api.mount_util import add_mount_for_gc, wait_for_mount
21
+
61
22
  assert isinstance(self.mount_path, Path)
62
23
  assert self.process is not None
63
- wait_for_mount(self.mount_path, self.process)
64
- _add_mount_for_gc(self)
24
+ wait_for_mount(self)
25
+ add_mount_for_gc(self)
65
26
 
66
27
  def close(self, wait=True) -> None:
67
28
  """Clean up the mount."""
29
+ from rclone_api.mount_util import (
30
+ cache_dir_delete_on_exit,
31
+ clean_mount,
32
+ remove_mount_for_gc,
33
+ )
34
+
68
35
  if self._closed:
69
36
  return
70
37
  self._closed = True
71
38
  self.process.terminate()
72
39
  clean_mount(self, verbose=False, wait=wait)
73
40
  if self.cache_dir and self.cache_dir_delete_on_exit:
74
- _cache_dir_delete_on_exit(self.cache_dir)
75
- _remove_mount_for_gc(self)
41
+ cache_dir_delete_on_exit(self.cache_dir)
42
+ remove_mount_for_gc(self)
76
43
 
77
44
  def __enter__(self) -> "Mount":
78
45
  return self
@@ -86,200 +53,3 @@ class Mount:
86
53
  # make this a hashable object
87
54
  def __hash__(self):
88
55
  return hash(self.mount_path)
89
-
90
-
91
- def run_command(cmd: str, verbose: bool) -> int:
92
- """Run a shell command and print its output if verbose is True."""
93
- if verbose:
94
- print(f"Executing: {cmd}")
95
- try:
96
- result = subprocess.run(
97
- cmd, shell=True, capture_output=True, text=True, check=False
98
- )
99
- if result.returncode != 0 and verbose:
100
- print(f"Command failed: {cmd}\nStdErr: {result.stderr.strip()}")
101
- return result.returncode
102
- except Exception as e:
103
- warnings.warn(f"Error running command '{cmd}': {e}")
104
- return -1
105
-
106
-
107
- def prepare_mount(outdir: Path, verbose: bool) -> None:
108
- if _SYSTEM == "Windows":
109
- # Windows -> Must create parent directories only if they don't exist
110
- if verbose:
111
- print(f"Creating parent directories for {outdir}")
112
- outdir.parent.mkdir(parents=True, exist_ok=True)
113
- else:
114
- # Linux -> Must create parent directories and the directory itself
115
- if verbose:
116
- print(f"Creating directories for {outdir}")
117
- outdir.mkdir(parents=True, exist_ok=True)
118
-
119
-
120
- def wait_for_mount(
121
- path: Path,
122
- mount_process: Any,
123
- timeout: int = 20,
124
- post_mount_delay: int = 5,
125
- poll_interval: float = 1.0,
126
- check_mount_flag: bool = False,
127
- ) -> None:
128
- """
129
- Wait for a mount point to become available by checking if the directory exists,
130
- optionally verifying that it is a mount point, and confirming that it contains files.
131
- This function periodically polls for the mount status, ensures the mount process
132
- is still running, and applies an extra delay after detecting content for stabilization.
133
-
134
- Args:
135
- path (Path): The mount point directory to check.
136
- mount_process (Any): A Process instance handling the mount (must be an instance of Process).
137
- timeout (int): Maximum time in seconds to wait for the mount to become available.
138
- post_mount_delay (int): Additional seconds to wait after detecting files.
139
- poll_interval (float): Seconds between each poll iteration.
140
- check_mount_flag (bool): If True, verifies that the path is recognized as a mount point.
141
-
142
- Raises:
143
- subprocess.CalledProcessError: If the mount_process exits unexpectedly.
144
- TimeoutError: If the mount is not available within the timeout period.
145
- TypeError: If mount_process is not an instance of Process.
146
- """
147
-
148
- if not isinstance(mount_process, Process):
149
- raise TypeError("mount_process must be an instance of Process")
150
-
151
- expire_time = time.time() + timeout
152
- last_error = None
153
-
154
- while time.time() < expire_time:
155
- # Check if the mount process has terminated unexpectedly.
156
- rtn = mount_process.poll()
157
- if rtn is not None:
158
- cmd_str = subprocess.list2cmdline(mount_process.cmd)
159
- print(f"Mount process terminated unexpectedly: {cmd_str}")
160
- raise subprocess.CalledProcessError(rtn, cmd_str)
161
-
162
- # Check if the mount path exists.
163
- if path.exists():
164
- # Optionally check if path is a mount point.
165
- if check_mount_flag:
166
- try:
167
- if not os.path.ismount(str(path)):
168
- print(
169
- f"{path} exists but is not recognized as a mount point yet."
170
- )
171
- time.sleep(poll_interval)
172
- continue
173
- except Exception as e:
174
- print(f"Could not verify mount point status for {path}: {e}")
175
-
176
- try:
177
- # Check for at least one entry in the directory.
178
- if any(path.iterdir()):
179
- print(
180
- f"Mount point {path} appears available with files. Waiting {post_mount_delay} seconds for stabilization."
181
- )
182
- time.sleep(post_mount_delay)
183
- return
184
- else:
185
- print(f"Mount point {path} is empty. Waiting for files to appear.")
186
- except Exception as e:
187
- last_error = e
188
- print(f"Error accessing {path}: {e}")
189
- else:
190
- print(f"Mount point {path} does not exist yet.")
191
-
192
- time.sleep(poll_interval)
193
-
194
- raise TimeoutError(
195
- f"Mount point {path} did not become available within {timeout} seconds. Last error: {last_error}"
196
- )
197
-
198
-
199
- def clean_mount(mount: Mount | Path, verbose: bool = False, wait=True) -> None:
200
- """
201
- Clean up a mount path across Linux, macOS, and Windows.
202
-
203
- The function attempts to unmount the mount at mount_path, then, if the
204
- directory is empty, removes it. On Linux it uses 'fusermount -u' (for FUSE mounts)
205
- and 'umount'. On macOS it uses 'umount' (and optionally 'diskutil unmount'),
206
- while on Windows it attempts to remove the mount point via 'mountvol /D'.
207
- """
208
- proc = mount.process if isinstance(mount, Mount) else None
209
- if proc is not None and proc.poll() is None:
210
- if verbose:
211
- print(f"Terminating mount process {proc.pid}")
212
- proc.kill()
213
-
214
- # Check if the mount path exists; if an OSError occurs, assume it exists.
215
- mount_path = mount.mount_path if isinstance(mount, Mount) else mount
216
- try:
217
- mount_exists = mount_path.exists()
218
- except OSError:
219
- # warnings.warn(f"Error checking {mount_path}: {e}")
220
- mount_exists = True
221
-
222
- # Give the system a moment (if unmount is in progress, etc.)
223
- if wait:
224
- time.sleep(2)
225
-
226
- if not mount_exists:
227
- if verbose:
228
- print(f"{mount_path} does not exist; nothing to clean up.")
229
- return
230
-
231
- if verbose:
232
- print(f"{mount_path} still exists, attempting to unmount and remove.")
233
-
234
- # Platform-specific unmount procedures
235
- if _SYSTEM == "Linux":
236
- # Try FUSE unmount first (if applicable), then the regular umount.
237
- run_command(f"fusermount -u {mount_path}", verbose)
238
- run_command(f"umount {mount_path}", verbose)
239
- elif _SYSTEM == "Darwin":
240
- # On macOS, use umount; optionally try diskutil for stubborn mounts.
241
- run_command(f"umount {mount_path}", verbose)
242
- # Optionally: uncomment the next line if diskutil unmount is preferred.
243
- # run_command(f"diskutil unmount {mount_path}", verbose)
244
- elif _SYSTEM == "Windows":
245
- # On Windows, remove the mount point using mountvol.
246
- run_command(f"mountvol {mount_path} /D", verbose)
247
- # If that does not work, try to remove the directory directly.
248
- try:
249
- mount_path.rmdir()
250
- if verbose:
251
- print(f"Successfully removed mount directory {mount_path}")
252
- except Exception:
253
- # warnings.warn(f"Failed to remove mount {mount_path}: {e}")
254
- pass
255
- else:
256
- warnings.warn(f"Unsupported platform: {_SYSTEM}")
257
-
258
- # Allow some time for the unmount commands to take effect.
259
- if wait:
260
- time.sleep(2)
261
-
262
- # Re-check if the mount path still exists.
263
- try:
264
- still_exists = mount_path.exists()
265
- except OSError as e:
266
- warnings.warn(f"Error re-checking {mount_path}: {e}")
267
- still_exists = True
268
-
269
- if still_exists:
270
- if verbose:
271
- print(f"{mount_path} still exists after unmount attempt.")
272
- # Attempt to remove the directory if it is empty.
273
- try:
274
- # Only remove if the directory is empty.
275
- if not any(mount_path.iterdir()):
276
- mount_path.rmdir()
277
- if verbose:
278
- print(f"Removed empty mount directory {mount_path}")
279
- else:
280
- warnings.warn(f"{mount_path} is not empty; cannot remove.")
281
- except Exception as e:
282
- warnings.warn(f"Failed during cleanup of {mount_path}: {e}")
283
- else:
284
- if verbose:
285
- print(f"{mount_path} successfully cleaned up.")
@@ -0,0 +1,245 @@
1
+ import atexit
2
+ import os
3
+ import platform
4
+ import shutil
5
+ import subprocess
6
+ import time
7
+ import warnings
8
+ import weakref
9
+ from concurrent.futures import ThreadPoolExecutor
10
+ from pathlib import Path
11
+
12
+ from rclone_api.mount import Mount
13
+ from rclone_api.process import Process
14
+
15
+ _SYSTEM = platform.system() # "Linux", "Darwin", "Windows", etc.
16
+
17
+ _MOUNTS_FOR_GC: weakref.WeakSet = weakref.WeakSet()
18
+
19
+
20
+ def _cleanup_mounts() -> None:
21
+ with ThreadPoolExecutor() as executor:
22
+ mount: Mount
23
+ for mount in _MOUNTS_FOR_GC:
24
+ executor.submit(mount.close)
25
+
26
+
27
+ def _run_command(cmd: str, verbose: bool) -> int:
28
+ """Run a shell command and print its output if verbose is True."""
29
+ if verbose:
30
+ print(f"Executing: {cmd}")
31
+ try:
32
+ result = subprocess.run(
33
+ cmd, shell=True, capture_output=True, text=True, check=False
34
+ )
35
+ if result.returncode != 0 and verbose:
36
+ print(f"Command failed: {cmd}\nStdErr: {result.stderr.strip()}")
37
+ return result.returncode
38
+ except Exception as e:
39
+ warnings.warn(f"Error running command '{cmd}': {e}")
40
+ return -1
41
+
42
+
43
+ atexit.register(_cleanup_mounts)
44
+
45
+
46
+ def cache_dir_delete_on_exit(cache_dir: Path) -> None:
47
+ if cache_dir.exists():
48
+ try:
49
+ shutil.rmtree(cache_dir)
50
+ except Exception as e:
51
+ warnings.warn(f"Error removing cache directory {cache_dir}: {e}")
52
+
53
+
54
+ def add_mount_for_gc(mount: Mount) -> None:
55
+ # weak reference to avoid circular references
56
+ _MOUNTS_FOR_GC.add(mount)
57
+
58
+
59
+ def remove_mount_for_gc(mount: Mount) -> None:
60
+ _MOUNTS_FOR_GC.discard(mount)
61
+
62
+
63
+ def prepare_mount(outdir: Path, verbose: bool) -> None:
64
+ if _SYSTEM == "Windows":
65
+ # Windows -> Must create parent directories only if they don't exist
66
+ if verbose:
67
+ print(f"Creating parent directories for {outdir}")
68
+ outdir.parent.mkdir(parents=True, exist_ok=True)
69
+ else:
70
+ # Linux -> Must create parent directories and the directory itself
71
+ if verbose:
72
+ print(f"Creating directories for {outdir}")
73
+ outdir.mkdir(parents=True, exist_ok=True)
74
+
75
+
76
+ def wait_for_mount(
77
+ mount: Mount,
78
+ timeout: int = 20,
79
+ post_mount_delay: int = 5,
80
+ poll_interval: float = 1.0,
81
+ check_mount_flag: bool = False,
82
+ ) -> None:
83
+ """
84
+ Wait for a mount point to become available by checking if the directory exists,
85
+ optionally verifying that it is a mount point, and confirming that it contains files.
86
+ This function periodically polls for the mount status, ensures the mount process
87
+ is still running, and applies an extra delay after detecting content for stabilization.
88
+
89
+ Args:
90
+ src (Path): The mount point directory to check.
91
+ mount_process (Any): A Process instance handling the mount (must be an instance of Process).
92
+ timeout (int): Maximum time in seconds to wait for the mount to become available.
93
+ post_mount_delay (int): Additional seconds to wait after detecting files.
94
+ poll_interval (float): Seconds between each poll iteration.
95
+ check_mount_flag (bool): If True, verifies that the path is recognized as a mount point.
96
+
97
+ Raises:
98
+ subprocess.CalledProcessError: If the mount_process exits unexpectedly.
99
+ TimeoutError: If the mount is not available within the timeout period.
100
+ TypeError: If mount_process is not an instance of Process.
101
+ """
102
+
103
+ mount_process = mount.process
104
+ src = mount.mount_path
105
+
106
+ if not isinstance(mount_process, Process):
107
+ raise TypeError("mount_process must be an instance of Process")
108
+
109
+ expire_time = time.time() + timeout
110
+ last_error = None
111
+
112
+ while time.time() < expire_time:
113
+ # Check if the mount process has terminated unexpectedly.
114
+ rtn = mount_process.poll()
115
+ if rtn is not None:
116
+ cmd_str = subprocess.list2cmdline(mount_process.cmd)
117
+ print(f"Mount process terminated unexpectedly: {cmd_str}")
118
+ raise subprocess.CalledProcessError(rtn, cmd_str)
119
+
120
+ # Check if the mount path exists.
121
+ if src.exists():
122
+ # Optionally check if path is a mount point.
123
+ if check_mount_flag:
124
+ try:
125
+ if not os.path.ismount(str(src)):
126
+ print(
127
+ f"{src} exists but is not recognized as a mount point yet."
128
+ )
129
+ time.sleep(poll_interval)
130
+ continue
131
+ except Exception as e:
132
+ print(f"Could not verify mount point status for {src}: {e}")
133
+
134
+ try:
135
+ # Check for at least one entry in the directory.
136
+ if any(src.iterdir()):
137
+ print(
138
+ f"Mount point {src} appears available with files. Waiting {post_mount_delay} seconds for stabilization."
139
+ )
140
+ time.sleep(post_mount_delay)
141
+ return
142
+ else:
143
+ print(f"Mount point {src} is empty. Waiting for files to appear.")
144
+ except Exception as e:
145
+ last_error = e
146
+ print(f"Error accessing {src}: {e}")
147
+ else:
148
+ print(f"Mount point {src} does not exist yet.")
149
+
150
+ time.sleep(poll_interval)
151
+
152
+ # raise TimeoutError(
153
+ # f"Mount point {src} did not become available within {timeout} seconds. Last error: {last_error}"
154
+ # )
155
+ if last_error is not None:
156
+ raise last_error
157
+
158
+
159
+ def clean_mount(mount: Mount | Path, verbose: bool = False, wait=True) -> None:
160
+ """
161
+ Clean up a mount path across Linux, macOS, and Windows.
162
+
163
+ The function attempts to unmount the mount at mount_path, then, if the
164
+ directory is empty, removes it. On Linux it uses 'fusermount -u' (for FUSE mounts)
165
+ and 'umount'. On macOS it uses 'umount' (and optionally 'diskutil unmount'),
166
+ while on Windows it attempts to remove the mount point via 'mountvol /D'.
167
+ """
168
+
169
+ def verbose_print(msg: str):
170
+ if verbose:
171
+ print(msg)
172
+
173
+ proc = mount.process if isinstance(mount, Mount) else None
174
+ if proc is not None and proc.poll() is None:
175
+ verbose_print(f"Terminating mount process {proc.pid}")
176
+ proc.kill()
177
+
178
+ # Check if the mount path exists; if an OSError occurs, assume it exists.
179
+ mount_path = mount.mount_path if isinstance(mount, Mount) else mount
180
+ try:
181
+ mount_exists = mount_path.exists()
182
+ except OSError:
183
+ # warnings.warn(f"Error checking {mount_path}: {e}")
184
+ mount_exists = True
185
+
186
+ # Give the system a moment (if unmount is in progress, etc.)
187
+ if wait:
188
+ time.sleep(2)
189
+
190
+ if not mount_exists:
191
+ verbose_print(f"{mount_path} does not exist; nothing to clean up.")
192
+ return
193
+
194
+ verbose_print(f"{mount_path} still exists, attempting to unmount and remove.")
195
+
196
+ # Platform-specific unmount procedures
197
+ if _SYSTEM == "Linux":
198
+ # Try FUSE unmount first (if applicable), then the regular umount.
199
+ _run_command(f"fusermount -u {mount_path}", verbose)
200
+ _run_command(f"umount {mount_path}", verbose)
201
+ elif _SYSTEM == "Darwin":
202
+ # On macOS, use umount; optionally try diskutil for stubborn mounts.
203
+ _run_command(f"umount {mount_path}", verbose)
204
+ # Optionally: uncomment the next line if diskutil unmount is preferred.
205
+ # _run_command(f"diskutil unmount {mount_path}", verbose)
206
+ elif _SYSTEM == "Windows":
207
+ # On Windows, remove the mount point using mountvol.
208
+ _run_command(f"mountvol {mount_path} /D", verbose)
209
+ # If that does not work, try to remove the directory directly.
210
+ try:
211
+ mount_path.rmdir()
212
+ if verbose:
213
+ print(f"Successfully removed mount directory {mount_path}")
214
+ except Exception:
215
+ # warnings.warn(f"Failed to remove mount {mount_path}: {e}")
216
+ pass
217
+ else:
218
+ warnings.warn(f"Unsupported platform: {_SYSTEM}")
219
+
220
+ # Allow some time for the unmount commands to take effect.
221
+ if wait:
222
+ time.sleep(2)
223
+
224
+ # Re-check if the mount path still exists.
225
+ try:
226
+ still_exists = mount_path.exists()
227
+ except OSError as e:
228
+ warnings.warn(f"Error re-checking {mount_path}: {e}")
229
+ still_exists = True
230
+
231
+ if still_exists:
232
+ verbose_print(f"{mount_path} still exists after unmount attempt.")
233
+ # Attempt to remove the directory if it is empty.
234
+ try:
235
+ # Only remove if the directory is empty.
236
+ if not any(mount_path.iterdir()):
237
+ mount_path.rmdir()
238
+ if verbose:
239
+ verbose_print(f"Removed empty mount directory {mount_path}")
240
+ else:
241
+ warnings.warn(f"{mount_path} is not empty; cannot remove.")
242
+ except Exception as e:
243
+ warnings.warn(f"Failed during cleanup of {mount_path}: {e}")
244
+ else:
245
+ verbose_print(f"{mount_path} successfully cleaned up.")
rclone_api/rclone_impl.py CHANGED
@@ -27,7 +27,7 @@ from rclone_api.file import File
27
27
  from rclone_api.file_stream import FilesStream
28
28
  from rclone_api.group_files import group_files
29
29
  from rclone_api.http_server import HttpServer
30
- from rclone_api.mount import Mount, clean_mount, prepare_mount
30
+ from rclone_api.mount import Mount
31
31
  from rclone_api.process import Process
32
32
  from rclone_api.remote import Remote
33
33
  from rclone_api.rpath import RPath
@@ -1017,6 +1017,7 @@ class RcloneImpl:
1017
1017
  Raises:
1018
1018
  subprocess.CalledProcessError: If the mount operation fails
1019
1019
  """
1020
+ from rclone_api.mount_util import clean_mount, prepare_mount
1020
1021
 
1021
1022
  allow_writes = allow_writes or False
1022
1023
  use_links = use_links or True
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.5.6
3
+ Version: 1.5.7
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,9 +17,10 @@ rclone_api/group_files.py,sha256=H92xPW9lQnbNw5KbtZCl00bD6iRh9yRbCuxku4j_3dg,803
17
17
  rclone_api/http_server.py,sha256=LhovQu2AI-Z7zQIWflWelCiCDLnWzisL32Rs5350kxE,8850
18
18
  rclone_api/install.py,sha256=Xb1BRn8rQcSpSd4dzmvIOELP2zM2DytUeIZ6jzv738A,2893
19
19
  rclone_api/log.py,sha256=VZHM7pNSXip2ZLBKMP7M1u-rp_F7zoafFDuR8CPUoKI,1271
20
- rclone_api/mount.py,sha256=TE_VIBMW7J1UkF_6HRCt8oi_jGdMov4S51bm2OgxFAM,10045
20
+ rclone_api/mount.py,sha256=LZqEhuKZunbWVqmsOIqkkCotaxWJpdFRS1InXveoU5E,1428
21
+ rclone_api/mount_util.py,sha256=jCoObKMNTKO7xG2udAmEXtXdF182rCS5tn56Oc2CEUk,8993
21
22
  rclone_api/process.py,sha256=tGooS5NLdPuqHh7hCH8SfK44A6LGftPQCPQUNgSo0a0,5714
22
- rclone_api/rclone_impl.py,sha256=fhpl149P_dtfxTfwT0hHBaCIe4AJ3BHLXLVsC-QzmPo,46304
23
+ rclone_api/rclone_impl.py,sha256=kr0gvOSuiwxU15rvATYZmSzpWYDiAzjZ2WNe1wqI6NM,46345
23
24
  rclone_api/remote.py,sha256=mTgMTQTwxUmbLjTpr-AGTId2ycXKI9mLX5L7PPpDIoc,520
24
25
  rclone_api/rpath.py,sha256=Y1JjQWcie39EgQrq-UtbfDz5yDLCwwfu27W7AQXllSE,2860
25
26
  rclone_api/scan_missing_folders.py,sha256=-8NCwpCaHeHrX-IepCoAEsX1rl8S-GOCxcIhTr_w3gA,4747
@@ -52,9 +53,9 @@ rclone_api/s3/multipart/upload_parts_inline.py,sha256=V7syKjFyVIe4U9Ahl5XgqVTzt9
52
53
  rclone_api/s3/multipart/upload_parts_resumable.py,sha256=diJoUpVYow6No_dNgOZIYVsv43k4evb6zixqpzWJaUk,9771
53
54
  rclone_api/s3/multipart/upload_parts_server_side_merge.py,sha256=Fp2pdrs5dONQI9LkfNolgAGj1-Z2V1SsRd0r0sreuXI,18040
54
55
  rclone_api/s3/multipart/upload_state.py,sha256=f-Aq2NqtAaMUMhYitlICSNIxCKurWAl2gDEUVizLIqw,6019
55
- rclone_api-1.5.6.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
56
- rclone_api-1.5.6.dist-info/METADATA,sha256=WQndsxBcQyikEIYTaIJ3IH8tzC00wUeW9BTFC3s-vkM,32633
57
- rclone_api-1.5.6.dist-info/WHEEL,sha256=rF4EZyR2XVS6irmOHQIJx2SUqXLZKRMUrjsg8UwN-XQ,109
58
- rclone_api-1.5.6.dist-info/entry_points.txt,sha256=fJteOlYVwgX3UbNuL9jJ0zUTuX2O79JFAeNgK7Sw7EQ,255
59
- rclone_api-1.5.6.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
60
- rclone_api-1.5.6.dist-info/RECORD,,
56
+ rclone_api-1.5.7.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
57
+ rclone_api-1.5.7.dist-info/METADATA,sha256=f2c9hq_CZYIpVr-U9jV44E9FwPg1Tn59XTJf_zhme14,32633
58
+ rclone_api-1.5.7.dist-info/WHEEL,sha256=rF4EZyR2XVS6irmOHQIJx2SUqXLZKRMUrjsg8UwN-XQ,109
59
+ rclone_api-1.5.7.dist-info/entry_points.txt,sha256=fJteOlYVwgX3UbNuL9jJ0zUTuX2O79JFAeNgK7Sw7EQ,255
60
+ rclone_api-1.5.7.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
61
+ rclone_api-1.5.7.dist-info/RECORD,,