plexus-python-common 1.0.34__tar.gz → 1.0.35__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.
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/PKG-INFO +2 -3
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/pyproject.toml +2 -4
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/utils/s3utils.py +148 -115
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus_python_common.egg-info/PKG-INFO +2 -3
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus_python_common.egg-info/requires.txt +1 -2
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/test/plexus_tests/common/utils/s3utils_test.py +16 -16
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/.editorconfig +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/.github/workflows/pr.yml +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/.github/workflows/push.yml +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/.gitignore +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/MANIFEST.in +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/README.md +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/VERSION +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/jsonutils/dummy.0.jsonl +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/jsonutils/dummy.1.jsonl +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/jsonutils/dummy.2.jsonl +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/s3utils/dir.baz/file.bar.baz +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/s3utils/dir.baz/file.foo.bar +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/s3utils/dir.baz/file.foo.baz +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/s3utils/dir.foo/dir.foo.bar/dir.foo.bar.baz/file.foo.bar.baz +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/s3utils/dir.foo/dir.foo.bar/file.bar.baz +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/s3utils/dir.foo/dir.foo.bar/file.foo.bar +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/s3utils/dir.foo/dir.foo.bar/file.foo.baz +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/s3utils/dir.foo/file.bar +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/s3utils/dir.foo/file.baz +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/s3utils/dir.foo/file.foo +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/s3utils_archive/archive.compressed.zip +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/s3utils_archive/archive.uncompressed.zip +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/shutils/0-dummy +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/shutils/1-dummy +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/shutils/2-dummy +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.0.0.jsonl +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.0.0.vol-0.jsonl +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.0.jsonl +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.1.1.jsonl +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.1.1.vol-1.jsonl +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.1.jsonl +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.2.2.jsonl +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.2.2.vol-2.jsonl +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.2.jsonl +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.csv.part0 +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.csv.part1 +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.csv.part2 +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.txt +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/setup.cfg +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/setup.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/__init__.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/carto/OSMFile.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/carto/OSMNode.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/carto/OSMTags.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/carto/OSMWay.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/carto/__init__.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/pose.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/proj.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/utils/__init__.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/utils/apiutils.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/utils/bagutils.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/utils/config.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/utils/datautils.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/utils/jsonutils.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/utils/ormutils.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/utils/shutils.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/utils/sqlutils.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/utils/strutils.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/utils/testutils.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus_python_common.egg-info/SOURCES.txt +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus_python_common.egg-info/dependency_links.txt +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus_python_common.egg-info/not-zip-safe +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus_python_common.egg-info/top_level.txt +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/test/plexus_test.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/test/plexus_tests/__init__.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/test/plexus_tests/common/carto/osm_file_test.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/test/plexus_tests/common/carto/osm_tags_test.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/test/plexus_tests/common/pose_test.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/test/plexus_tests/common/proj_test.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/test/plexus_tests/common/utils/bagutils_test.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/test/plexus_tests/common/utils/datautils_test.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/test/plexus_tests/common/utils/jsonutils_test.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/test/plexus_tests/common/utils/ormutils_test.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/test/plexus_tests/common/utils/shutils_test.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/test/plexus_tests/common/utils/strutils_test.py +0 -0
- {plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/test/plexus_tests/common/utils/testutils_test.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plexus-python-common
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.35
|
|
4
4
|
Classifier: Programming Language :: Python :: 3
|
|
5
5
|
Classifier: Programming Language :: Python :: 3.12
|
|
6
6
|
Classifier: Programming Language :: Python :: 3.13
|
|
@@ -8,8 +8,7 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
8
8
|
Requires-Python: <3.15,>=3.12
|
|
9
9
|
Requires-Dist: aiobotocore<2.27,>=2.26
|
|
10
10
|
Requires-Dist: boto3<1.42,>=1.41
|
|
11
|
-
Requires-Dist: boto3-stubs<1.42,>=1.41
|
|
12
|
-
Requires-Dist: cloudpathlib>=0.21
|
|
11
|
+
Requires-Dist: boto3-stubs[s3]<1.42,>=1.41
|
|
13
12
|
Requires-Dist: fsspec>=2025.10
|
|
14
13
|
Requires-Dist: lxml>=6.0
|
|
15
14
|
Requires-Dist: numpy>=2.3
|
|
@@ -10,8 +10,7 @@ build-backend = "setuptools.build_meta"
|
|
|
10
10
|
dev = [
|
|
11
11
|
"aiobotocore>=2.26,<2.27",
|
|
12
12
|
"boto3>=1.41,<1.42",
|
|
13
|
-
"boto3-stubs>=1.41,<1.42",
|
|
14
|
-
"cloudpathlib>=0.21",
|
|
13
|
+
"boto3-stubs[s3]>=1.41,<1.42",
|
|
15
14
|
"fsspec>=2025.10",
|
|
16
15
|
"lxml>=6.0",
|
|
17
16
|
"numpy>=2.3",
|
|
@@ -53,8 +52,7 @@ classifiers = [
|
|
|
53
52
|
dependencies = [
|
|
54
53
|
"aiobotocore>=2.26,<2.27",
|
|
55
54
|
"boto3>=1.41,<1.42",
|
|
56
|
-
"boto3-stubs>=1.41,<1.42",
|
|
57
|
-
"cloudpathlib>=0.21",
|
|
55
|
+
"boto3-stubs[s3]>=1.41,<1.42",
|
|
58
56
|
"fsspec>=2025.10",
|
|
59
57
|
"lxml>=6.0",
|
|
60
58
|
"numpy>=2.3",
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/utils/s3utils.py
RENAMED
|
@@ -13,21 +13,21 @@ import typing
|
|
|
13
13
|
import zipfile
|
|
14
14
|
import zlib
|
|
15
15
|
from collections.abc import Callable, Generator
|
|
16
|
-
from pathlib import Path
|
|
17
16
|
from typing import Any, Literal
|
|
18
17
|
|
|
19
18
|
import boto3
|
|
20
19
|
import fsspec
|
|
21
20
|
import fsspec.utils
|
|
22
|
-
from cloudpathlib import CloudPath, S3Client, S3Path
|
|
23
21
|
from iker.common.utils.sequtils import chunk_between, head, last
|
|
24
22
|
from iker.common.utils.shutils import glob_match, listfile, path_depth
|
|
25
23
|
from iker.common.utils.strutils import is_empty, trim_to_none
|
|
26
|
-
from
|
|
24
|
+
from mypy_boto3_s3 import S3Client
|
|
25
|
+
from rich.progress import BarColumn, DownloadColumn, Progress, TextColumn, TransferSpeedColumn
|
|
27
26
|
|
|
28
27
|
__all__ = [
|
|
29
28
|
"S3ObjectMeta",
|
|
30
29
|
"s3_make_client",
|
|
30
|
+
"s3_head_object",
|
|
31
31
|
"s3_list_objects",
|
|
32
32
|
"s3_listfile",
|
|
33
33
|
"s3_cp_download",
|
|
@@ -36,10 +36,8 @@ __all__ = [
|
|
|
36
36
|
"s3_sync_upload",
|
|
37
37
|
"s3_pull_text",
|
|
38
38
|
"s3_push_text",
|
|
39
|
-
"S3TransferCallbackClient",
|
|
40
|
-
"ArchiveMemberChunk",
|
|
41
|
-
"s3_make_progress_callback",
|
|
42
39
|
"s3_make_progressed_client",
|
|
40
|
+
"ArchiveMemberChunk",
|
|
43
41
|
"s3_archive_member_tree",
|
|
44
42
|
"s3_archive_listfile",
|
|
45
43
|
"s3_archive_open_member",
|
|
@@ -84,12 +82,28 @@ def s3_make_client(
|
|
|
84
82
|
session = boto3.Session(aws_access_key_id=trim_to_none(access_key_id),
|
|
85
83
|
aws_secret_access_key=trim_to_none(secret_access_key),
|
|
86
84
|
region_name=trim_to_none(region_name))
|
|
87
|
-
client =
|
|
85
|
+
client = session.client("s3", endpoint_url=trim_to_none(endpoint_url))
|
|
88
86
|
try:
|
|
89
87
|
yield client
|
|
90
88
|
finally:
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
client.close()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def s3_head_object(
|
|
93
|
+
client: S3Client,
|
|
94
|
+
bucket: str,
|
|
95
|
+
key: str,
|
|
96
|
+
) -> S3ObjectMeta:
|
|
97
|
+
"""
|
|
98
|
+
Retrieves metadata of an object from the given S3 ``bucket`` and ``key``.
|
|
99
|
+
|
|
100
|
+
:param client: An instance of ``S3Client``.
|
|
101
|
+
:param bucket: Bucket name.
|
|
102
|
+
:param key: Object key.
|
|
103
|
+
:return: An ``S3ObjectMeta`` object representing the S3 object.
|
|
104
|
+
"""
|
|
105
|
+
response = client.head_object(Bucket=bucket, Key=key)
|
|
106
|
+
return S3ObjectMeta(key=key, last_modified=response["LastModified"], size=response["ContentLength"])
|
|
93
107
|
|
|
94
108
|
|
|
95
109
|
def s3_list_objects(
|
|
@@ -111,12 +125,12 @@ def s3_list_objects(
|
|
|
111
125
|
count = 0
|
|
112
126
|
while True:
|
|
113
127
|
if is_empty(continuation_token):
|
|
114
|
-
response = client.
|
|
128
|
+
response = client.list_objects_v2(MaxKeys=1000, Bucket=bucket, Prefix=prefix)
|
|
115
129
|
else:
|
|
116
|
-
response = client.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
130
|
+
response = client.list_objects_v2(MaxKeys=1000,
|
|
131
|
+
Bucket=bucket,
|
|
132
|
+
Prefix=prefix,
|
|
133
|
+
ContinuationToken=continuation_token)
|
|
120
134
|
|
|
121
135
|
contents = response.get("Contents", [])
|
|
122
136
|
count += len(contents)
|
|
@@ -175,7 +189,7 @@ def s3_cp_download(client: S3Client, bucket: str, key: str, file_path: str | os.
|
|
|
175
189
|
:param key: Object key.
|
|
176
190
|
:param file_path: Local file path to save the object.
|
|
177
191
|
"""
|
|
178
|
-
client.
|
|
192
|
+
client.download_file(bucket, key, file_path)
|
|
179
193
|
|
|
180
194
|
|
|
181
195
|
def s3_cp_upload(client: S3Client, file_path: str | os.PathLike[str], bucket: str, key: str):
|
|
@@ -188,10 +202,10 @@ def s3_cp_upload(client: S3Client, file_path: str | os.PathLike[str], bucket: st
|
|
|
188
202
|
:param key: Object key for the uploaded file.
|
|
189
203
|
"""
|
|
190
204
|
t, _ = mimetypes.MimeTypes().guess_type(file_path)
|
|
191
|
-
client.
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
205
|
+
client.upload_file(file_path,
|
|
206
|
+
bucket,
|
|
207
|
+
key,
|
|
208
|
+
ExtraArgs={"ContentType": "binary/octet-stream" if t is None else t})
|
|
195
209
|
|
|
196
210
|
|
|
197
211
|
def s3_sync_download(
|
|
@@ -311,7 +325,7 @@ def s3_pull_text(client: S3Client, bucket: str, key: str, encoding: str = None)
|
|
|
311
325
|
:return: The decoded text content.
|
|
312
326
|
"""
|
|
313
327
|
with tempfile.TemporaryFile() as fp:
|
|
314
|
-
client.
|
|
328
|
+
client.download_fileobj(bucket, key, fp)
|
|
315
329
|
fp.seek(0)
|
|
316
330
|
return fp.read().decode(encoding or "utf-8")
|
|
317
331
|
|
|
@@ -329,95 +343,111 @@ def s3_push_text(client: S3Client, text: str, bucket: str, key: str, encoding: s
|
|
|
329
343
|
with tempfile.TemporaryFile() as fp:
|
|
330
344
|
fp.write(text.encode(encoding or "utf-8"))
|
|
331
345
|
fp.seek(0)
|
|
332
|
-
client.
|
|
346
|
+
client.upload_fileobj(fp, bucket, key)
|
|
333
347
|
|
|
334
348
|
|
|
335
|
-
|
|
336
|
-
|
|
349
|
+
class S3ClientProgressProxy(object):
|
|
350
|
+
def __init__(self, client: S3Client, progress: Progress):
|
|
351
|
+
self.client = client
|
|
352
|
+
self.progress = progress
|
|
337
353
|
|
|
354
|
+
@contextlib.contextmanager
|
|
355
|
+
def make_transfer_callback(self, key: str, bytes_total: int, direction: Literal["download", "upload"]):
|
|
356
|
+
task_id = self.progress.add_task(direction, total=bytes_total, key=key)
|
|
357
|
+
try:
|
|
358
|
+
yield lambda bytes_sent: self.progress.update(task_id, advance=bytes_sent)
|
|
359
|
+
finally:
|
|
360
|
+
self.progress.remove_task(task_id)
|
|
338
361
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
callback: Callable[[CloudPath, TransferDirection, TransferState, int], None],
|
|
342
|
-
path: Path | CloudPath,
|
|
343
|
-
direction: TransferDirection,
|
|
344
|
-
):
|
|
345
|
-
if callback is None:
|
|
346
|
-
yield None
|
|
347
|
-
return
|
|
348
|
-
|
|
349
|
-
callback(path, direction, "start", 0)
|
|
350
|
-
try:
|
|
351
|
-
yield functools.partial(callback, path, direction, "update")
|
|
352
|
-
finally:
|
|
353
|
-
callback(path, direction, "stop", 0)
|
|
362
|
+
def __getattr__(self, name):
|
|
363
|
+
return getattr(self.client, name)
|
|
354
364
|
|
|
355
|
-
|
|
356
|
-
class S3TransferCallbackClient(S3Client):
|
|
357
|
-
def __init__(
|
|
365
|
+
def download_file(
|
|
358
366
|
self,
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
367
|
+
Bucket,
|
|
368
|
+
Key,
|
|
369
|
+
Filename,
|
|
370
|
+
ExtraArgs=None,
|
|
371
|
+
Callback=None,
|
|
372
|
+
Config=None,
|
|
362
373
|
):
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
374
|
+
object_meta = s3_head_object(self.client, Bucket, Key)
|
|
375
|
+
with (
|
|
376
|
+
contextlib.nullcontext(Callback) if Callback is not None
|
|
377
|
+
else self.make_transfer_callback(Key, object_meta.size, "download")
|
|
378
|
+
) as callback:
|
|
379
|
+
return self.client.download_file(Bucket,
|
|
380
|
+
Key,
|
|
381
|
+
Filename,
|
|
382
|
+
ExtraArgs=ExtraArgs,
|
|
383
|
+
Callback=callback,
|
|
384
|
+
Config=Config)
|
|
385
|
+
|
|
386
|
+
def download_fileobj(
|
|
387
|
+
self,
|
|
388
|
+
Bucket,
|
|
389
|
+
Key,
|
|
390
|
+
Fileobj,
|
|
391
|
+
ExtraArgs=None,
|
|
392
|
+
Callback=None,
|
|
393
|
+
Config=None,
|
|
394
|
+
):
|
|
395
|
+
object_meta = s3_head_object(self.client, Bucket, Key)
|
|
396
|
+
with (
|
|
397
|
+
contextlib.nullcontext(Callback) if Callback is not None
|
|
398
|
+
else self.make_transfer_callback(Key, object_meta.size, "download")
|
|
399
|
+
) as callback:
|
|
400
|
+
return self.client.download_fileobj(Bucket,
|
|
401
|
+
Key,
|
|
402
|
+
Fileobj,
|
|
403
|
+
ExtraArgs=ExtraArgs,
|
|
404
|
+
Callback=callback,
|
|
405
|
+
Config=Config)
|
|
406
|
+
|
|
407
|
+
def upload_file(
|
|
408
|
+
self,
|
|
409
|
+
Filename,
|
|
410
|
+
Bucket,
|
|
411
|
+
Key,
|
|
412
|
+
ExtraArgs=None,
|
|
413
|
+
Callback=None,
|
|
414
|
+
Config=None,
|
|
415
|
+
):
|
|
416
|
+
bytes_total = os.path.getsize(Filename)
|
|
417
|
+
with (
|
|
418
|
+
contextlib.nullcontext(Callback) if Callback is not None
|
|
419
|
+
else self.make_transfer_callback(Key, bytes_total, "upload")
|
|
420
|
+
) as callback:
|
|
421
|
+
return self.client.upload_file(Filename,
|
|
422
|
+
Bucket,
|
|
423
|
+
Key,
|
|
424
|
+
ExtraArgs=ExtraArgs,
|
|
425
|
+
Callback=callback,
|
|
426
|
+
Config=Config)
|
|
427
|
+
|
|
428
|
+
def upload_fileobj(
|
|
429
|
+
self,
|
|
430
|
+
Fileobj,
|
|
431
|
+
Bucket,
|
|
432
|
+
Key,
|
|
433
|
+
ExtraArgs=None,
|
|
434
|
+
Callback=None,
|
|
435
|
+
Config=None,
|
|
436
|
+
):
|
|
437
|
+
current_pos = Fileobj.tell()
|
|
438
|
+
Fileobj.seek(0, os.SEEK_END)
|
|
439
|
+
bytes_total = Fileobj.tell() - current_pos
|
|
440
|
+
Fileobj.seek(current_pos)
|
|
441
|
+
with (
|
|
442
|
+
contextlib.nullcontext(Callback) if Callback is not None
|
|
443
|
+
else self.make_transfer_callback(Key, bytes_total, "upload")
|
|
444
|
+
) as callback:
|
|
445
|
+
return self.client.upload_fileobj(Fileobj,
|
|
446
|
+
Bucket,
|
|
447
|
+
Key,
|
|
448
|
+
ExtraArgs=ExtraArgs,
|
|
449
|
+
Callback=callback,
|
|
450
|
+
Config=Config)
|
|
421
451
|
|
|
422
452
|
|
|
423
453
|
if typing.TYPE_CHECKING:
|
|
@@ -435,18 +465,18 @@ def s3_make_progressed_client(
|
|
|
435
465
|
secret_access_key: str = None,
|
|
436
466
|
region_name: str = None,
|
|
437
467
|
endpoint_url: str = None,
|
|
438
|
-
) -> Generator[
|
|
468
|
+
) -> Generator[S3ClientProgressProxy, None, None]:
|
|
439
469
|
"""
|
|
440
|
-
Creates an S3 client with progress
|
|
470
|
+
Creates an S3 client with progress reporting as a context manager.
|
|
441
471
|
|
|
442
472
|
:param access_key_id: AWS access key ID.
|
|
443
473
|
:param secret_access_key: AWS secret access key.
|
|
444
474
|
:param region_name: AWS service region name.
|
|
445
475
|
:param endpoint_url: AWS service endpoint URL.
|
|
446
|
-
:return: An instance of ``
|
|
476
|
+
:return: An instance of ``S3Client`` with progress reporting.
|
|
447
477
|
"""
|
|
448
478
|
with Progress(
|
|
449
|
-
TextColumn("[blue]{task.fields[
|
|
479
|
+
TextColumn("[blue]{task.fields[key]}"),
|
|
450
480
|
BarColumn(),
|
|
451
481
|
DownloadColumn(),
|
|
452
482
|
TransferSpeedColumn(),
|
|
@@ -454,9 +484,12 @@ def s3_make_progressed_client(
|
|
|
454
484
|
session = boto3.Session(aws_access_key_id=trim_to_none(access_key_id),
|
|
455
485
|
aws_secret_access_key=trim_to_none(secret_access_key),
|
|
456
486
|
region_name=trim_to_none(region_name))
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
487
|
+
client = session.client("s3", endpoint_url=trim_to_none(endpoint_url))
|
|
488
|
+
proxy = S3ClientProgressProxy(client, progress)
|
|
489
|
+
try:
|
|
490
|
+
yield proxy
|
|
491
|
+
finally:
|
|
492
|
+
proxy.close()
|
|
460
493
|
|
|
461
494
|
|
|
462
495
|
def s3_options_from_s3_client(client: S3Client) -> dict[str, Any]:
|
|
@@ -466,12 +499,12 @@ def s3_options_from_s3_client(client: S3Client) -> dict[str, Any]:
|
|
|
466
499
|
:param client: An instance of ``S3Client``.
|
|
467
500
|
:return: A dictionary of S3 connection options.
|
|
468
501
|
"""
|
|
469
|
-
if client
|
|
502
|
+
if client is None:
|
|
470
503
|
return {}
|
|
471
504
|
|
|
472
505
|
s3_options: dict[str, Any] = {}
|
|
473
506
|
|
|
474
|
-
credentials = client.
|
|
507
|
+
credentials = client._request_signer._credentials
|
|
475
508
|
if credentials is not None:
|
|
476
509
|
if credentials.access_key:
|
|
477
510
|
s3_options["key"] = credentials.access_key
|
|
@@ -481,10 +514,10 @@ def s3_options_from_s3_client(client: S3Client) -> dict[str, Any]:
|
|
|
481
514
|
s3_options["token"] = credentials.token
|
|
482
515
|
|
|
483
516
|
client_kwargs = {}
|
|
484
|
-
if client.
|
|
485
|
-
client_kwargs["region_name"] = client.
|
|
486
|
-
if client.
|
|
487
|
-
client_kwargs["endpoint_url"] = client.
|
|
517
|
+
if client.meta.region_name:
|
|
518
|
+
client_kwargs["region_name"] = client.meta.region_name
|
|
519
|
+
if client.meta.endpoint_url:
|
|
520
|
+
client_kwargs["endpoint_url"] = client.meta.endpoint_url
|
|
488
521
|
|
|
489
522
|
if client_kwargs:
|
|
490
523
|
s3_options["client_kwargs"] = client_kwargs
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plexus-python-common
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.35
|
|
4
4
|
Classifier: Programming Language :: Python :: 3
|
|
5
5
|
Classifier: Programming Language :: Python :: 3.12
|
|
6
6
|
Classifier: Programming Language :: Python :: 3.13
|
|
@@ -8,8 +8,7 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
8
8
|
Requires-Python: <3.15,>=3.12
|
|
9
9
|
Requires-Dist: aiobotocore<2.27,>=2.26
|
|
10
10
|
Requires-Dist: boto3<1.42,>=1.41
|
|
11
|
-
Requires-Dist: boto3-stubs<1.42,>=1.41
|
|
12
|
-
Requires-Dist: cloudpathlib>=0.21
|
|
11
|
+
Requires-Dist: boto3-stubs[s3]<1.42,>=1.41
|
|
13
12
|
Requires-Dist: fsspec>=2025.10
|
|
14
13
|
Requires-Dist: lxml>=6.0
|
|
15
14
|
Requires-Dist: numpy>=2.3
|
|
@@ -31,7 +31,7 @@ class S3UtilsTest(unittest.TestCase):
|
|
|
31
31
|
|
|
32
32
|
def test_s3_list_object__random_text_files(self):
|
|
33
33
|
with moto.mock_aws(), s3_make_client() as client:
|
|
34
|
-
client.
|
|
34
|
+
client.create_bucket(Bucket="dummy-bucket")
|
|
35
35
|
|
|
36
36
|
data = []
|
|
37
37
|
for i in range(0, 2000):
|
|
@@ -49,7 +49,7 @@ class S3UtilsTest(unittest.TestCase):
|
|
|
49
49
|
self.assertEqual(s3_pull_text(client, "dummy-bucket", key), text)
|
|
50
50
|
|
|
51
51
|
with moto.mock_aws(), s3_make_progressed_client() as client:
|
|
52
|
-
client.
|
|
52
|
+
client.create_bucket(Bucket="dummy-bucket")
|
|
53
53
|
|
|
54
54
|
data = []
|
|
55
55
|
for i in range(0, 2000):
|
|
@@ -58,17 +58,17 @@ class S3UtilsTest(unittest.TestCase):
|
|
|
58
58
|
data.append((key, text))
|
|
59
59
|
|
|
60
60
|
for key, text in data:
|
|
61
|
-
client
|
|
61
|
+
s3_push_text(client, text, "dummy-bucket", key)
|
|
62
62
|
|
|
63
63
|
result = list(s3_list_objects(client, "dummy-bucket", "dummy_prefix/"))
|
|
64
64
|
|
|
65
65
|
for key, text in data:
|
|
66
66
|
self.assertTrue(any(o.key == key for o in result))
|
|
67
|
-
self.assertEqual(client
|
|
67
|
+
self.assertEqual(s3_pull_text(client, "dummy-bucket", key), text)
|
|
68
68
|
|
|
69
69
|
def test_s3_listfile__random_text_files(self):
|
|
70
70
|
with moto.mock_aws(), s3_make_client() as client:
|
|
71
|
-
client.
|
|
71
|
+
client.create_bucket(Bucket="dummy-bucket")
|
|
72
72
|
|
|
73
73
|
data = []
|
|
74
74
|
for i in range(0, 2000):
|
|
@@ -86,7 +86,7 @@ class S3UtilsTest(unittest.TestCase):
|
|
|
86
86
|
self.assertEqual(s3_pull_text(client, "dummy-bucket", key), text)
|
|
87
87
|
|
|
88
88
|
with moto.mock_aws(), s3_make_progressed_client() as client:
|
|
89
|
-
client.
|
|
89
|
+
client.create_bucket(Bucket="dummy-bucket")
|
|
90
90
|
|
|
91
91
|
data = []
|
|
92
92
|
for i in range(0, 2000):
|
|
@@ -95,13 +95,13 @@ class S3UtilsTest(unittest.TestCase):
|
|
|
95
95
|
data.append((key, text))
|
|
96
96
|
|
|
97
97
|
for key, text in data:
|
|
98
|
-
client
|
|
98
|
+
s3_push_text(client, text, "dummy-bucket", key)
|
|
99
99
|
|
|
100
100
|
result = list(s3_listfile(client, "dummy-bucket", "dummy_prefix/"))
|
|
101
101
|
|
|
102
102
|
for key, text in data:
|
|
103
103
|
self.assertTrue(any(o.key == key for o in result))
|
|
104
|
-
self.assertEqual(client
|
|
104
|
+
self.assertEqual(s3_pull_text(client, "dummy-bucket", key), text)
|
|
105
105
|
|
|
106
106
|
data_s3_listfile = [
|
|
107
107
|
(
|
|
@@ -236,7 +236,7 @@ class S3UtilsTest(unittest.TestCase):
|
|
|
236
236
|
@ddt.unpack
|
|
237
237
|
def test_s3_listfile(self, src, bucket, prefix, include_patterns, exclude_patterns, depth, expect):
|
|
238
238
|
with moto.mock_aws(), s3_make_client() as client:
|
|
239
|
-
client.
|
|
239
|
+
client.create_bucket(Bucket=bucket)
|
|
240
240
|
|
|
241
241
|
s3_sync_upload(client,
|
|
242
242
|
os.path.join(resources_directory, src),
|
|
@@ -394,7 +394,7 @@ class S3UtilsTest(unittest.TestCase):
|
|
|
394
394
|
@ddt.unpack
|
|
395
395
|
def test_s3_sync(self, src, dst, bucket, prefix, include_patterns, exclude_patterns, depth, expect):
|
|
396
396
|
with moto.mock_aws(), s3_make_client() as client:
|
|
397
|
-
client.
|
|
397
|
+
client.create_bucket(Bucket=bucket)
|
|
398
398
|
|
|
399
399
|
s3_sync_upload(client,
|
|
400
400
|
os.path.join(resources_directory, src),
|
|
@@ -414,7 +414,7 @@ class S3UtilsTest(unittest.TestCase):
|
|
|
414
414
|
set(map(lambda x: norm_path(os.path.join(temp_directory, x)), expect)))
|
|
415
415
|
|
|
416
416
|
with moto.mock_aws(), s3_make_client() as client:
|
|
417
|
-
client.
|
|
417
|
+
client.create_bucket(Bucket=bucket)
|
|
418
418
|
|
|
419
419
|
s3_sync_upload(client,
|
|
420
420
|
os.path.join(resources_directory, src),
|
|
@@ -434,7 +434,7 @@ class S3UtilsTest(unittest.TestCase):
|
|
|
434
434
|
set(map(lambda x: norm_path(os.path.join(temp_directory, x)), expect)))
|
|
435
435
|
|
|
436
436
|
with moto.mock_aws(), s3_make_client() as client:
|
|
437
|
-
client.
|
|
437
|
+
client.create_bucket(Bucket=bucket)
|
|
438
438
|
|
|
439
439
|
s3_sync_upload(client,
|
|
440
440
|
os.path.join(resources_directory, src),
|
|
@@ -467,7 +467,7 @@ class S3UtilsTest(unittest.TestCase):
|
|
|
467
467
|
@ddt.unpack
|
|
468
468
|
def test_s3_text(self, bucket, key, text, encoding):
|
|
469
469
|
with moto.mock_aws(), s3_make_client() as client:
|
|
470
|
-
client.
|
|
470
|
+
client.create_bucket(Bucket=bucket)
|
|
471
471
|
|
|
472
472
|
s3_push_text(client, text, bucket, key, encoding=encoding)
|
|
473
473
|
self.assertEqual(s3_pull_text(client, bucket, key, encoding=encoding), text)
|
|
@@ -478,7 +478,7 @@ class S3UtilsTest(unittest.TestCase):
|
|
|
478
478
|
moto_server_endpoint_url() as endpoint_url,
|
|
479
479
|
s3_make_client(endpoint_url=endpoint_url) as client,
|
|
480
480
|
):
|
|
481
|
-
client.
|
|
481
|
+
client.create_bucket(Bucket="dummy-bucket")
|
|
482
482
|
|
|
483
483
|
s3_sync_upload(client,
|
|
484
484
|
os.path.join(resources_directory, "unittest", "s3utils_archive"),
|
|
@@ -538,7 +538,7 @@ class S3UtilsTest(unittest.TestCase):
|
|
|
538
538
|
moto_server_endpoint_url() as endpoint_url,
|
|
539
539
|
s3_make_client(endpoint_url=endpoint_url) as client,
|
|
540
540
|
):
|
|
541
|
-
client.
|
|
541
|
+
client.create_bucket(Bucket="dummy-bucket")
|
|
542
542
|
|
|
543
543
|
s3_sync_upload(client,
|
|
544
544
|
os.path.join(resources_directory, "unittest", "s3utils_archive"),
|
|
@@ -565,7 +565,7 @@ class S3UtilsTest(unittest.TestCase):
|
|
|
565
565
|
moto_server_endpoint_url() as endpoint_url,
|
|
566
566
|
s3_make_client(endpoint_url=endpoint_url) as client,
|
|
567
567
|
):
|
|
568
|
-
client.
|
|
568
|
+
client.create_bucket(Bucket="dummy-bucket")
|
|
569
569
|
|
|
570
570
|
s3_sync_upload(client,
|
|
571
571
|
os.path.join(resources_directory, "unittest", "s3utils_archive"),
|
|
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
|
|
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
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/shutils/0-dummy
RENAMED
|
File without changes
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/shutils/1-dummy
RENAMED
|
File without changes
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/shutils/2-dummy
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.0.jsonl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.1.jsonl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.2.jsonl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/resources/unittest/shutils/dummy.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/carto/OSMFile.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/carto/OSMNode.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/carto/OSMTags.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/carto/OSMWay.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/carto/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/utils/__init__.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/utils/apiutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/utils/bagutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/utils/config.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/utils/datautils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/utils/jsonutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/utils/ormutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/utils/shutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/utils/sqlutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/utils/strutils.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/src/plexus/common/utils/testutils.py
RENAMED
|
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
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/test/plexus_tests/common/pose_test.py
RENAMED
|
File without changes
|
{plexus_python_common-1.0.34 → plexus_python_common-1.0.35}/test/plexus_tests/common/proj_test.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|