rclone-api 1.0.98__py2.py3-none-any.whl → 1.0.99__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.
@@ -1,93 +1,121 @@
1
- from dataclasses import dataclass, field
1
+ from dataclasses import dataclass, fields, is_dataclass
2
+ from typing import Type, TypeVar
2
3
 
4
+ T = TypeVar("T")
3
5
 
4
- @dataclass
5
- class CopyFlags:
6
- check_first: bool = False
7
- checksum: bool = False
8
- ignore_existing: bool = False
9
- ignore_times: bool = False
10
- immutable: bool = False
11
- inplace: bool = False
12
- links: bool = False
13
- metadata: bool = False
14
-
15
-
16
- @dataclass
17
- class SyncFlags:
18
- backup_dir: str | None = None
19
- delete_after: bool = False
20
- delete_before: bool = False
21
- delete_during: bool = False
22
- ignore_errors: bool = False
23
- track_renames: bool = False
24
-
25
-
26
- @dataclass
27
- class ImportantFlags:
28
- dry_run: bool = False
29
- interactive: bool = False
30
- verbose: int = 0 # 0 = default, 1 = -v, 2 = -vv, etc.
31
-
32
-
33
- @dataclass
34
- class NetworkingFlags:
35
- bwlimit: str | None = None
36
- timeout: str | None = "5m0s"
37
- tpslimit: float | None = None
38
- user_agent: str | None = "rclone/v1.69.1"
39
6
 
7
+ def _merge(cls: Type[T], dataclass_a: T, dataclass_b: T) -> T:
8
+ if not is_dataclass(dataclass_a) or not is_dataclass(dataclass_b):
9
+ raise ValueError("Both inputs must be dataclass instances")
10
+ if type(dataclass_a) is not type(dataclass_b):
11
+ raise ValueError("Dataclass instances must be of the same type")
40
12
 
41
- @dataclass
42
- class PerformanceFlags:
43
- buffer_size: str | None = "16MiB"
44
- checkers: int = 8
45
- transfers: int = 4
13
+ merged_kwargs = {}
14
+ for field in fields(dataclass_a):
15
+ a_value = getattr(dataclass_a, field.name)
16
+ b_value = getattr(dataclass_b, field.name)
46
17
 
18
+ if is_dataclass(a_value) and is_dataclass(b_value):
19
+ merged_kwargs[field.name] = _merge(type(a_value), a_value, b_value)
20
+ else:
21
+ merged_kwargs[field.name] = b_value if b_value is not None else a_value
47
22
 
48
- @dataclass
49
- class ConfigFlags:
50
- config: str | None = None
51
- ask_password: bool = True
52
- auto_confirm: bool = False
23
+ return cls(**merged_kwargs)
53
24
 
54
25
 
55
- @dataclass
56
- class DebuggingFlags:
57
- cpuprofile: str | None = None
58
- memprofile: str | None = None
26
+ def _field_name_to_flag(field_name: str) -> str:
27
+ return f"--{field_name.replace('_', '-')}"
59
28
 
60
29
 
61
30
  @dataclass
62
- class FilterFlags:
63
- exclude: list[str] = field(default_factory=list)
64
- include: list[str] = field(default_factory=list)
65
- max_age: str | None = None
66
- min_size: str | None = None
31
+ class BaseFlags:
32
+ def to_args(self) -> list[str]:
33
+ args = []
34
+ for field in fields(self):
35
+ value = getattr(self, field.name)
36
+ if value is None:
37
+ continue
38
+ # If the field value is a nested dataclass that supports to_args, use it.
39
+ if is_dataclass(value) and hasattr(value, "to_args"):
40
+ to_args = getattr(value, "to_args")
41
+ args.extend(to_args())
42
+ elif isinstance(value, bool):
43
+ # Only include the flag if the boolean is True.
44
+ if value:
45
+ args.append(_field_name_to_flag(field.name))
46
+ else:
47
+ args.append(_field_name_to_flag(field.name))
48
+ if isinstance(value, list):
49
+ # Join list values with a comma.
50
+ args.append(",".join(map(str, value)))
51
+ else:
52
+ args.append(str(value))
53
+ return args
54
+
55
+ def merge(self, other: "BaseFlags") -> "BaseFlags":
56
+ # Use the type of self, so merging CopyFlags returns a CopyFlags instance.
57
+ return _merge(type(self), self, other)
58
+
59
+ def __repr__(self):
60
+ return str(self.to_args())
61
+
62
+
63
+ @dataclass(repr=False)
64
+ class CopyFlags(BaseFlags):
65
+ check_first: bool | None = None
66
+ checksum: bool | None = False
67
+ compare_dest: list[str] | None = None
68
+ copy_dest: list[str] | None = None
69
+ cutoff_mode: str | None = None
70
+ ignore_case_sync: bool | None = None
71
+ ignore_checksum: bool | None = None
72
+ ignore_existing: bool | None = None
73
+ ignore_size: bool | None = None
74
+ ignore_times: bool | None = None
75
+ immutable: bool | None = None
76
+ inplace: bool | None = None
77
+ links: bool | None = None
78
+ max_backlog: int | None = None
79
+ max_duration: str | None = None
80
+ max_transfer: str | None = None
81
+ metadata: bool | None = None
82
+ modify_window: str | None = None
83
+ multi_thread_chunk_size: str | None = None
84
+ multi_thread_cutoff: str | None = None
85
+ multi_thread_streams: int | None = None
86
+ multi_thread_write_buffer_size: str | None = None
87
+ no_check_dest: bool | None = None
88
+ no_traverse: bool | None = None
89
+ no_update_dir_modtime: bool | None = None
90
+ no_update_modtime: bool | None = None
91
+ order_by: str | None = None
92
+ partial_suffix: str | None = None
93
+ refresh_times: bool | None = None
94
+ server_side_across_configs: bool | None = None
95
+ size_only: bool | None = None
96
+ streaming_upload_cutoff: str | None = None
97
+ update: bool | None = None
98
+
99
+
100
+ @dataclass(repr=False)
101
+ class Flags(BaseFlags):
102
+ copy: CopyFlags | None = None
67
103
 
