rclone-api 1.3.1__tar.gz → 1.3.3__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 (88) hide show
  1. {rclone_api-1.3.1 → rclone_api-1.3.3}/PKG-INFO +1 -1
  2. {rclone_api-1.3.1 → rclone_api-1.3.3}/pyproject.toml +1 -1
  3. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/profile/mount_copy_bytes.py +14 -6
  4. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/rclone.py +15 -79
  5. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/s3/chunk_task.py +0 -19
  6. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api.egg-info/PKG-INFO +1 -1
  7. {rclone_api-1.3.1 → rclone_api-1.3.3}/tests/test_copy_bytes.py +2 -2
  8. {rclone_api-1.3.1 → rclone_api-1.3.3}/.aiderignore +0 -0
  9. {rclone_api-1.3.1 → rclone_api-1.3.3}/.github/workflows/lint.yml +0 -0
  10. {rclone_api-1.3.1 → rclone_api-1.3.3}/.github/workflows/push_macos.yml +0 -0
  11. {rclone_api-1.3.1 → rclone_api-1.3.3}/.github/workflows/push_ubuntu.yml +0 -0
  12. {rclone_api-1.3.1 → rclone_api-1.3.3}/.github/workflows/push_win.yml +0 -0
  13. {rclone_api-1.3.1 → rclone_api-1.3.3}/.gitignore +0 -0
  14. {rclone_api-1.3.1 → rclone_api-1.3.3}/.pylintrc +0 -0
  15. {rclone_api-1.3.1 → rclone_api-1.3.3}/.vscode/launch.json +0 -0
  16. {rclone_api-1.3.1 → rclone_api-1.3.3}/.vscode/settings.json +0 -0
  17. {rclone_api-1.3.1 → rclone_api-1.3.3}/.vscode/tasks.json +0 -0
  18. {rclone_api-1.3.1 → rclone_api-1.3.3}/LICENSE +0 -0
  19. {rclone_api-1.3.1 → rclone_api-1.3.3}/MANIFEST.in +0 -0
  20. {rclone_api-1.3.1 → rclone_api-1.3.3}/README.md +0 -0
  21. {rclone_api-1.3.1 → rclone_api-1.3.3}/clean +0 -0
  22. {rclone_api-1.3.1 → rclone_api-1.3.3}/install +0 -0
  23. {rclone_api-1.3.1 → rclone_api-1.3.3}/lint +0 -0
  24. {rclone_api-1.3.1 → rclone_api-1.3.3}/requirements.testing.txt +0 -0
  25. {rclone_api-1.3.1 → rclone_api-1.3.3}/setup.cfg +0 -0
  26. {rclone_api-1.3.1 → rclone_api-1.3.3}/setup.py +0 -0
  27. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/__init__.py +0 -0
  28. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/assets/example.txt +0 -0
  29. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/cli.py +0 -0
  30. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/cmd/copy_large_s3.py +0 -0
  31. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/cmd/list_files.py +0 -0
  32. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/completed_process.py +0 -0
  33. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/config.py +0 -0
  34. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/convert.py +0 -0
  35. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/deprecated.py +0 -0
  36. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/diff.py +0 -0
  37. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/dir.py +0 -0
  38. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/dir_listing.py +0 -0
  39. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/exec.py +0 -0
  40. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/experimental/flags.py +0 -0
  41. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/experimental/flags_base.py +0 -0
  42. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/file.py +0 -0
  43. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/filelist.py +0 -0
  44. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/group_files.py +0 -0
  45. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/log.py +0 -0
  46. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/mount.py +0 -0
  47. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/mount_read_chunker.py +0 -0
  48. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/process.py +0 -0
  49. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/remote.py +0 -0
  50. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/rpath.py +0 -0
  51. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/s3/api.py +0 -0
  52. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/s3/basic_ops.py +0 -0
  53. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/s3/chunk_types.py +0 -0
  54. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/s3/create.py +0 -0
  55. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/s3/types.py +0 -0
  56. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/s3/upload_file_multipart.py +0 -0
  57. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/scan_missing_folders.py +0 -0
  58. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/types.py +0 -0
  59. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/util.py +0 -0
  60. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api/walk.py +0 -0
  61. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api.egg-info/SOURCES.txt +0 -0
  62. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api.egg-info/dependency_links.txt +0 -0
  63. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api.egg-info/entry_points.txt +0 -0
  64. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api.egg-info/requires.txt +0 -0
  65. {rclone_api-1.3.1 → rclone_api-1.3.3}/src/rclone_api.egg-info/top_level.txt +0 -0
  66. {rclone_api-1.3.1 → rclone_api-1.3.3}/test +0 -0
  67. {rclone_api-1.3.1 → rclone_api-1.3.3}/tests/archive/test_paramiko.py.disabled +0 -0
  68. {rclone_api-1.3.1 → rclone_api-1.3.3}/tests/test_cmd_list_files.py +0 -0
  69. {rclone_api-1.3.1 → rclone_api-1.3.3}/tests/test_copy.py +0 -0
  70. {rclone_api-1.3.1 → rclone_api-1.3.3}/tests/test_copy_file_resumable_s3.py +0 -0
  71. {rclone_api-1.3.1 → rclone_api-1.3.3}/tests/test_copy_files.py +0 -0
  72. {rclone_api-1.3.1 → rclone_api-1.3.3}/tests/test_diff.py +0 -0
  73. {rclone_api-1.3.1 → rclone_api-1.3.3}/tests/test_group_files.py +0 -0
  74. {rclone_api-1.3.1 → rclone_api-1.3.3}/tests/test_is_synced.py +0 -0
  75. {rclone_api-1.3.1 → rclone_api-1.3.3}/tests/test_ls.py +0 -0
  76. {rclone_api-1.3.1 → rclone_api-1.3.3}/tests/test_mount.py +0 -0
  77. {rclone_api-1.3.1 → rclone_api-1.3.3}/tests/test_mount_s3.py +0 -0
  78. {rclone_api-1.3.1 → rclone_api-1.3.3}/tests/test_obscure.py +0 -0
  79. {rclone_api-1.3.1 → rclone_api-1.3.3}/tests/test_rclone_config.py +0 -0
  80. {rclone_api-1.3.1 → rclone_api-1.3.3}/tests/test_remote_control.py +0 -0
  81. {rclone_api-1.3.1 → rclone_api-1.3.3}/tests/test_remotes.py +0 -0
  82. {rclone_api-1.3.1 → rclone_api-1.3.3}/tests/test_s3.py +0 -0
  83. {rclone_api-1.3.1 → rclone_api-1.3.3}/tests/test_scan_missing_folders.py +0 -0
  84. {rclone_api-1.3.1 → rclone_api-1.3.3}/tests/test_size_files.py +0 -0
  85. {rclone_api-1.3.1 → rclone_api-1.3.3}/tests/test_size_suffix.py +0 -0
  86. {rclone_api-1.3.1 → rclone_api-1.3.3}/tests/test_walk.py +0 -0
  87. {rclone_api-1.3.1 → rclone_api-1.3.3}/tox.ini +0 -0
  88. {rclone_api-1.3.1 → rclone_api-1.3.3}/upload_package.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.3.1
