rclone-api 1.0.88__py2.py3-none-any.whl → 1.0.90__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.
@@ -0,0 +1,69 @@
1
+ import warnings
2
+
3
+ import boto3
4
+ from botocore.client import BaseClient
5
+ from botocore.config import Config
6
+
7
+ from rclone_api.s3.types import S3Credentials, S3Provider
8
+
9
+
10
+ # Create a Boto3 session and S3 client, this is back blaze specific.
11
+ # Add a function if you want to use a different S3 provider.
12
+ # If AWS support is added in a fork then please merge it back here.
13
+ def _create_backblaze_s3_client(creds: S3Credentials) -> BaseClient:
14
+ """Create and return an S3 client."""
15
+ region_name = creds.region_name
16
+ access_key = creds.access_key_id
17
+ secret_key = creds.secret_access_key
18
+ endpoint_url = creds.endpoint_url
19
+ region_name = region_name or "https://s3.us-west-002.backblazeb2.com"
20
+
21
+ session = boto3.session.Session() # type: ignore
22
+ return session.client(
23
+ service_name="s3",
24
+ aws_access_key_id=access_key,
25
+ aws_secret_access_key=secret_key,
26
+ endpoint_url=endpoint_url,
27
+ config=Config(
28
+ signature_version="s3v4",
29
+ # Note that BackBlase has a boko3 bug where it doesn't support the new
30
+ # checksum header, the following line was an attempt of fix it on the newest
31
+ # version of boto3, but it didn't work.
32
+ # s3={"payload_signing_enabled": False}, # Disable checksum header
33
+ ),
34
+ )
35
+
36
+
37
+ def _create_unknown_s3_client(creds: S3Credentials) -> BaseClient:
38
+ """Create and return an S3 client."""
39
+ access_key = creds.access_key_id
40
+ secret_key = creds.secret_access_key
41
+ endpoint_url = creds.endpoint_url
42
+ if (endpoint_url is not None) and not (endpoint_url.startswith("http")):
43
+ warnings.warn(f"Endpoint URL is schema niaive: {endpoint_url}, assuming HTTPS")
44
+ endpoint_url = f"https://{endpoint_url}"
45
+
46
+ session = boto3.session.Session() # type: ignore
47
+ return session.client(
48
+ service_name="s3",
49
+ aws_access_key_id=access_key,
50
+ aws_secret_access_key=secret_key,
51
+ endpoint_url=endpoint_url,
52
+ config=Config(
53
+ signature_version="s3v4",
54
+ region_name=creds.region_name,
55
+ # Note that BackBlase has a boko3 bug where it doesn't support the new
56
+ # checksum header, the following line was an attempt of fix it on the newest
57
+ # version of boto3, but it didn't work.
58
+ # s3={"payload_signing_enabled": False}, # Disable checksum header
59
+ ),
60
+ )
61
+
62
+
63
+ def create_s3_client(credentials: S3Credentials) -> BaseClient:
64
+ """Create and return an S3 client."""
65
+ provider = credentials.provider
66
+ if provider == S3Provider.BACKBLAZE:
67
+ return _create_backblaze_s3_client(credentials)
68
+ else:
69
+ return _create_unknown_s3_client(credentials)
rclone_api/s3/types.py ADDED
@@ -0,0 +1,58 @@
1
+ from dataclasses import dataclass
2
+ from enum import Enum
3
+ from pathlib import Path
4
+
5
+
6
+ class S3Provider(Enum):
7
+ S3 = "s3" # generic S3
8
+ BACKBLAZE = "b2"
9
+ DIGITAL_OCEAN = "DigitalOcean"
10
+
11
+ @staticmethod
12
+ def from_str(value: str) -> "S3Provider":
13
+ """Convert string to S3Provider."""
14
+ if value == "b2":
15
+ return S3Provider.BACKBLAZE
16
+ if value == "DigitalOcean":
17
+ return S3Provider.DIGITAL_OCEAN
18
+ raise ValueError(f"Unknown S3Provider: {value}")
19
+
20
+
21
+ @dataclass
22
+ class S3Credentials:
23
+ """Credentials for accessing S3."""
24
+
25
+ provider: S3Provider
26
+ access_key_id: str
27
+ secret_access_key: str
28
+ session_token: str | None = None
29
+ region_name: str | None = None
30
+ endpoint_url: str | None = None
31
+
32
+
33
+ @dataclass
34
+ class S3UploadTarget:
35
+ """Target information for S3 upload."""
36
+
37
+ src_file: Path
38
+ bucket_name: str
39
+ s3_key: str
40
+
41
+
42
+ @dataclass
43
+ class S3MutliPartUploadConfig:
44
+ """Input for multi-part upload."""
45
+
46
+ chunk_size: int
47
+ retries: int
48
+ resume_path_json: Path
49
+ max_chunks_before_suspension: int | None = None
50
+ mount_path: Path | None = None # If set this will be used to mount the src file, otherwise it's one is chosen automatically
51
+
52
+
53
+
54
+ class MultiUploadResult(Enum):
55
+ UPLOADED_FRESH = 1
56
+ UPLOADED_RESUME = 2
57
+ SUSPENDED = 3
58
+ ALREADY_DONE = 4
rclone_api/types.py CHANGED
@@ -19,9 +19,11 @@ class Order(Enum):
19
19
  RANDOM = "random"