68
104
 
69
- @dataclass
70
- class ListingFlags:
71
- fast_list: bool = False
105
+ def unit_test() -> None:
106
+ copy_flags_a = CopyFlags(compare_dest=["a", "b"])
107
+ copy_flags_b = CopyFlags(checksum=False)
108
+ flags_a = copy_flags_a.merge(copy_flags_b)
109
+ print("A:", flags_a)
72
110
 
111
+ copy_flags_c = CopyFlags(checksum=True)
112
+ copy_flags_d = CopyFlags(checksum=False)
73
113
 
74
- @dataclass
75
- class LoggingFlags:
76
- log_file: str | None = None
77
- log_level: str = "NOTICE" # Options: DEBUG, INFO, NOTICE, ERROR
78
- stats: str | None = "1m0s"
79
- progress: bool = False
114
+ merged_c_d = copy_flags_c.merge(copy_flags_d)
115
+ print("B:", merged_c_d)
116
+ merged_d_c = copy_flags_d.merge(copy_flags_c)
117
+ print("C:", merged_d_c)
80
118
 
81
119
 
82
- @dataclass
83
- class Flags:
84
- copy: CopyFlags | None = None
85
- sync: SyncFlags | None = None
86
- important: ImportantFlags | None = None
87
- networking: NetworkingFlags | None = None
88
- performance: PerformanceFlags | None = None
89
- config: ConfigFlags | None = None
90
- debugging: DebuggingFlags | None = None
91
- filter: FilterFlags | None = None
92
- listing: ListingFlags | None = None
93
- logging: LoggingFlags | None = None
120
+ if __name__ == "__main__":
121
+ unit_test()
rclone_api/rclone.py CHANGED
@@ -919,6 +919,7 @@ class Rclone:
919
919
  if outdir.exists():
920
920
  print(f"{outdir} mount still exists, attempting to remove")
921
921
  if not _IS_WINDOWS:
922
+
922
923
  def exec(cmd: str) -> int:
923
924
  if verbose:
924
925
  print(f"Executing: {cmd}")
@@ -260,6 +260,20 @@ def _get_chunk_tmpdir() -> Path:
260
260
  return out
261
261
 
262
262
 
263
+ def _get_file_size(file_path: Path, timeout: int = 60) -> int:
264
+ sleep_time = timeout / 60 if timeout > 0 else 1
265
+ start = time.time()
266
+ while True:
267
+ try:
268
+ if file_path.exists():
269
+ return file_path.stat().st_size
270
+ except FileNotFoundError:
271
+ pass
272
+ if time.time() - start > timeout:
273
+ raise TimeoutError(f"File {file_path} not found after {timeout} seconds")
274
+ time.sleep(sleep_time)
275
+
276
+
263
277
  def file_chunker(
264
278
  upload_state: UploadState, max_chunks: int | None, output: Queue[FileChunk | None]
265
279
  ) -> None:
@@ -279,7 +293,8 @@ def file_chunker(
279
293
  file_path = upload_info.src_file_path
280
294
  chunk_size = upload_info.chunk_size
281
295
  src = Path(file_path)
282
- file_size = os.path.getsize(file_path)
296
+ # Mounted files may take a while to appear, so keep retrying.
297
+ file_size = _get_file_size(src, timeout=60)
283
298
  part_number = 1
284
299
  done_part_numbers: set[int] = {
285
300
  p.part_number for p in upload_state.parts if p is not None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.0.98
3
+ Version: 1.0.99
4
4
  Summary: rclone api in python
5
5
  Home-page: https://github.com/zackees/rclone-api
6
6
  License: BSD 3-Clause License
@@ -12,7 +12,7 @@ rclone_api/file.py,sha256=EP5yT2dZ0H2p7CY5n0y5k5pHhIliV25pm8KOwBklUTk,1863
12
12
  rclone_api/filelist.py,sha256=xbiusvNgaB_b_kQOZoHMJJxn6TWGtPrWd2J042BI28o,767
13
13
  rclone_api/group_files.py,sha256=H92xPW9lQnbNw5KbtZCl00bD6iRh9yRbCuxku4j_3dg,8036
14
14
  rclone_api/process.py,sha256=RrMfTe0bndmJ6gBK67ioqNvCstJ8aTC8RlGX1XBLlcw,4191
15
- rclone_api/rclone.py,sha256=eSQNNKS8tO0WHvbMKT_Ov1IIhUxsUy8L0KTOoPpVW-Y,43539
15
+ rclone_api/rclone.py,sha256=_CejQQ8jF-VYc_rO2u5i9qr8jmyksxyuoO4ZdHLW6tg,43540
16
16
  rclone_api/remote.py,sha256=O9WDUFQy9f6oT1HdUbTixK2eg0xtBBm8k4Xl6aa6K00,431
17
17
  rclone_api/rpath.py,sha256=8ZA_1wxWtskwcy0I8V2VbjKDmzPkiWd8Q2JQSvh-sYE,2586
18
18
  rclone_api/scan_missing_folders.py,sha256=Kulca2Q6WZodt00ATFHkmqqInuoPvBkhTcS9703y6po,4740
@@ -22,15 +22,15 @@ rclone_api/walk.py,sha256=-54NVE8EJcCstwDoaC_UtHm73R2HrZwVwQmsnv55xNU,3369
22
22
  rclone_api/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
23
23
  rclone_api/cmd/copy_large_s3.py,sha256=33KFvCrh5uk-rdRtkREdEs2WNwxGgTdCAWDLCE4dm0A,2855
24
24
  rclone_api/cmd/list_files.py,sha256=x8FHODEilwKqwdiU1jdkeJbLwOqUkUQuDWPo2u_zpf0,741
25
- rclone_api/experimental/flags.py,sha256=7sJyRU1vh5DgSdpFiv0gom2Aaw1-OP2v4PL5OXEruUo,2083
25
+ rclone_api/experimental/flags.py,sha256=AHbTaFHuyYFm3pjdvbQ100jztOXOdNuxalMr8UjXnV4,4097
26
26
  rclone_api/s3/api.py,sha256=VstlaEnBjO2JDQuCRLdTfUGvQLbfshlXXhAzimFv4Vc,3763
27
27
  rclone_api/s3/basic_ops.py,sha256=hK3366xhVEzEcjz9Gk_8lFx6MRceAk72cax6mUrr6ko,2104
28
- rclone_api/s3/chunk_uploader.py,sha256=Wcm4h_A6ORaJvT9P9qSeknbJYch22RKZ4FGo1iePlAw,17385
28
+ rclone_api/s3/chunk_uploader.py,sha256=IX9Rvt8cypiPC_lfWnG0O2iom4_TTtlM5JAR6-L2w-o,17919
29
29
  rclone_api/s3/create.py,sha256=SK3IGHZwsSkoG4Zb4NCphcVg9_f7VifDKng-tExMS2s,3088
30
30
  rclone_api/s3/types.py,sha256=81_3jwg6MGIxC-GxL-6zANzKO6au9C0BWvAqRyODxOM,1361
31
- rclone_api-1.0.98.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
32
- rclone_api-1.0.98.dist-info/METADATA,sha256=N3rINQp548v5kGCXCnQnS36fdiQME8ifJk4gAv2GaGU,4479
33
- rclone_api-1.0.98.dist-info/WHEEL,sha256=rF4EZyR2XVS6irmOHQIJx2SUqXLZKRMUrjsg8UwN-XQ,109
34
- rclone_api-1.0.98.dist-info/entry_points.txt,sha256=6eNqTRXKhVf8CpWNjXiOa_0Du9tHiW_HD2iQSXRsUg8,132
35
- rclone_api-1.0.98.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
36
- rclone_api-1.0.98.dist-info/RECORD,,
31
+ rclone_api-1.0.99.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
32
+ rclone_api-1.0.99.dist-info/METADATA,sha256=ZFl7ZyCU8maaSGuE356UwwIZqW8aZrjrJ_BfU1r9Yts,4479
33
+ rclone_api-1.0.99.dist-info/WHEEL,sha256=rF4EZyR2XVS6irmOHQIJx2SUqXLZKRMUrjsg8UwN-XQ,109
34
+ rclone_api-1.0.99.dist-info/entry_points.txt,sha256=6eNqTRXKhVf8CpWNjXiOa_0Du9tHiW_HD2iQSXRsUg8,132
35
+ rclone_api-1.0.99.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
36
+ rclone_api-1.0.99.dist-info/RECORD,,