3
+ Version: 1.3.3
4
4
  Summary: rclone api in python
5
5
  Home-page: https://github.com/zackees/rclone-api
6
6
  License: BSD 3-Clause License
@@ -23,7 +23,7 @@ dependencies = [
23
23
  ]
24
24
 
25
25
  # Change this with the version number bump.
26
- version = "1.3.1"
26
+ version = "1.3.3"
27
27
 
28
28
  [tool.setuptools]
29
29
  package-dir = {"" = "src"}
@@ -137,11 +137,11 @@ def _run_profile(
137
137
  future = filechunker.fetch(offset.as_int(), size.as_int(), "TEST OBJECT")
138
138
  futures.append(future)
139
139
 
140
+ # dry run to warm up the mounts, then read a different byte range.
140
141
  for future in futures:
141
142
  filepart_or_err = future.result()
142
143
  if isinstance(filepart_or_err, Exception):
143
144
  assert False, f"Error: {filepart_or_err}"
144
- bytes_count += filepart_or_err.n_bytes()
145
145
  filepart_or_err.close()
146
146
  futures.clear()
147
147
 
@@ -164,9 +164,17 @@ def _run_profile(
164
164
  diff = (time.time() - start) / num
165
165
  net_io_end = psutil.net_io_counters()
166
166
  # self.assertEqual(len(bytes_or_err), size)
167
- assert (
168
- bytes_count == SizeSuffix(size * num).as_int()
169
- ), f"Length: {SizeSuffix(bytes_count)} != {SizeSuffix(size* num)}"
167
+ # assert (
168
+ # bytes_count == SizeSuffix(size * num).as_int()
169
+ # ), f"Length: {SizeSuffix(bytes_count)} != {SizeSuffix(size* num)}"
170
+
171
+ if bytes_count != SizeSuffix(size * num).as_int():
172
+ print("\n######################## ERROR ########################")
173
+ print(f"Error: Length: {SizeSuffix(bytes_count)} != {SizeSuffix(size* num)}")
174
+ print(f" Bytes count: {bytes_count}")
175
+ print(f" Size: {SizeSuffix(size * num)}")
176
+ print(f" num: {num}")
177
+ print("########################################################\n")
170
178
 
171
179
  # print io stats
172
180
  bytes_sent = (net_io_end.bytes_sent - net_io_start.bytes_sent) // num
@@ -203,11 +211,11 @@ def test_profile_copy_bytes(
203
211
  sizes = [size.as_int()]
204
212
  else:
205
213
  sizes = [
206
- # 1024 * 1024 * 1,
214
+ 1024 * 1024 * 1,
207
215
  # 1024 * 1024 * 2,
208
216
  # 1024 * 1024 * 4,
209
217
  # 1024 * 1024 * 8,
210
- 1024 * 1024 * 16,
218
+ # 1024 * 1024 * 16,
211
219
  # 1024 * 1024 * 32,
212
220
  1024 * 1024 * 64,
213
221
  1024 * 1024 * 128,
@@ -682,7 +682,7 @@ class Rclone:
682
682
  save_state_json: Path,
683
683
  chunk_size: SizeSuffix | None = None,
684
684
  read_threads: int = 8,
685
- write_threads: int = 16,
685
+ write_threads: int = 8,
686
686
  retries: int = 3,
687
687
  verbose: bool | None = None,
688
688
  max_chunks_before_suspension: int | None = None,
@@ -829,74 +829,6 @@ class Rclone:
829
829
  finally:
830
830
  chunk_fetcher.shutdown()
831
831
 
832
- def _copy_bytes(
833
- self,
834
- src: str,
835
- offset: int,
836
- length: int,
837
- transfers: int = 1, # Note, increasing transfers can result in devestating drop in performance.
838
- # If outfile is supplied then bytes are written to this file and success returns bytes(0)
839
- outfile: Path | None = None,
840
- mount_log: Path | None = None,
841
- direct_io: bool = True,
842
- ) -> bytes | Exception:
843
- """Copy bytes from a file to another file."""
844
- from rclone_api.util import random_str
845
-
846
- tmp_mnts = Path("tmp_mnts") / random_str(12)
847
- src_parent_path = Path(src).parent.as_posix()
848
- src_file = Path(src).name
849
- other_args: list[str] = [
850
- "--no-modtime",
851
- # "--vfs-read-wait", "1s"
852
- ]
853
- vfs_read_chunk_size = SizeSuffix(length // transfers)
854
- vfs_read_chunk_size_limit = SizeSuffix(length)
855
- vfs_read_chunk_streams = transfers
856
- vfs_disk_space_total_size = SizeSuffix(length)
857
- # --max-read-ahead SizeSuffix
858
- max_read_ahead = SizeSuffix(vfs_read_chunk_size.as_int())
859
-
860
- # other_args += ["--vfs-read-chunk-size", str(vfs_read_chunk_size)]
861
- other_args += ["--vfs-read-chunk-size", str(vfs_read_chunk_size)]
862
- other_args += ["--vfs-read-chunk-size-limit", str(vfs_read_chunk_size_limit)]
863
- other_args += ["--vfs-read-chunk-streams", str(vfs_read_chunk_streams)]
864
- other_args += ["--vfs-disk-space-total-size", str(vfs_disk_space_total_size)]
865
- other_args += ["--max-read-ahead", str(max_read_ahead)]
866
- other_args += ["--read-only"]
867
- if direct_io:
868
- other_args += ["--direct-io"]
869
-
870
- with TemporaryDirectory() as tmpdir:
871
- cache_dir = Path(tmpdir) / "cache"
872
- other_args += ["--cache-dir", str(cache_dir.absolute())]
873
- try:
874
- # use scoped mount to do the read, then write the bytes to the destination
875
- with self.scoped_mount(
876
- src_parent_path,
877
- tmp_mnts,
878
- use_links=True,
879
- verbose=mount_log is not None,
880
- vfs_cache_mode="minimal",
881
- other_args=other_args,
882
- log=mount_log,
883
- cache_dir=cache_dir,
884
- cache_dir_delete_on_exit=True,
885
- ):
886
- src_file_mnt = tmp_mnts / src_file
887
- with open(src_file_mnt, "rb") as f:
888
- f.seek(offset)
889
- data = f.read(length)
890
- if outfile is None:
891
- return data
892
- with open(outfile, "wb") as out:
893
- out.write(data)
894
- del data
895
- return bytes(0)
896
-
897
- except Exception as e:
898
- return e
899
-
900
832
  def get_multi_mount_file_chunker(
901
833
  self,
902
834
  src: str,
@@ -928,16 +860,19 @@ class Rclone:
928
860
  if direct_io:
929
861
  other_args += ["--direct-io"]
930
862
 
863
+ base_mount_dir = Path("tmp_mnts")
864
+ base_cache_dir = Path("cache")
865
+
931
866
  filename = Path(src).name
932
867
  with ThreadPoolExecutor(max_workers=threads) as executor:
933
868
  futures: list[Future] = []
934
869
  try:
935
870
  for i in range(threads):
936
- tmp_mnts = Path("tmp_mnts") / random_str(12)
871
+ tmp_mnts = base_mount_dir / random_str(12)
937
872
  verbose = mount_log is not None
938
873
 
939
874
  src_parent_path = Path(src).parent.as_posix()
940
- cache_dir = Path("cache") / random_str(12)
875
+ cache_dir = base_cache_dir / random_str(12)
941
876
 
942
877
  def task(
943
878
  src_parent_path=src_parent_path,
@@ -992,7 +927,7 @@ class Rclone:
992
927
  )
993
928
  return filechunker
994
929
 
995
- def copy_bytes(
930
+ def copy_bytes_multimount(
996
931
  self,
997
932
  src: str,
998
933
  offset: int,
@@ -1004,7 +939,7 @@ class Rclone:
1004
939
  mount_log: Path | None = None,
1005
940
  direct_io: bool = True,
1006
941
  ) -> bytes | Exception:
1007
- """Copy bytes from a file to another file."""
942
+ """Copy a slice of bytes from the src file to dst. Parallelism is achieved through multiple mounted files."""
1008
943
  from rclone_api.types import FilePart
1009
944
 
1010
945
  # determine number of threads from chunk size
@@ -1022,12 +957,13 @@ class Rclone:
1022
957
  payload = fp.payload
1023
958
  if isinstance(payload, Exception):
1024
959
  return payload
1025
- if outfile is None:
1026
- out = payload.read_bytes()
1027
- payload.unlink()
1028
- return out
1029
- shutil.move(payload, outfile)
1030
- return bytes(0)
960
+ try:
961
+ if outfile is None:
962
+ return payload.read_bytes()
963
+ shutil.move(payload, outfile)
964
+ return bytes(0)
965
+ finally:
966
+ fp.close()
1031
967
 
1032
968
  except Exception as e:
1033
969
  warnings.warn(f"Error copying bytes: {e}")
@@ -132,11 +132,6 @@ def file_chunker(
132
132
  upload_info = upload_state.upload_info
133
133
  file_path = upload_info.src_file_path
134
134
  chunk_size = upload_info.chunk_size
135
- # src = Path(file_path)
136
-
137
- # for p in upload_state.parts:
138
- # if not isinstance(p, EndOfStream):
139
- # part_tracker.add_done_part_number(p.part_number)
140
135
 
141
136
  done_part_numbers: set[int] = {
142
137
  p.part_number for p in upload_state.parts if not isinstance(p, EndOfStream)
@@ -173,9 +168,6 @@ def file_chunker(
173
168
 
174
169
  assert offset < file_size, f"Offset {offset} is greater than file size"
175
170
  fetch_size = max(0, min(chunk_size, file_size - offset))
176
-
177
- # assert fetch_size > 0, f"Invalid fetch size: {fetch_size}"
178
-
179
171
  if fetch_size == 0:
180
172
  logger.error(
181
173
  f"Empty data for part {curr_part_number} of {file_path}, is this the last chunk?"
@@ -186,17 +178,7 @@ def file_chunker(
186
178
  f"This should have been the last part, but it is not: {final_part_number} != {curr_part_number}"
187
179
  )
188
180
 
189
- # Open the file, seek, read the chunk, and close immediately.
190
- # with open(file_path, "rb") as f:
191
- # f.seek(offset)
192
- # data = f.read(chunk_size)
193
-
194
- # data = chunk_fetcher(offset, chunk_size).result()
195
-
196
181
  assert curr_part_number is not None
197
- # cpn: int = curr_part_number
198
-
199
- # offset = (curr_part_number - 1) * chunk_size
200
182
  logger.info(
201
183
  f"Reading chunk {curr_part_number} of {num_parts} for {file_path}"
202
184
  )
@@ -208,7 +190,6 @@ def file_chunker(
208
190
  while queue_upload.full():
209
191
  time.sleep(0.1)
210
192
  except Exception as e:
211
-
212
193
  logger.error(f"Error reading file: {e}", exc_info=True)
213
194
  finally:
214
195
  logger.info(f"Finishing FILE CHUNKER for {file_path} and adding EndOfStream")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.3.1
3
+ Version: 1.3.3
4
4
  Summary: rclone api in python
5
5
  Home-page: https://github.com/zackees/rclone-api
6
6
  License: BSD 3-Clause License
@@ -72,7 +72,7 @@ class RcloneCopyBytesTester(unittest.TestCase):
72
72
 
73
73
  def test_copy_bytes(self) -> None:
74
74
  rclone = Rclone(_generate_rclone_config())
75
- bytes_or_err: bytes | Exception = rclone.copy_bytes(
75
+ bytes_or_err: bytes | Exception = rclone.copy_bytes_multimount(
76
76
  src="dst:rclone-api-unit-test/zachs_video/breaking_ai_mind.mp4",
77
77
  offset=0,
78
78
  length=1024 * 1024,
@@ -92,7 +92,7 @@ class RcloneCopyBytesTester(unittest.TestCase):
92
92
  tmp = Path(tmpdir) / "tmp.mp4"
93
93
  log = Path(tmpdir) / "log.txt"
94
94
  rclone = Rclone(_generate_rclone_config())
95
- bytes_or_err: bytes | Exception = rclone.copy_bytes(
95
+ bytes_or_err: bytes | Exception = rclone.copy_bytes_multimount(
96
96
  src="dst:rclone-api-unit-test/zachs_video/breaking_ai_mind.mp4",
97
97
  offset=0,
98
98
  length=1024 * 1024,
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