rclone-api 1.1.95__tar.gz → 1.1.97__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 (86) hide show
  1. {rclone_api-1.1.95 → rclone_api-1.1.97}/PKG-INFO +1 -1
  2. {rclone_api-1.1.95 → rclone_api-1.1.97}/pyproject.toml +1 -1
  3. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/profile/mount_copy_bytes.py +3 -17
  4. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/rclone.py +19 -17
  5. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/s3/api.py +5 -1
  6. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/s3/chunk_file.py +52 -24
  7. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/s3/types.py +3 -0
  8. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/s3/upload_file_multipart.py +6 -2
  9. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api.egg-info/PKG-INFO +1 -1
  10. {rclone_api-1.1.95 → rclone_api-1.1.97}/tests/test_copy_file_resumable_s3.py +0 -1
  11. {rclone_api-1.1.95 → rclone_api-1.1.97}/tests/test_s3.py +21 -2
  12. {rclone_api-1.1.95 → rclone_api-1.1.97}/.aiderignore +0 -0
  13. {rclone_api-1.1.95 → rclone_api-1.1.97}/.github/workflows/lint.yml +0 -0
  14. {rclone_api-1.1.95 → rclone_api-1.1.97}/.github/workflows/push_macos.yml +0 -0
  15. {rclone_api-1.1.95 → rclone_api-1.1.97}/.github/workflows/push_ubuntu.yml +0 -0
  16. {rclone_api-1.1.95 → rclone_api-1.1.97}/.github/workflows/push_win.yml +0 -0
  17. {rclone_api-1.1.95 → rclone_api-1.1.97}/.gitignore +0 -0
  18. {rclone_api-1.1.95 → rclone_api-1.1.97}/.pylintrc +0 -0
  19. {rclone_api-1.1.95 → rclone_api-1.1.97}/.vscode/launch.json +0 -0
  20. {rclone_api-1.1.95 → rclone_api-1.1.97}/.vscode/settings.json +0 -0
  21. {rclone_api-1.1.95 → rclone_api-1.1.97}/.vscode/tasks.json +0 -0
  22. {rclone_api-1.1.95 → rclone_api-1.1.97}/LICENSE +0 -0
  23. {rclone_api-1.1.95 → rclone_api-1.1.97}/MANIFEST.in +0 -0
  24. {rclone_api-1.1.95 → rclone_api-1.1.97}/README.md +0 -0
  25. {rclone_api-1.1.95 → rclone_api-1.1.97}/clean +0 -0
  26. {rclone_api-1.1.95 → rclone_api-1.1.97}/install +0 -0
  27. {rclone_api-1.1.95 → rclone_api-1.1.97}/lint +0 -0
  28. {rclone_api-1.1.95 → rclone_api-1.1.97}/requirements.testing.txt +0 -0
  29. {rclone_api-1.1.95 → rclone_api-1.1.97}/setup.cfg +0 -0
  30. {rclone_api-1.1.95 → rclone_api-1.1.97}/setup.py +0 -0
  31. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/__init__.py +0 -0
  32. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/assets/example.txt +0 -0
  33. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/cli.py +0 -0
  34. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/cmd/copy_large_s3.py +0 -0
  35. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/cmd/list_files.py +0 -0
  36. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/completed_process.py +0 -0
  37. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/config.py +0 -0
  38. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/convert.py +0 -0
  39. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/deprecated.py +0 -0
  40. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/diff.py +0 -0
  41. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/dir.py +0 -0
  42. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/dir_listing.py +0 -0
  43. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/exec.py +0 -0
  44. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/experimental/flags.py +0 -0
  45. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/experimental/flags_base.py +0 -0
  46. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/file.py +0 -0
  47. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/filelist.py +0 -0
  48. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/group_files.py +0 -0
  49. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/mount.py +0 -0
  50. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/process.py +0 -0
  51. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/remote.py +0 -0
  52. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/rpath.py +0 -0
  53. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/s3/basic_ops.py +0 -0
  54. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/s3/chunk_types.py +0 -0
  55. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/s3/create.py +0 -0
  56. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/scan_missing_folders.py +0 -0
  57. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/types.py +0 -0
  58. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/util.py +0 -0
  59. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api/walk.py +0 -0
  60. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api.egg-info/SOURCES.txt +0 -0
  61. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api.egg-info/dependency_links.txt +0 -0
  62. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api.egg-info/entry_points.txt +0 -0
  63. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api.egg-info/requires.txt +0 -0
  64. {rclone_api-1.1.95 → rclone_api-1.1.97}/src/rclone_api.egg-info/top_level.txt +0 -0
  65. {rclone_api-1.1.95 → rclone_api-1.1.97}/test +0 -0
  66. {rclone_api-1.1.95 → rclone_api-1.1.97}/tests/archive/test_paramiko.py.disabled +0 -0
  67. {rclone_api-1.1.95 → rclone_api-1.1.97}/tests/test_cmd_list_files.py +0 -0
  68. {rclone_api-1.1.95 → rclone_api-1.1.97}/tests/test_copy.py +0 -0
  69. {rclone_api-1.1.95 → rclone_api-1.1.97}/tests/test_copy_bytes.py +0 -0
  70. {rclone_api-1.1.95 → rclone_api-1.1.97}/tests/test_copy_files.py +0 -0
  71. {rclone_api-1.1.95 → rclone_api-1.1.97}/tests/test_diff.py +0 -0
  72. {rclone_api-1.1.95 → rclone_api-1.1.97}/tests/test_group_files.py +0 -0
  73. {rclone_api-1.1.95 → rclone_api-1.1.97}/tests/test_is_synced.py +0 -0
  74. {rclone_api-1.1.95 → rclone_api-1.1.97}/tests/test_ls.py +0 -0
  75. {rclone_api-1.1.95 → rclone_api-1.1.97}/tests/test_mount.py +0 -0
  76. {rclone_api-1.1.95 → rclone_api-1.1.97}/tests/test_mount_s3.py +0 -0
  77. {rclone_api-1.1.95 → rclone_api-1.1.97}/tests/test_obscure.py +0 -0
  78. {rclone_api-1.1.95 → rclone_api-1.1.97}/tests/test_rclone_config.py +0 -0
  79. {rclone_api-1.1.95 → rclone_api-1.1.97}/tests/test_remote_control.py +0 -0
  80. {rclone_api-1.1.95 → rclone_api-1.1.97}/tests/test_remotes.py +0 -0
  81. {rclone_api-1.1.95 → rclone_api-1.1.97}/tests/test_scan_missing_folders.py +0 -0
  82. {rclone_api-1.1.95 → rclone_api-1.1.97}/tests/test_size_files.py +0 -0
  83. {rclone_api-1.1.95 → rclone_api-1.1.97}/tests/test_size_suffix.py +0 -0
  84. {rclone_api-1.1.95 → rclone_api-1.1.97}/tests/test_walk.py +0 -0
  85. {rclone_api-1.1.95 → rclone_api-1.1.97}/tox.ini +0 -0
  86. {rclone_api-1.1.95 → rclone_api-1.1.97}/upload_package.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.1.95
