rclone-api 1.1.8__tar.gz → 1.1.11__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.
Files changed (85) hide show
  1. {rclone_api-1.1.8 → rclone_api-1.1.11}/PKG-INFO +1 -1
  2. {rclone_api-1.1.8 → rclone_api-1.1.11}/pyproject.toml +1 -1
  3. rclone_api-1.1.11/rclone-mounted-ranged-download.conf.old3393025366 +0 -0
  4. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/cmd/copy_large_s3.py +2 -2
  5. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/mount.py +16 -3
  6. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/rclone.py +6 -4
  7. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/s3/chunk_types.py +14 -0
  8. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/s3/chunk_uploader.py +19 -1
  9. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/types.py +19 -19
  10. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api.egg-info/PKG-INFO +1 -1
  11. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api.egg-info/SOURCES.txt +1 -0
  12. {rclone_api-1.1.8 → rclone_api-1.1.11}/tests/test_size_suffix.py +6 -1
  13. {rclone_api-1.1.8 → rclone_api-1.1.11}/.aiderignore +0 -0
  14. {rclone_api-1.1.8 → rclone_api-1.1.11}/.github/workflows/lint.yml +0 -0
  15. {rclone_api-1.1.8 → rclone_api-1.1.11}/.github/workflows/push_macos.yml +0 -0
  16. {rclone_api-1.1.8 → rclone_api-1.1.11}/.github/workflows/push_ubuntu.yml +0 -0
  17. {rclone_api-1.1.8 → rclone_api-1.1.11}/.github/workflows/push_win.yml +0 -0
  18. {rclone_api-1.1.8 → rclone_api-1.1.11}/.gitignore +0 -0
  19. {rclone_api-1.1.8 → rclone_api-1.1.11}/.pylintrc +0 -0
  20. {rclone_api-1.1.8 → rclone_api-1.1.11}/.vscode/launch.json +0 -0
  21. {rclone_api-1.1.8 → rclone_api-1.1.11}/.vscode/settings.json +0 -0
  22. {rclone_api-1.1.8 → rclone_api-1.1.11}/.vscode/tasks.json +0 -0
  23. {rclone_api-1.1.8 → rclone_api-1.1.11}/LICENSE +0 -0
  24. {rclone_api-1.1.8 → rclone_api-1.1.11}/MANIFEST.in +0 -0
  25. {rclone_api-1.1.8 → rclone_api-1.1.11}/README.md +0 -0
  26. {rclone_api-1.1.8 → rclone_api-1.1.11}/clean +0 -0
  27. {rclone_api-1.1.8 → rclone_api-1.1.11}/install +0 -0
  28. {rclone_api-1.1.8 → rclone_api-1.1.11}/lint +0 -0
  29. {rclone_api-1.1.8 → rclone_api-1.1.11}/requirements.testing.txt +0 -0
  30. {rclone_api-1.1.8 → rclone_api-1.1.11}/setup.cfg +0 -0
  31. {rclone_api-1.1.8 → rclone_api-1.1.11}/setup.py +0 -0
  32. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/__init__.py +0 -0
  33. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/assets/example.txt +0 -0
  34. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/cli.py +0 -0
  35. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/cmd/list_files.py +0 -0
  36. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/completed_process.py +0 -0
  37. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/config.py +0 -0
  38. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/convert.py +0 -0
  39. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/deprecated.py +0 -0
  40. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/diff.py +0 -0
  41. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/dir.py +0 -0
  42. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/dir_listing.py +0 -0
  43. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/exec.py +0 -0
  44. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/experimental/flags.py +0 -0
  45. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/experimental/flags_base.py +0 -0
  46. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/file.py +0 -0
  47. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/filelist.py +0 -0
  48. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/group_files.py +0 -0
  49. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/process.py +0 -0
  50. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/remote.py +0 -0
  51. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/rpath.py +0 -0
  52. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/s3/api.py +0 -0
  53. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/s3/basic_ops.py +0 -0
  54. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/s3/chunk_file.py +0 -0
  55. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/s3/create.py +0 -0
  56. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/s3/types.py +0 -0
  57. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/scan_missing_folders.py +0 -0
  58. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/util.py +0 -0
  59. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api/walk.py +0 -0
  60. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api.egg-info/dependency_links.txt +0 -0
  61. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api.egg-info/entry_points.txt +0 -0
  62. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api.egg-info/requires.txt +0 -0
  63. {rclone_api-1.1.8 → rclone_api-1.1.11}/src/rclone_api.egg-info/top_level.txt +0 -0
  64. {rclone_api-1.1.8 → rclone_api-1.1.11}/test +0 -0
  65. {rclone_api-1.1.8 → rclone_api-1.1.11}/tests/archive/test_paramiko.py.disabled +0 -0
  66. {rclone_api-1.1.8 → rclone_api-1.1.11}/tests/test_cmd_list_files.py +0 -0
  67. {rclone_api-1.1.8 → rclone_api-1.1.11}/tests/test_copy.py +0 -0
  68. {rclone_api-1.1.8 → rclone_api-1.1.11}/tests/test_copy_files.py +0 -0
  69. {rclone_api-1.1.8 → rclone_api-1.1.11}/tests/test_diff.py +0 -0
  70. {rclone_api-1.1.8 → rclone_api-1.1.11}/tests/test_group_files.py +0 -0
  71. {rclone_api-1.1.8 → rclone_api-1.1.11}/tests/test_is_synced.py +0 -0
  72. {rclone_api-1.1.8 → rclone_api-1.1.11}/tests/test_ls.py +0 -0
  73. {rclone_api-1.1.8 → rclone_api-1.1.11}/tests/test_mount.py +0 -0
  74. {rclone_api-1.1.8 → rclone_api-1.1.11}/tests/test_mount_s3.py +0 -0
  75. {rclone_api-1.1.8 → rclone_api-1.1.11}/tests/test_mounted_ranged_download.py +0 -0
  76. {rclone_api-1.1.8 → rclone_api-1.1.11}/tests/test_obscure.py +0 -0
  77. {rclone_api-1.1.8 → rclone_api-1.1.11}/tests/test_rclone_config.py +0 -0
  78. {rclone_api-1.1.8 → rclone_api-1.1.11}/tests/test_remote_control.py +0 -0
  79. {rclone_api-1.1.8 → rclone_api-1.1.11}/tests/test_remotes.py +0 -0
  80. {rclone_api-1.1.8 → rclone_api-1.1.11}/tests/test_s3.py +0 -0
  81. {rclone_api-1.1.8 → rclone_api-1.1.11}/tests/test_scan_missing_folders.py +0 -0
  82. {rclone_api-1.1.8 → rclone_api-1.1.11}/tests/test_size_files.py +0 -0
  83. {rclone_api-1.1.8 → rclone_api-1.1.11}/tests/test_walk.py +0 -0
  84. {rclone_api-1.1.8 → rclone_api-1.1.11}/tox.ini +0 -0
  85. {rclone_api-1.1.8 → rclone_api-1.1.11}/upload_package.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.1.8
