rclone-api 1.1.5__py2.py3-none-any.whl → 1.1.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/__init__.py CHANGED
@@ -10,7 +10,7 @@ from .rclone import Rclone, rclone_verbose
10
10
  from .remote import Remote
11
11
  from .rpath import RPath
12
12
  from .s3.types import MultiUploadResult
13
- from .types import ListingOption, Order, SizeResult
13
+ from .types import ListingOption, Order, SizeResult, SizeSuffix
14
14
 
15
15
  __all__ = [
16
16
  "Rclone",
@@ -34,4 +34,5 @@ __all__ = [
34
34
  "Parsed",
35
35
  "Section",
36
36
  "MultiUploadResult",
37
+ "SizeSuffix",
37
38
  ]
@@ -2,7 +2,7 @@ import argparse
2
2
  from dataclasses import dataclass
3
3
  from pathlib import Path
4
4
 
5
- from rclone_api import MultiUploadResult, Rclone
5
+ from rclone_api import MultiUploadResult, Rclone, SizeSuffix
6
6
 
7
7
  _1MB = 1024 * 1024
8
8
 
@@ -12,7 +12,7 @@ class Args:
12
12
  config_path: Path
13
13
  src: str
14
14
  dst: str
15
- chunk_size_mb: int
15
+ chunk_size_mb: SizeSuffix
16
16
  read_concurrent_chunks: int
17
17
  retries: int
18
18
  save_state_json: Path
@@ -35,17 +35,20 @@ def _parse_args() -> Args:
35
35
  "--config", help="Path to rclone config file", type=Path, required=True
36
36
  )
37
37
  parser.add_argument(
38
- "--chunk-size-mb", help="Chunk size in MB", type=int, default=256
38
+ "--chunk-size",
39
+ help="Chunk size that will be read and uploaded in in SizeSuffix (i.e. 128M = 128 megabytes) form",
40
+ type=str,
41
+ default="128M",
39
42
  )
40
43
  parser.add_argument(
41
44
  "--read-concurrent-chunks",
42
- help="Maximum number of chunks to read",
45
+ help="Maximum number of chunks to read in a look ahead cache",
43
46
  type=int,
44
- default=4,
47
+ default=1,
45
48
  )
46
49
  parser.add_argument("--retries", help="Number of retries", type=int, default=3)
47
50
  parser.add_argument(
48
- "--resumable-json",
51
+ "--resume-json",
49
52
  help="Path to resumable JSON file",
50
53
  type=Path,
51
54
  default="resume.json",
@@ -56,10 +59,10 @@ def _parse_args() -> Args:
56
59
  config_path=Path(args.config),
57
60
  src=args.src,
58
61
  dst=args.dst,
59
- chunk_size_mb=args.chunk_size_mb,
62
+ chunk_size_mb=SizeSuffix(args.chunk_size),
60
63
  read_concurrent_chunks=args.read_concurrent_chunks,
61
64
  retries=args.retries,
62
- save_state_json=args.resumable_json,
65
+ save_state_json=args.resume_json,
63
66
  verbose=args.verbose,
64
67
  )
65
68
  return out
rclone_api/mount.py CHANGED
@@ -23,6 +23,19 @@ def run_command(cmd: str, verbose: bool) -> int:
23
23
  return -1
24
24
 
25
25
 
26
+ def prepare_mount(outdir: Path, verbose: bool) -> None:
27
+ if _SYSTEM == "Windows":
28
+ # Windows -> Must create parent directories only if they don't exist
29
+ if verbose:
30
+ print(f"Creating parent directories for {outdir}")
31
+ outdir.parent.mkdir(parents=True, exist_ok=True)
32
+ else:
33
+ # Linux -> Must create parent directories and the directory itself
34
+ if verbose:
35
+ print(f"Creating directories for {outdir}")
36
+ outdir.mkdir(parents=True, exist_ok=True)
37
+
38
+
26
39
  def clean_mount(mount_path: Path, verbose: bool = False) -> None:
27
40
  """
28
41
  Clean up a mount path across Linux, macOS, and Windows.
rclone_api/rclone.py CHANGED
@@ -34,7 +34,13 @@ from rclone_api.s3.types import (
34
34
  S3Provider,
35
35
  S3UploadTarget,
36
36
  )
37
- from rclone_api.types import ListingOption, ModTimeStrategy, Order, SizeResult
37
+ from rclone_api.types import (
38
+ ListingOption,
39
+ ModTimeStrategy,
40
+ Order,
41
+ SizeResult,
42
+ SizeSuffix,
43
+ )
38
44
  from rclone_api.util import (
39
45
  get_check,
40
46
  get_rclone_exe,
@@ -673,10 +679,13 @@ class Rclone:
673
679
  src: str,
674
680
  dst: str,
675
681
  save_state_json: Path,
676
- chunk_size: int = 16
677
- * 1024
678
- * 1024, # This setting will scale the performance of the upload
679
- concurrent_chunks: int = 4, # This setting will scale the performance of the upload
682
+ chunk_size: SizeSuffix | None = None,
683
+ # 16
684
+ # * 1024
685
+ # * 1024, # This setting will scale the performance of the upload
686
+ concurrent_chunks: (
687
+ int | None
688
+ ) = None, # This setting will scale the performance of the upload
680
689
  retries: int = 3,
681
690
  verbose: bool | None = None,
682
691
  max_chunks_before_suspension: int | None = None,
@@ -687,16 +696,22 @@ class Rclone:
687
696
  from rclone_api.s3.create import S3Credentials
688
697
  from rclone_api.util import S3PathInfo, random_str, split_s3_path
689
698
 
699
+ _tmp: SizeSuffix | str = chunk_size or "16MiB"
700
+ chunk_size = SizeSuffix(_tmp)
701
+ assert chunk_size is not None
702
+ concurrent_chunks = concurrent_chunks or 4
703
+ size_limit = SizeSuffix(chunk_size * concurrent_chunks)
704
+
690
705
  other_args: list[str] = [
691
706
  "--no-modtime",
692
707
  "--vfs-read-wait",
693
708
  "1s",
694
709
  "--vfs-disk-space-total-size",
695
- str(2 * chunk_size * concurrent_chunks), # purge quickly.
710
+ size_limit.as_str(), # purge quickly.
696
711
  "--vfs-read-chunk-size",
697
- str(chunk_size),
712
+ chunk_size.as_str(),
698
713
  "--vfs-read-chunk-size-limit",
699
- str(chunk_size * concurrent_chunks),
714
+ size_limit.as_str(),
700
715
  "--vfs-read-chunk-streams",
701
716
  str(concurrent_chunks),
702
717
  "--vfs-fast-fingerprint",
@@ -765,7 +780,7 @@ class Rclone:
765
780
 
766
781
  client = S3Client(s3_creds)
767
782
  config: S3MutliPartUploadConfig = S3MutliPartUploadConfig(
768
- chunk_size=chunk_size,
783
+ chunk_size=chunk_size.as_int(),
769
784
  retries=retries,
770
785
  resume_path_json=save_state_json,
771
786
  max_chunks_before_suspension=max_chunks_before_suspension,
@@ -788,7 +803,7 @@ class Rclone:
788
803
  )
789
804
 
790
805
  upload_config = S3MutliPartUploadConfig(
791
- chunk_size=chunk_size,
806
+ chunk_size=chunk_size.as_int(),
792
807
  retries=retries,
793
808
  resume_path_json=save_state_json,
794
809
  max_chunks_before_suspension=max_chunks_before_suspension,
@@ -845,24 +860,14 @@ class Rclone:
845
860
  Raises:
846
861
  subprocess.CalledProcessError: If the mount operation fails
847
862
  """
863
+ from rclone_api.mount import clean_mount, prepare_mount
864
+
848
865
  allow_writes = allow_writes or False
849
866
  use_links = use_links or True
850
867
  verbose = get_verbose(verbose)
851
868
  vfs_cache_mode = vfs_cache_mode or "full"
852
- if outdir.exists():
853
- is_empty = not list(outdir.iterdir())
854
- if not is_empty:
855
- raise ValueError(
856
- f"Mount directory already exists and is not empty: {outdir}"
857
- )
858
- outdir.rmdir()
859
-
860
- if _IS_WINDOWS:
861
- # Windows -> Must create parent directories only if they don't exist
862
- outdir.parent.mkdir(parents=True, exist_ok=True)
863
- else:
864
- # Linux -> Must create parent directories and the directory itself
865
- outdir.mkdir(parents=True, exist_ok=True)
869
+ clean_mount(outdir, verbose=verbose)
870
+ prepare_mount(outdir, verbose=verbose)
866
871
  src_str = convert_to_str(src)
867
872
  cmd_list: list[str] = ["mount", src_str, str(outdir)]
868
873
  if not allow_writes:
rclone_api/types.py CHANGED
@@ -1,3 +1,4 @@
1
+ import re
1
2
  from dataclasses import dataclass
2
3
  from enum import Enum
3
4
 
@@ -33,3 +34,92 @@ class SizeResult:
33
34
  prefix: str
34
35
  total_size: int
35
36
  file_sizes: dict[str, int]