20
20
 
21
21
 
22
- # class GroupingOption(Enum):
23
- # BUCKET = "bucket"
24
- # REMOTE = "remote"
22
+ @dataclass
23
+ class S3PathInfo:
24
+ remote: str
25
+ bucket: str
26
+ key: str
25
27
 
26
28
 
27
29
  @dataclass
rclone_api/util.py CHANGED
@@ -11,6 +11,7 @@ from rclone_api.config import Config
11
11
  from rclone_api.dir import Dir
12
12
  from rclone_api.remote import Remote
13
13
  from rclone_api.rpath import RPath
14
+ from rclone_api.types import S3PathInfo
14
15
 
15
16
  # from .rclone import Rclone
16
17
 
@@ -126,7 +127,7 @@ def rclone_execute(
126
127
  print(f"Error cleaning up tempdir: {e}")
127
128
 
128
129
 
129
- def wait_for_mount(path: Path, mount_process: Any, timeout: int = 60) -> None:
130
+ def wait_for_mount(path: Path, mount_process: Any, timeout: int = 10) -> None:
130
131
  from rclone_api.process import Process
131
132
 
132
133
  assert isinstance(mount_process, Process)
@@ -134,7 +135,34 @@ def wait_for_mount(path: Path, mount_process: Any, timeout: int = 60) -> None:
134
135
  while time.time() < expire_time:
135
136
  rtn = mount_process.poll()
136
137
  if rtn is not None:
137
- raise subprocess.CalledProcessError(rtn, mount_process.cmd)
138
+ cmd_str = subprocess.list2cmdline(mount_process.cmd)
139
+ raise subprocess.CalledProcessError(rtn, cmd_str)
138
140
  if path.exists():
139
- return
140
- raise TimeoutError(f"Path {path} did not exist after {timeout} seconds")
141
+ # how many files?
142
+ dircontents = os.listdir(str(path))
143
+ if len(dircontents) > 0:
144
+ print(f"Mount point {path}, waiting 5 seconds for files to appear.")
145
+ time.sleep(5)
146
+ return
147
+ time.sleep(1)
148
+
149
+
150
+ def split_s3_path(path: str) -> S3PathInfo:
151
+ if ":" not in path:
152
+ raise ValueError(f"Invalid S3 path: {path}")
153
+
154
+ prts = path.split(":", 1)
155
+ remote = prts[0]
156
+ path = prts[1]
157
+ parts: list[str] = []
158
+ for part in path.split("/"):
159
+ part = part.strip()
160
+ if part:
161
+ parts.append(part)
162
+ if len(parts) < 2:
163
+ raise ValueError(f"Invalid S3 path: {path}")
164
+ bucket = parts[0]
165
+ key = "/".join(parts[1:])
166
+ assert bucket
167
+ assert key
168
+ return S3PathInfo(remote=remote, bucket=bucket, key=key)
@@ -1,9 +1,8 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.0.88
3
+ Version: 1.0.90
4
4
  Summary: rclone api in python
5
5
  Home-page: https://github.com/zackees/rclone-api
6
- Maintainer: Zachary Vorhies
7
6
  License: BSD 3-Clause License
8
7
  Keywords: template-python-cmd
9
8
  Classifier: Programming Language :: Python :: 3
@@ -12,8 +11,8 @@ Description-Content-Type: text/markdown
12
11
  License-File: LICENSE
13
12
  Requires-Dist: pyright>=1.1.393
14
13
  Requires-Dist: python-dotenv>=1.0.0
14
+ Requires-Dist: boto3<=1.35.99,>=1.20.1
15
15
  Dynamic: home-page
16
- Dynamic: maintainer
17
16
 
18
17
  # rclone-api
19
18
 
@@ -0,0 +1,35 @@
1
+ rclone_api/__init__.py,sha256=m7hQkaIsn9grAlziu9nvDlOIQY6VflYFwC1c_g0qCJo,833
2
+ rclone_api/cli.py,sha256=dibfAZIh0kXWsBbfp3onKLjyZXo54mTzDjUdzJlDlWo,231
3
+ rclone_api/completed_process.py,sha256=_IZ8IWK7DM1_tsbDEkH6wPZ-bbcrgf7A7smls854pmg,1775
4
+ rclone_api/config.py,sha256=f6jEAxVorGFr31oHfcsu5AJTtOJj2wR5tTSsbGGZuIw,2558
5
+ rclone_api/convert.py,sha256=Mx9Qo7zhkOedJd8LdhPvNGHp8znJzOk4f_2KWnoGc78,1012
6
+ rclone_api/deprecated.py,sha256=qWKpnZdYcBK7YQZKuVoWWXDwi-uqiAtbjgPcci_efow,590
7
+ rclone_api/diff.py,sha256=tMoJMAGmLSE6Q_7QhPf6PnCzb840djxMZtDmhc2GlGQ,5227
8
+ rclone_api/dir.py,sha256=i4h7LX5hB_WmVixxDRWL_l1nifvscrdWct_8Wx7wHZc,3540
9
+ rclone_api/dir_listing.py,sha256=GoziW8Sne6FY90MLNcb2aO3aaa3jphB6H8ExYrV0Ryo,1882
10
+ rclone_api/exec.py,sha256=1ovvaMXDEfLiT7BrYZyE85u_yFhEUwUNW3jPOzqknR8,1023
11
+ rclone_api/file.py,sha256=EP5yT2dZ0H2p7CY5n0y5k5pHhIliV25pm8KOwBklUTk,1863
12
+ rclone_api/filelist.py,sha256=xbiusvNgaB_b_kQOZoHMJJxn6TWGtPrWd2J042BI28o,767
13
+ rclone_api/group_files.py,sha256=H92xPW9lQnbNw5KbtZCl00bD6iRh9yRbCuxku4j_3dg,8036
14
+ rclone_api/process.py,sha256=RrMfTe0bndmJ6gBK67ioqNvCstJ8aTC8RlGX1XBLlcw,4191
15
+ rclone_api/rclone.py,sha256=W4CIhDEPJqY5X9-HXKp-DTVLhU5H8YUOjGYAHDVy-yU,41207
16
+ rclone_api/remote.py,sha256=O9WDUFQy9f6oT1HdUbTixK2eg0xtBBm8k4Xl6aa6K00,431
17
+ rclone_api/rpath.py,sha256=8ZA_1wxWtskwcy0I8V2VbjKDmzPkiWd8Q2JQSvh-sYE,2586
18
+ rclone_api/scan_missing_folders.py,sha256=Kulca2Q6WZodt00ATFHkmqqInuoPvBkhTcS9703y6po,4740
19
+ rclone_api/types.py,sha256=NC3e78aXCx-sEQ-FqEaC9KzaJDdJhJrKa4Nwum_-Db0,563
20
+ rclone_api/util.py,sha256=4bJF1CC8J2NW6UbJU3oSxmTJgN04uTovABZJn7c3-zk,5025
21
+ rclone_api/walk.py,sha256=-54NVE8EJcCstwDoaC_UtHm73R2HrZwVwQmsnv55xNU,3369
22
+ rclone_api/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
23
+ rclone_api/cmd/copy_large_s3.py,sha256=FPU0S1Y9pApVLmWcdMT_3llywjEtOtwobXTnMXE-FhY,2690
24
+ rclone_api/cmd/list_files.py,sha256=x8FHODEilwKqwdiU1jdkeJbLwOqUkUQuDWPo2u_zpf0,741
25
+ rclone_api/s3/api.py,sha256=Ju9A6uNHON2by2jJPtH-awe-3d588QLh5E-vjdV4kOY,2729
26
+ rclone_api/s3/basic_ops.py,sha256=hK3366xhVEzEcjz9Gk_8lFx6MRceAk72cax6mUrr6ko,2104
27
+ rclone_api/s3/chunk_uploader.py,sha256=Wcm4h_A6ORaJvT9P9qSeknbJYch22RKZ4FGo1iePlAw,17385
28
+ rclone_api/s3/create.py,sha256=X4mgjekgAoyHNeXnznHNS63DFViAhI0p7-fXDxml0y4,2701
29
+ rclone_api/s3/types.py,sha256=BKvNkAFjLJK7Zd9xQya8gdjqJPLqdCPZSXD8GeFyP1A,1346
30
+ rclone_api-1.0.90.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
31
+ rclone_api-1.0.90.dist-info/METADATA,sha256=7Y-NCkQi9yBFGqd90T-j9oA2rvA0rEu_-znW5LW_dZY,4479
32
+ rclone_api-1.0.90.dist-info/WHEEL,sha256=rF4EZyR2XVS6irmOHQIJx2SUqXLZKRMUrjsg8UwN-XQ,109
33
+ rclone_api-1.0.90.dist-info/entry_points.txt,sha256=6eNqTRXKhVf8CpWNjXiOa_0Du9tHiW_HD2iQSXRsUg8,132
34
+ rclone_api-1.0.90.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
35
+ rclone_api-1.0.90.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.0)
2
+ Generator: setuptools (75.8.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py2-none-any
5
5
  Tag: py3-none-any
@@ -1,2 +1,3 @@
1
1
  [console_scripts]
2
+ rclone-api-copylarge-s3 = rclone_api.cmd.copy_large_s3:main
2
3
  rclone-api-listfiles = rclone_api.cmd.list_files:main
@@ -1,29 +0,0 @@
1
- rclone_api/__init__.py,sha256=UH1aIcovOSoiWwubbgBs_fvX3YcMhHnLTYJzLnCPKVs,722
2
- rclone_api/cli.py,sha256=dibfAZIh0kXWsBbfp3onKLjyZXo54mTzDjUdzJlDlWo,231
3
- rclone_api/completed_process.py,sha256=_IZ8IWK7DM1_tsbDEkH6wPZ-bbcrgf7A7smls854pmg,1775
4
- rclone_api/config.py,sha256=tP6cU9DnCCEIRc_KP9HPur1jFLLg2QGFSxNwFm6_MVw,118
5
- rclone_api/convert.py,sha256=Mx9Qo7zhkOedJd8LdhPvNGHp8znJzOk4f_2KWnoGc78,1012
6
- rclone_api/deprecated.py,sha256=qWKpnZdYcBK7YQZKuVoWWXDwi-uqiAtbjgPcci_efow,590
7
- rclone_api/diff.py,sha256=tMoJMAGmLSE6Q_7QhPf6PnCzb840djxMZtDmhc2GlGQ,5227
8
- rclone_api/dir.py,sha256=i4h7LX5hB_WmVixxDRWL_l1nifvscrdWct_8Wx7wHZc,3540
9
- rclone_api/dir_listing.py,sha256=GoziW8Sne6FY90MLNcb2aO3aaa3jphB6H8ExYrV0Ryo,1882
10
- rclone_api/exec.py,sha256=1ovvaMXDEfLiT7BrYZyE85u_yFhEUwUNW3jPOzqknR8,1023
11
- rclone_api/file.py,sha256=EP5yT2dZ0H2p7CY5n0y5k5pHhIliV25pm8KOwBklUTk,1863
12
- rclone_api/filelist.py,sha256=xbiusvNgaB_b_kQOZoHMJJxn6TWGtPrWd2J042BI28o,767
13
- rclone_api/group_files.py,sha256=O8Awod4_ILGB4RfatBmiG4vXy9eO0rBhbEaZPr6X_sY,7969
14
- rclone_api/process.py,sha256=RrMfTe0bndmJ6gBK67ioqNvCstJ8aTC8RlGX1XBLlcw,4191
15
- rclone_api/rclone.py,sha256=lfdOwUiFyXkiLNISkfb4_FIWwlmTo5Vgqfy9sS9by0s,34059
16
- rclone_api/remote.py,sha256=O9WDUFQy9f6oT1HdUbTixK2eg0xtBBm8k4Xl6aa6K00,431
17
- rclone_api/rpath.py,sha256=8ZA_1wxWtskwcy0I8V2VbjKDmzPkiWd8Q2JQSvh-sYE,2586
18
- rclone_api/scan_missing_folders.py,sha256=Kulca2Q6WZodt00ATFHkmqqInuoPvBkhTcS9703y6po,4740
19
- rclone_api/types.py,sha256=9-qY0Z1NFwiIrvV3e4Ty-dVfed1I_482jPohJ-2hs-o,567
20
- rclone_api/util.py,sha256=XMrA2m_di4h8JTM-qyx2iyrFZn-l-or_SJOa5tEsDsI,4200
21
- rclone_api/walk.py,sha256=-54NVE8EJcCstwDoaC_UtHm73R2HrZwVwQmsnv55xNU,3369
22
- rclone_api/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
23
- rclone_api/cmd/list_files.py,sha256=x8FHODEilwKqwdiU1jdkeJbLwOqUkUQuDWPo2u_zpf0,741
24
- rclone_api-1.0.88.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
25
- rclone_api-1.0.88.dist-info/METADATA,sha256=1kuxxPVsUsTpNgzSy6clP88Cmt7s8Uxi8yEUn45uu24,4489
26
- rclone_api-1.0.88.dist-info/WHEEL,sha256=9Hm2OB-j1QcCUq9Jguht7ayGIIZBRTdOXD1qg9cCgPM,109
27
- rclone_api-1.0.88.dist-info/entry_points.txt,sha256=XUoTX3m7CWxdj2VAKhEuO0NMOfX2qf-OcEDFwdyk9ZE,72
28
- rclone_api-1.0.88.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
29
- rclone_api-1.0.88.dist-info/RECORD,,