3
+ Version: 1.1.11
4
4
  Summary: rclone api in python
5
5
  Home-page: https://github.com/zackees/rclone-api
6
6
  License: BSD 3-Clause License
@@ -21,7 +21,7 @@ dependencies = [
21
21
  ]
22
22
 
23
23
  # Change this with the version number bump.
24
- version = "1.1.8"
24
+ version = "1.1.11"
25
25
 
26
26
  [tool.setuptools]
27
27
  package-dir = {"" = "src"}
@@ -38,13 +38,13 @@ def _parse_args() -> Args:
38
38
  "--chunk-size",
39
39
  help="Chunk size that will be read and uploaded in in SizeSuffix (i.e. 128M = 128 megabytes) form",
40
40
  type=str,
41
- default="512M",
41
+ default="1G",
42
42
  )
43
43
  parser.add_argument(
44
44
  "--threads",
45
45
  help="Number of threads to use per chunk",
46
46
  type=int,
47
- default=16,
47
+ default=64,
48
48
  )
49
49
  parser.add_argument("--retries", help="Number of retries", type=int, default=3)
50
50
  parser.add_argument(
@@ -18,12 +18,23 @@ class Mount:
18
18
 
19
19
  mount_path: Path
20
20
  process: Process
21
+ _closed: bool = False
21
22
 
22
23
  def __post_init__(self):
23
24
  assert isinstance(self.mount_path, Path)
24
25
  assert self.process is not None
25
26
  wait_for_mount(self.mount_path, self.process)
26
27
 
28
+ def close(self, wait=True) -> None:
29
+ """Clean up the mount."""
30
+ if self._closed:
31
+ return
32
+ self._closed = True
33
+ clean_mount(self, verbose=False)
34
+
35
+ def __del__(self):
36
+ self.close(wait=False)
37
+
27
38
 
28
39
  def run_command(cmd: str, verbose: bool) -> int:
29
40
  """Run a shell command and print its output if verbose is True."""
@@ -74,7 +85,7 @@ def wait_for_mount(path: Path, mount_process: Any, timeout: int = 10) -> None:
74
85
  time.sleep(1)
75
86
 
76
87
 
77
- def clean_mount(mount: Mount | Path, verbose: bool = False) -> None:
88
+ def clean_mount(mount: Mount | Path, verbose: bool = False, wait=True) -> None:
78
89
  """
79
90
  Clean up a mount path across Linux, macOS, and Windows.
80
91
 
@@ -98,7 +109,8 @@ def clean_mount(mount: Mount | Path, verbose: bool = False) -> None:
98
109
  mount_exists = True
99
110
 
100
111
  # Give the system a moment (if unmount is in progress, etc.)
101
- time.sleep(2)
112
+ if wait:
113
+ time.sleep(2)
102
114
 
103
115
  if not mount_exists:
104
116
  if verbose:
@@ -132,7 +144,8 @@ def clean_mount(mount: Mount | Path, verbose: bool = False) -> None:
132
144
  warnings.warn(f"Unsupported platform: {_SYSTEM}")
133
145
 
134
146
  # Allow some time for the unmount commands to take effect.
135
- time.sleep(2)
147
+ if wait:
148
+ time.sleep(2)
136
149
 
137
150
  # Re-check if the mount path still exists.
138
151
  try:
@@ -695,6 +695,7 @@ class Rclone:
695
695
  vfs_read_chunk_size = unit_chunk_size
696
696
  vfs_read_chunk_size_limit = chunk_size
697
697
  vfs_read_chunk_streams = threads
698
+ vfs_disk_space_total_size = chunk_size
698
699
  assert (
699
700
  chunk_size.as_int() % vfs_read_chunk_size.as_int() == 0
700
701
  ), f"chunk_size {chunk_size} must be a multiple of vfs_read_chunk_size {vfs_read_chunk_size}"
@@ -704,6 +705,10 @@ class Rclone:
704
705
  vfs_read_chunk_size_limit.as_str(),
705
706
  ]
706
707
  other_args += ["--vfs-read-chunk-streams", str(vfs_read_chunk_streams)]
708
+ other_args += [
709
+ "--vfs-disk-space-total-size",
710
+ vfs_disk_space_total_size.as_str(),
711
+ ]
707
712
  mount_path = mount_path or Path("tmp_mnts") / random_str(12)
708
713
  src_path = Path(src)
709
714
  name = src_path.name
@@ -903,11 +908,8 @@ class Rclone:
903
908
  warnings.warn(f"Error in scoped_mount: {e}\n\nStack Trace:\n{stack_trace}")
904
909
  raise
905
910
  finally:
906
-
907
911
  if not error_happened:
908
- from rclone_api.mount import clean_mount
909
-
910
- clean_mount(mount, verbose=verbose)
912
+ mount.close()
911
913
 
912
914
  # Settings optimized for s3.
913
915
  def mount_s3(
@@ -1,3 +1,4 @@
1
+ import hashlib
1
2
  import json
2
3
  import os
3
4
  import time
@@ -103,6 +104,19 @@ class UploadInfo:
103
104
  return
104
105
  self._total_chunks = self.total_chunks()
105
106
 
107
+ def fingerprint(self) -> str:
108
+ # hash the attributes that are used to identify the upload
109
+ hasher = hashlib.sha256()
110
+ # first is file size
111
+ hasher.update(str(self.file_size).encode("utf-8"))
112
+ # second is the file path
113
+ hasher.update(str(self.src_file_path).encode("utf-8"))
114
+ # next is chunk size
115
+ hasher.update(str(self.chunk_size).encode("utf-8"))
116
+ # next is the number of parts
117
+ hasher.update(str(self._total_chunks).encode("utf-8"))
118
+ return hasher.hexdigest()
119
+
106
120
  def to_json(self) -> dict:
107
121
  json_dict = {}
108
122
  for f in fields(self):
@@ -162,7 +162,25 @@ def upload_file_multipart(
162
162
  return upload_state
163
163
 
164
164
  filechunks: Queue[FileChunk | None] = Queue(10)
165
- upload_state = get_upload_state() or make_new_state()
165
+ new_state = make_new_state()
166
+ loaded_state = get_upload_state()
167
+
168
+ if loaded_state is None:
169
+ upload_state = new_state
170
+ else:
171
+ # if the file size has changed, we cannot resume
172
+ if (
173
+ loaded_state.upload_info.fingerprint()
174
+ != new_state.upload_info.fingerprint()
175
+ ):
176
+ locked_print(
177
+ f"Cannot resume upload: file size changed, starting over for {file_path}"
178
+ )
179
+ _abort_previous_upload(loaded_state)
180
+ upload_state = new_state
181
+ else:
182
+ upload_state = loaded_state
183
+
166
184
  try:
167
185
  upload_state.update_source_file(file_path)
168
186
  except ValueError as e:
@@ -52,33 +52,33 @@ def _to_size_suffix(size: int) -> str:
52
52
  raise ValueError(f"Invalid size: {size}")
53
53
 
54
54
 
55
- _PATTERN_SIZE_SUFFIX = re.compile(r"^(\d+)([A-Za-z]+)$")
55
+ # Update regex to allow decimals (e.g., 16.5MB)
56
+ _PATTERN_SIZE_SUFFIX = re.compile(r"^(\d+(?:\.\d+)?)([A-Za-z]+)$")
56
57
 
57
58
 
58
59
  def _from_size_suffix(size: str) -> int:
59
- # 16MiB
60
- # parse out number and suffix
61
60
  if size == "0":
62
61
  return 0
63
62
  match = _PATTERN_SIZE_SUFFIX.match(size)
64
63
  if match is None:
65
64
  raise ValueError(f"Invalid size suffix: {size}")
66
- size = match.group(1)
67
- suffix = match.group(2)[0:1].upper()
68
- n = int(size)
69
- if suffix == "B":
70
- return n
71
- if suffix == "K":
72
- return n * 1024
73
- if suffix == "M":
74
- return n * 1024 * 1024
75
- if suffix == "G":
76
- return n * 1024 * 1024 * 1024
77
- if suffix == "T":
78
- return n * 1024 * 1024 * 1024 * 1024
79
- if suffix == "P":
80
- return n * 1024 * 1024 * 1024 * 1024 * 1024
81
- raise ValueError(f"Invalid size suffix: {size}")
65
+ num_str, suffix = match.group(1), match.group(2)
66
+ n = float(num_str)
67
+ # Determine the unit from the first letter (e.g., "M" from "MB")
68
+ unit = suffix[0].upper()
69
+ if unit == "B":
70
+ return int(n)
71
+ if unit == "K":
72
+ return int(n * 1024)
73
+ if unit == "M":
74
+ return int(n * 1024 * 1024)
75
+ if unit == "G":
76
+ return int(n * 1024 * 1024 * 1024)
77
+ if unit == "T":
78
+ return int(n * 1024**4)
79
+ if unit == "P":
80
+ return int(n * 1024**5)
81
+ raise ValueError(f"Invalid size suffix: {suffix}")
82
82
 
83
83
 
84
84
  class SizeSuffix:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.1.8
3
+ Version: 1.1.11
4
4
  Summary: rclone api in python
5
5
  Home-page: https://github.com/zackees/rclone-api
6
6
  License: BSD 3-Clause License
@@ -8,6 +8,7 @@ clean
8
8
  install
9
9
  lint
10
10
  pyproject.toml
11
+ rclone-mounted-ranged-download.conf.old3393025366
11
12
  requirements.testing.txt
12
13
  setup.py
13
14
  test
@@ -10,12 +10,17 @@ from rclone_api import SizeSuffix
10
10
  class RcloneSuffixSize(unittest.TestCase):
11
11
  """Test rclone functionality."""
12
12
 
13
- def test_list_remotes(self) -> None:
13
+ def test_simple_suffix(self) -> None:
14
14
  size_suffix = SizeSuffix(1024)
15
15
  size_suffix = SizeSuffix("16MB")
16
16
  size_int = size_suffix.as_int()
17
17
  self.assertEqual(size_int, 16 * 1024 * 1024)
18
18
 
19
+ def test_float_suffix(self) -> None:
20
+ size_suffix = SizeSuffix("16.5MB")
21
+ size_int = size_suffix.as_int()
22
+ self.assertEqual(size_int, int(16.5 * 1024 * 1024))
23
+
19
24
 
20
25
  if __name__ == "__main__":
21
26
  unittest.main()
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