37
+
38
+
39
+ def _to_size_suffix(size: int) -> str:
40
+ if size < 1024:
41
+ return f"{size}B"
42
+ if size < 1024 * 1024:
43
+ return f"{size // 1024}K"
44
+ if size < 1024 * 1024 * 1024:
45
+ return f"{size // (1024 * 1024)}M"
46
+ if size < 1024 * 1024 * 1024 * 1024:
47
+ return f"{size // (1024 * 1024 * 1024)}G"
48
+ if size < 1024 * 1024 * 1024 * 1024 * 1024:
49
+ return f"{size // (1024 * 1024 * 1024 * 1024)}T"
50
+ if size < 1024 * 1024 * 1024 * 1024 * 1024 * 1024:
51
+ return f"{size // (1024 * 1024 * 1024 * 1024 * 1024)}P"
52
+ raise ValueError(f"Invalid size: {size}")
53
+
54
+
55
+ _PATTERN_SIZE_SUFFIX = re.compile(r"^(\d+)([A-Za-z]+)$")
56
+
57
+
58
+ def _from_size_suffix(size: str) -> int:
59
+ # 16MiB
60
+ # parse out number and suffix
61
+ match = _PATTERN_SIZE_SUFFIX.match(size)
62
+ if match is None:
63
+ raise ValueError(f"Invalid size suffix: {size}")
64
+ size = match.group(1)
65
+ suffix = match.group(2)[0:1].upper()
66
+ n = int(size)
67
+ if suffix == "B":
68
+ return n
69
+ if suffix == "K":
70
+ return n * 1024
71
+ if suffix == "M":
72
+ return n * 1024 * 1024
73
+ if suffix == "G":
74
+ return n * 1024 * 1024 * 1024
75
+ if suffix == "T":
76
+ return n * 1024 * 1024 * 1024 * 1024
77
+ if suffix == "P":
78
+ return n * 1024 * 1024 * 1024 * 1024 * 1024
79
+ raise ValueError(f"Invalid size suffix: {size}")
80
+
81
+
82
+ class SizeSuffix:
83
+ def __init__(self, size: "int | str | SizeSuffix"):
84
+ self._size: int
85
+ if isinstance(size, SizeSuffix):
86
+ self._size = size._size
87
+ elif isinstance(size, int):
88
+ self._size = size
89
+ elif isinstance(size, str):
90
+ self._size = _from_size_suffix(size)
91
+ else:
92
+ raise ValueError(f"Invalid type for size: {type(size)}")
93
+
94
+ def as_int(self) -> int:
95
+ return self._size
96
+
97
+ def as_str(self) -> str:
98
+ return _to_size_suffix(self._size)
99
+
100
+ def __repr__(self) -> str:
101
+ return self.as_str()
102
+
103
+ def __str__(self) -> str:
104
+ return self.as_str()
105
+
106
+ @staticmethod
107
+ def _to_size(size: "int | SizeSuffix") -> int:
108
+ if isinstance(size, int):
109
+ return size
110
+ elif isinstance(size, SizeSuffix):
111
+ return size._size
112
+ else:
113
+ raise ValueError(f"Invalid type for size: {type(size)}")
114
+
115
+ def __mul__(self, other: "int | SizeSuffix") -> "SizeSuffix":
116
+ other_int = SizeSuffix(other)
117
+ return SizeSuffix(self._size * other_int._size)
118
+
119
+ def __add__(self, other: "int | SizeSuffix") -> "SizeSuffix":
120
+ other_int = SizeSuffix(other)
121
+ return SizeSuffix(self._size + other_int._size)
122
+
123
+ def __sub__(self, other: "int | SizeSuffix") -> "SizeSuffix":
124
+ other_int = SizeSuffix(other)
125
+ return SizeSuffix(self._size - other_int._size)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.1.5
3
+ Version: 1.1.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
@@ -1,4 +1,4 @@
1
- rclone_api/__init__.py,sha256=m7hQkaIsn9grAlziu9nvDlOIQY6VflYFwC1c_g0qCJo,833
1
+ rclone_api/__init__.py,sha256=6TVA3JUVxr76wzy29XRTX_xTdQ5JBGwuheNvQc8EgsU,863
2
2
  rclone_api/cli.py,sha256=dibfAZIh0kXWsBbfp3onKLjyZXo54mTzDjUdzJlDlWo,231
3
3
  rclone_api/completed_process.py,sha256=_IZ8IWK7DM1_tsbDEkH6wPZ-bbcrgf7A7smls854pmg,1775
4
4
  rclone_api/config.py,sha256=f6jEAxVorGFr31oHfcsu5AJTtOJj2wR5tTSsbGGZuIw,2558
@@ -11,17 +11,17 @@ rclone_api/exec.py,sha256=1ovvaMXDEfLiT7BrYZyE85u_yFhEUwUNW3jPOzqknR8,1023
11
11
  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
- rclone_api/mount.py,sha256=OJGiZLhtvBJ8CTHSYUoKnYblCff3cD8_q4JTgTIVJHQ,3839
14
+ rclone_api/mount.py,sha256=Zf41gGfTXtSB-KkQoXCq_dpqBvezgz_dPBzk_aDk5Dc,4354
15
15
  rclone_api/process.py,sha256=RrMfTe0bndmJ6gBK67ioqNvCstJ8aTC8RlGX1XBLlcw,4191
