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.
- rclone_api/__init__.py +5 -1
- rclone_api/cmd/copy_large_s3.py +99 -0
- rclone_api/config.py +80 -1
- rclone_api/group_files.py +4 -1
- rclone_api/rclone.py +238 -49
- rclone_api/s3/api.py +73 -0
- rclone_api/s3/basic_ops.py +61 -0
- rclone_api/s3/chunk_uploader.py +538 -0
- rclone_api/s3/create.py +69 -0
- rclone_api/s3/types.py +58 -0
- rclone_api/types.py +5 -3
- rclone_api/util.py +32 -4
- {rclone_api-1.0.88.dist-info → rclone_api-1.0.90.dist-info}/METADATA +2 -3
- rclone_api-1.0.90.dist-info/RECORD +35 -0
- {rclone_api-1.0.88.dist-info → rclone_api-1.0.90.dist-info}/WHEEL +1 -1
- {rclone_api-1.0.88.dist-info → rclone_api-1.0.90.dist-info}/entry_points.txt +1 -0
- rclone_api-1.0.88.dist-info/RECORD +0 -29
- {rclone_api-1.0.88.dist-info → rclone_api-1.0.90.dist-info}/LICENSE +0 -0
- {rclone_api-1.0.88.dist-info → rclone_api-1.0.90.dist-info}/top_level.txt +0 -0
rclone_api/s3/create.py
ADDED
|
@@ -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
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 =
|
|
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
|
-
|
|
138
|
+
cmd_str = subprocess.list2cmdline(mount_process.cmd)
|
|
139
|
+
raise subprocess.CalledProcessError(rtn, cmd_str)
|
|
138
140
|
if path.exists():
|
|
139
|
-
|
|
140
|
-
|
|
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.
|
|
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,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,,
|
|
File without changes
|
|
File without changes
|