3
+ Version: 1.1.97
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.1.95"
26
+ version = "1.1.97"
27
27
 
28
28
  [tool.setuptools]
29
29
  package-dir = {"" = "src"}
@@ -142,27 +142,11 @@ def _run_profile(
142
142
  assert False, f"Error: {bytes_or_err}"
143
143
  futures.clear()
144
144
 
145
-
146
145
  start = time.time()
147
146
  net_io_start = psutil.net_io_counters()
148
147
 
149
- # bytes_or_err: bytes | Exception = rclone.copy_bytes(
150
- # src=src_file,
151
- # offset=offset.as_int(),
152
- # length=size.as_int(),
153
- # chunk_size=chunk_size,
154
- # direct_io=direct_io,
155
- # max_threads=transfers,
156
- # mount_log=mount_log,
157
- # )
158
-
159
- # bytes_or_err = filechunker.fetch(
160
- # (offset + SizeSuffix("1G")).as_int(), size.as_int()
161
- # )
162
-
163
148
  offset = SizeSuffix("1G")
164
149
 
165
- futures: list[Future[bytes | Exception]] = []
166
150
  for i in range(num):
167
151
  offset = SizeSuffix(i * chunk_size.as_int()) + offset
168
152
  future = filechunker.fetch(offset.as_int(), size.as_int())
@@ -177,7 +161,9 @@ def _run_profile(
177
161
  diff = (time.time() - start) / num
178
162
  net_io_end = psutil.net_io_counters()
179
163
  # self.assertEqual(len(bytes_or_err), size)
180
- assert bytes_count == SizeSuffix(size* num).as_int(), f"Length: {SizeSuffix(bytes_count)} != {SizeSuffix(size* num)}"
164
+ assert (
165
+ bytes_count == SizeSuffix(size * num).as_int()
166
+ ), f"Length: {SizeSuffix(bytes_count)} != {SizeSuffix(size* num)}"
181
167
 
182
168
  # print io stats
183
169
  bytes_sent = (net_io_end.bytes_sent - net_io_start.bytes_sent) // num
@@ -777,9 +777,18 @@ class Rclone:
777
777
  endpoint_url=section.endpoint(),
778
778
  )
779
779
 
780
+ chunk_fetcher: MultiMountFileChunker = self.get_multi_mount_file_chunker(
781
+ src=src_path.parent.as_posix(),
782
+ chunk_size=chunk_size,
783
+ threads=read_threads,
784
+ mount_log=mount_log,
785
+ direct_io=True,
786
+ )
787
+
780
788
  client = S3Client(s3_creds)
781
- config: S3MutliPartUploadConfig = S3MutliPartUploadConfig(
789
+ upload_config: S3MutliPartUploadConfig = S3MutliPartUploadConfig(
782
790
  chunk_size=chunk_size.as_int(),
791
+ chunk_fetcher=chunk_fetcher.fetch,
783
792
  max_write_threads=write_threads,
784
793
  retries=retries,
785
794
  resume_path_json=save_state_json,
@@ -791,10 +800,7 @@ class Rclone:
791
800
  print(f"Uploading {name} to {s3_key} in bucket {bucket_name}")
792
801
  print(f"Source: {src_path}")
793
802
  print(f"bucket_name: {bucket_name}")
794
- print(f"upload_config: {config}")
795
-
796
- upload_target: S3UploadTarget
797
- upload_config: S3MutliPartUploadConfig
803
+ print(f"upload_config: {upload_config}")
798
804
 
799
805
  upload_target = S3UploadTarget(
800
806
  bucket_name=bucket_name,
@@ -802,18 +808,14 @@ class Rclone:
802
808
  s3_key=s3_key,
803
809
  )
804
810
 
805
- upload_config = S3MutliPartUploadConfig(
806
- chunk_size=chunk_size.as_int(),
807
- retries=retries,
808
- max_write_threads=write_threads,
809
- resume_path_json=save_state_json,
810
- max_chunks_before_suspension=max_chunks_before_suspension,
811
- )
812
-
813
- out: MultiUploadResult = client.upload_file_multipart(
814
- upload_target=upload_target, upload_config=upload_config
815
- )
816
- return out
811
+ try:
812
+ out: MultiUploadResult = client.upload_file_multipart(
813
+ upload_target=upload_target,
814
+ upload_config=upload_config,
815
+ )
816
+ return out
817
+ finally:
818
+ chunk_fetcher.close()
817
819
 
818
820
  def _copy_bytes(
819
821
  self,
@@ -11,7 +11,10 @@ from rclone_api.s3.basic_ops import (
11
11
  )
12
12
  from rclone_api.s3.create import create_s3_client
13
13
  from rclone_api.s3.types import S3Credentials, S3MutliPartUploadConfig, S3UploadTarget
14
- from rclone_api.s3.upload_file_multipart import MultiUploadResult, upload_file_multipart
14
+ from rclone_api.s3.upload_file_multipart import (
15
+ MultiUploadResult,
16
+ upload_file_multipart,
17
+ )
15
18
 
16
19
  _MIN_THRESHOLD_FOR_CHUNKING = 5 * 1024 * 1024
17
20
 
@@ -67,6 +70,7 @@ class S3Client:
67
70
 
68
71
  out = upload_file_multipart(
69
72
  s3_client=self.client,
73
+ chunk_fetcher=upload_config.chunk_fetcher,
70
74
  bucket_name=bucket_name,
71
75
  file_path=upload_target.src_file,
72
76
  object_name=upload_target.s3_key,
@@ -1,8 +1,10 @@
1
1
  import time
2
2
  import warnings
3
+ from concurrent.futures import Future
3
4
  from pathlib import Path
4
5
  from queue import Queue
5
6
  from threading import Event
7
+ from typing import Callable
6
8
 
7
9
  from rclone_api.s3.chunk_types import FileChunk, UploadState
8
10
  from rclone_api.util import locked_print
@@ -27,12 +29,16 @@ def _get_file_size(file_path: Path, timeout: int = 60) -> int:
27
29
 
28
30
  def file_chunker(
29
31
  upload_state: UploadState,
32
+ chunk_fetcher: Callable[[int, int], Future[bytes | Exception]],
30
33
  max_chunks: int | None,
31
34
  cancel_signal: Event,
32
35
  output: Queue[FileChunk | None],
33
36
  ) -> None:
34
37
  count = 0
35
38
 
39
+ if False:
40
+ print(chunk_fetcher)
41
+
36
42
  def should_stop() -> bool:
37
43
  nonlocal count
38
44
 
@@ -41,14 +47,6 @@ def file_chunker(
41
47
  if count >= max_chunks:
42
48
  return True
43
49
  count += 1
44
- if count > 10 and count % 10 == 0:
45
- # recheck that the file size has not changed
46
- file_size = _get_file_size(upload_state.upload_info.src_file_path)
47
- if file_size != upload_state.upload_info.file_size:
48
- locked_print(
49
- f"File size changed, cannot resume, expected {upload_state.upload_info.file_size}, got {file_size}"
50
- )
51
- raise ValueError("File size changed, cannot resume")
52
50
  return False
53
51
 
54
52
  upload_info = upload_state.upload_info
@@ -58,7 +56,6 @@ def file_chunker(
58
56
  # Mounted files may take a while to appear, so keep retrying.
59
57
 
60
58
  try:
61
- file_size = _get_file_size(src, timeout=60)
62
59
  part_number = 1
63
60
  done_part_numbers: set[int] = {
64
61
  p.part_number for p in upload_state.parts if p is not None
@@ -86,25 +83,56 @@ def file_chunker(
86
83
  break
87
84
  assert curr_parth_num is not None
88
85
  offset = (curr_parth_num - 1) * chunk_size
86
+ file_size = upload_info.file_size
89
87
 
90
88
  assert offset < file_size, f"Offset {offset} is greater than file size"
91
89
 
92
90
  # Open the file, seek, read the chunk, and close immediately.
93
- with open(file_path, "rb") as f:
94
- f.seek(offset)
95
- data = f.read(chunk_size)
96
-
97
- if not data:
98
- warnings.warn(f"Empty data for part {part_number} of {file_path}")
99
-
100
- file_chunk = FileChunk(
101
- src,
102
- upload_id=upload_info.upload_id,
103
- part_number=part_number,
104
- data=data, # After this, data should not be reused.
105
- )
106
- done_part_numbers.add(part_number)
107
- output.put(file_chunk)
91
+ # with open(file_path, "rb") as f:
92
+ # f.seek(offset)
93
+ # data = f.read(chunk_size)
94
+
95
+ # data = chunk_fetcher(offset, chunk_size).result()
96
+
97
+ assert curr_parth_num is not None
98
+ cpn: int = curr_parth_num
99
+
100
+ def on_complete(
101
+ fut: Future[bytes | Exception],
102
+ part_number: int = cpn,
103
+ file_path: Path = file_path,
104
+ ) -> None:
105
+ data: bytes | Exception = fut.result()
106
+ if isinstance(data, Exception):
107
+ warnings.warn(
108
+ f"Error reading file: {data}, skipping part {part_number}"
109
+ )
110
+ return
111
+
112
+ if isinstance(data, Exception):
113
+ err: Exception = data
114
+ warnings.warn(
115
+ f"Error reading file: {err}, skipping part {part_number}"
116
+ )
117
+ return
118
+
119
+ if not data:
120
+ warnings.warn(f"Empty data for part {part_number} of {file_path}")
121
+
122
+ file_chunk = FileChunk(
123
+ src,
124
+ upload_id=upload_info.upload_id,
125
+ part_number=part_number,
126
+ data=data, # After this, data should not be reused.
127
+ )
128
+ done_part_numbers.add(part_number)
129
+ output.put(file_chunk)
130
+
131
+ fut = chunk_fetcher(curr_parth_num, chunk_size)
132
+ fut.add_done_callback(on_complete)
133
+ # wait until the output queue can accept the next chunk
134
+ while output.full():
135
+ time.sleep(0.1)
108
136
  except Exception as e:
109
137
 
110
138
  warnings.warn(f"Error reading file: {e}")
@@ -1,6 +1,8 @@
1
+ from concurrent.futures import Future
1
2
  from dataclasses import dataclass
2
3
  from enum import Enum
3
4
  from pathlib import Path
5
+ from typing import Callable
4
6
 
5
7
 
6
8
  class S3Provider(Enum):
@@ -45,6 +47,7 @@ class S3MutliPartUploadConfig:
45
47
 
46
48
  chunk_size: int
47
49
  retries: int
50
+ chunk_fetcher: Callable[[int, int], Future[bytes | Exception]]
48
51
  resume_path_json: Path
49
52
  max_write_threads: int
50
53
  max_chunks_before_suspension: int | None = None
@@ -2,10 +2,11 @@ import _thread
2
2
  import os
3
3
  import traceback
4
4
  import warnings
5
- from concurrent.futures import ThreadPoolExecutor
5
+ from concurrent.futures import Future, ThreadPoolExecutor
6
6
  from pathlib import Path
7
7
  from queue import Queue
8
8
  from threading import Event, Thread
9
+ from typing import Callable
9
10
 
10
11
  from botocore.client import BaseClient
11
12
 
@@ -117,6 +118,7 @@ def _abort_previous_upload(upload_state: UploadState) -> None:
117
118
 
118
119
  def upload_file_multipart(
119
120
  s3_client: BaseClient,
121
+ chunk_fetcher: Callable[[int, int], Future[bytes | Exception]],
120
122
  bucket_name: str,
121
123
  file_path: Path,
122
124
  object_name: str,
@@ -169,7 +171,7 @@ def upload_file_multipart(
169
171
  )
170
172
  return upload_state
171
173
 
172
- filechunks: Queue[FileChunk | None] = Queue(10)
174
+ filechunks: Queue[FileChunk | None] = Queue(upload_threads)
173
175
  new_state = make_new_state()
174
176
  loaded_state = get_upload_state()
175
177
 
@@ -211,6 +213,7 @@ def upload_file_multipart(
211
213
 
212
214
  def chunker_task(
213
215
  upload_state=upload_state,
216
+ chunk_fetcher=chunk_fetcher,
214
217
  output=filechunks,
215
218
  max_chunks=max_chunks_before_suspension,
216
219
  cancel_signal=cancel_chunker_event,
@@ -219,6 +222,7 @@ def upload_file_multipart(
219
222
  try:
220
223
  file_chunker(
221
224
  upload_state=upload_state,
225
+ chunk_fetcher=chunk_fetcher,
222
226
  output=output,
223
227
  max_chunks=max_chunks,
224
228
  cancel_signal=cancel_signal,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.1.95
3
+ Version: 1.1.97
4
4
  Summary: rclone api in python
5
5
  Home-page: https://github.com/zackees/rclone-api
6
6
  License: BSD 3-Clause License
@@ -42,7 +42,6 @@ from rclone_api import Rclone, SizeSuffix
42
42
  _HERE = Path(__file__).parent
43
43
  _PROJECT_ROOT = _HERE.parent
44
44
  _CONFIG_PATH = _PROJECT_ROOT / "rclone-mounted-ranged-download.conf"
45
- _IS_WINDOWS = os.name == "nt"
46
45
 
47
46
  _CHUNK_SIZE = 1024 * 1024 * 16
48
47
 
@@ -1,6 +1,7 @@
1
1
  import os
2
2
  import tempfile
3
3
  import unittest
4
+ from concurrent.futures import Future, ThreadPoolExecutor
4
5
  from pathlib import Path
5
6
 
6
7
  from dotenv import load_dotenv
@@ -65,6 +66,20 @@ class RcloneS3Tester(unittest.TestCase):
65
66
  filesize = tmpfile.stat().st_size
66
67
  state_json = Path(tempdir) / "state.json"
67
68
 
69
+ def simple_fetcher(
70
+ chunk_id: int, chunk_size: int
71
+ ) -> Future[bytes | Exception]:
72
+ with ThreadPoolExecutor() as executor:
73
+
74
+ def task() -> bytes | Exception:
75
+ with open(str(tmpfile), "rb") as f:
76
+ idx = (chunk_id - 1) * chunk_size
77
+ f.seek(idx * chunk_size)
78
+ return f.read(chunk_size)
79
+
80
+ fut = executor.submit(task)
81
+ return fut
82
+
68
83
  upload_target: S3UploadTarget = S3UploadTarget(
69
84
  src_file=Path(tmpfile),
70
85
  bucket_name=BUCKET_NAME,
@@ -74,6 +89,7 @@ class RcloneS3Tester(unittest.TestCase):
74
89
  # Uploads one chunk then stops.
75
90
  upload_config_partial: S3MutliPartUploadConfig = S3MutliPartUploadConfig(
76
91
  chunk_size=chunk_size,
92
+ chunk_fetcher=simple_fetcher,
77
93
  max_write_threads=16,
78
94
  retries=0,
79
95
  resume_path_json=state_json,
@@ -83,6 +99,7 @@ class RcloneS3Tester(unittest.TestCase):
83
99
  # Finishes the upload.
84
100
  upload_config_all: S3MutliPartUploadConfig = S3MutliPartUploadConfig(
85
101
  chunk_size=chunk_size,
102
+ chunk_fetcher=simple_fetcher,
86
103
  max_write_threads=16,
87
104
  retries=0,
88
105
  resume_path_json=state_json,
@@ -96,7 +113,8 @@ class RcloneS3Tester(unittest.TestCase):
96
113
  )
97
114
  self.assertEqual(rslt, MultiUploadResult.SUSPENDED)
98
115
  rslt = s3_client.upload_file_multipart(
99
- upload_target=upload_target, upload_config=upload_config_all
116
+ upload_target=upload_target,
117
+ upload_config=upload_config_all,
100
118
  )
101
119
  self.assertEqual(rslt, MultiUploadResult.UPLOADED_RESUME)
102
120
  print(f"Uploading file {f.name} to {BUCKET_NAME}")
@@ -105,7 +123,8 @@ class RcloneS3Tester(unittest.TestCase):
105
123
  print("Upload successful.")
106
124
  print(f"Uploading file {f.name} to {BUCKET_NAME}/{dst_path}")
107
125
  rslt = s3_client.upload_file_multipart(
108
- upload_target=upload_target, upload_config=upload_config_all
126
+ upload_target=upload_target,
127
+ upload_config=upload_config_all,
109
128
  )
110
129
  self.assertEqual(rslt, MultiUploadResult.ALREADY_DONE)
111
130
  print("done")
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