16
- rclone_api/rclone.py,sha256=TQuZiUGjVLr8cYVEZQ1m9fyujbASvNs5XFB9EBJOpuc,42115
16
+ rclone_api/rclone.py,sha256=yI_8QdDXppcLO-KbRVGteu-onTa4fTd4ojy_N1HYxx4,42029
17
17
  rclone_api/remote.py,sha256=O9WDUFQy9f6oT1HdUbTixK2eg0xtBBm8k4Xl6aa6K00,431
18
18
  rclone_api/rpath.py,sha256=8ZA_1wxWtskwcy0I8V2VbjKDmzPkiWd8Q2JQSvh-sYE,2586
19
19
  rclone_api/scan_missing_folders.py,sha256=Kulca2Q6WZodt00ATFHkmqqInuoPvBkhTcS9703y6po,4740
20
- rclone_api/types.py,sha256=NC3e78aXCx-sEQ-FqEaC9KzaJDdJhJrKa4Nwum_-Db0,563
20
+ rclone_api/types.py,sha256=e7VmqA2U6MkTT9VDHeP2qwRw_lUUFbaiN6RO-R8B1oo,3336
21
21
  rclone_api/util.py,sha256=ujinqW4xUkZAHBCL1VMhGu88LMdUFIu1ApF8rZEH8rQ,5324
22
22
  rclone_api/walk.py,sha256=-54NVE8EJcCstwDoaC_UtHm73R2HrZwVwQmsnv55xNU,3369
23
23
  rclone_api/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
24
- rclone_api/cmd/copy_large_s3.py,sha256=33KFvCrh5uk-rdRtkREdEs2WNwxGgTdCAWDLCE4dm0A,2855
24
+ rclone_api/cmd/copy_large_s3.py,sha256=kF0wRVRGjIB2CxuwN4z1WnfYjiux7fHkY9FLCw3EJsI,2999
25
25
  rclone_api/cmd/list_files.py,sha256=x8FHODEilwKqwdiU1jdkeJbLwOqUkUQuDWPo2u_zpf0,741
26
26
  rclone_api/experimental/flags.py,sha256=0-mtXg9J4MoMm2uBKbsMLj4pSGRLQUAqNRDJWGttnAQ,2443
27
27
  rclone_api/experimental/flags_base.py,sha256=ajU_czkTcAxXYU-SlmiCfHY7aCQGHvpCLqJ-Z8uZLk0,2102
@@ -32,9 +32,9 @@ rclone_api/s3/chunk_types.py,sha256=Fq0IlhZ0IftuFQFkbICmmrOonII0BNzuY4CIKNC4wB0,
32
32
  rclone_api/s3/chunk_uploader.py,sha256=KO8834Gery9HKWSqjQTNW0pbBbVoGrza9gj-1OaNLQQ,9130
33
33
  rclone_api/s3/create.py,sha256=SK3IGHZwsSkoG4Zb4NCphcVg9_f7VifDKng-tExMS2s,3088
34
34
  rclone_api/s3/types.py,sha256=81_3jwg6MGIxC-GxL-6zANzKO6au9C0BWvAqRyODxOM,1361
35
- rclone_api-1.1.5.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
36
- rclone_api-1.1.5.dist-info/METADATA,sha256=5gWOijGTvfWbWh8q_PkuzrRXqqA-hFK-hTpq-rYoDNE,4478
37
- rclone_api-1.1.5.dist-info/WHEEL,sha256=rF4EZyR2XVS6irmOHQIJx2SUqXLZKRMUrjsg8UwN-XQ,109
38
- rclone_api-1.1.5.dist-info/entry_points.txt,sha256=6eNqTRXKhVf8CpWNjXiOa_0Du9tHiW_HD2iQSXRsUg8,132
39
- rclone_api-1.1.5.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
40
- rclone_api-1.1.5.dist-info/RECORD,,
35
+ rclone_api-1.1.7.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
36
+ rclone_api-1.1.7.dist-info/METADATA,sha256=HujPmSO7dGtf6yoYvjQ3ZkVHfezRF9sTXx1ZXLcaCO8,4478
37
+ rclone_api-1.1.7.dist-info/WHEEL,sha256=rF4EZyR2XVS6irmOHQIJx2SUqXLZKRMUrjsg8UwN-XQ,109
38
+ rclone_api-1.1.7.dist-info/entry_points.txt,sha256=6eNqTRXKhVf8CpWNjXiOa_0Du9tHiW_HD2iQSXRsUg8,132
39
+ rclone_api-1.1.7.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
40
+ rclone_api-1.1.7.dist-info/RECORD,,