python3-commons 0.5.16__tar.gz → 0.5.17__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.
Potentially problematic release.
This version of python3-commons might be problematic. Click here for more details.
- {python3_commons-0.5.16/src/python3_commons.egg-info → python3_commons-0.5.17}/PKG-INFO +1 -1
- {python3_commons-0.5.16 → python3_commons-0.5.17}/src/python3_commons/audit.py +5 -2
- {python3_commons-0.5.16 → python3_commons-0.5.17}/src/python3_commons/helpers.py +22 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/src/python3_commons/object_storage.py +13 -11
- {python3_commons-0.5.16 → python3_commons-0.5.17}/src/python3_commons/serializers/msgspec.py +6 -2
- python3_commons-0.5.17/src/python3_commons/stream_tar.py +102 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17/src/python3_commons.egg-info}/PKG-INFO +1 -1
- {python3_commons-0.5.16 → python3_commons-0.5.17}/src/python3_commons.egg-info/SOURCES.txt +1 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/.coveragerc +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/.github/workflows/python-publish.yaml +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/.gitignore +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/AUTHORS.rst +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/CHANGELOG.rst +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/LICENSE +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/README.md +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/README.rst +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/docs/Makefile +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/docs/_static/.gitignore +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/docs/authors.rst +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/docs/changelog.rst +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/docs/conf.py +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/docs/index.rst +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/docs/license.rst +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/pyproject.toml +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/requirements.txt +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/requirements_dev.txt +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/requirements_test.txt +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/setup.cfg +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/setup.py +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/src/python3_commons/__init__.py +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/src/python3_commons/conf.py +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/src/python3_commons/db.py +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/src/python3_commons/fs.py +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/src/python3_commons/logging/__init__.py +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/src/python3_commons/logging/filters.py +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/src/python3_commons/logging/formatter.py +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/src/python3_commons/serializers/__init__.py +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/src/python3_commons/serializers/json.py +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/src/python3_commons/serializers/msgpack.py +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/src/python3_commons.egg-info/dependency_links.txt +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/src/python3_commons.egg-info/not-zip-safe +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/src/python3_commons.egg-info/requires.txt +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/src/python3_commons.egg-info/top_level.txt +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/tests/conftest.py +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/tests/test_audit.py +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/tests/test_msgpack.py +0 -0
- {python3_commons-0.5.16 → python3_commons-0.5.17}/tests/test_msgspec.py +0 -0
|
@@ -13,7 +13,7 @@ from zeep.wsdl.definitions import AbstractOperation
|
|
|
13
13
|
|
|
14
14
|
from python3_commons import object_storage
|
|
15
15
|
from python3_commons.conf import S3Settings, s3_settings
|
|
16
|
-
from python3_commons.object_storage import
|
|
16
|
+
from python3_commons.object_storage import ObjectStorage
|
|
17
17
|
|
|
18
18
|
logger = logging.getLogger(__name__)
|
|
19
19
|
|
|
@@ -35,6 +35,8 @@ class GeneratedStream(io.BytesIO):
|
|
|
35
35
|
|
|
36
36
|
self.write(chunk)
|
|
37
37
|
|
|
38
|
+
self.seek(0)
|
|
39
|
+
|
|
38
40
|
if chunk := super().read(size):
|
|
39
41
|
pos = self.tell()
|
|
40
42
|
|
|
@@ -64,6 +66,7 @@ def generate_archive(objects: Iterable[tuple[str, datetime, bytes]],
|
|
|
64
66
|
info.size = len(content)
|
|
65
67
|
info.mtime = last_modified.timestamp()
|
|
66
68
|
archive.addfile(info, io.BytesIO(content))
|
|
69
|
+
archive.fileobj.flush()
|
|
67
70
|
|
|
68
71
|
buffer.seek(0)
|
|
69
72
|
|
|
@@ -82,7 +85,7 @@ def generate_archive(objects: Iterable[tuple[str, datetime, bytes]],
|
|
|
82
85
|
def write_audit_data_sync(settings: S3Settings, key: str, data: bytes):
|
|
83
86
|
if settings.s3_secret_access_key:
|
|
84
87
|
try:
|
|
85
|
-
client =
|
|
88
|
+
client = ObjectStorage(settings).get_client()
|
|
86
89
|
absolute_path = object_storage.get_absolute_path(f'audit/{key}')
|
|
87
90
|
|
|
88
91
|
client.put_object(settings.s3_bucket, absolute_path, io.BytesIO(data), len(data))
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import logging
|
|
3
3
|
import shlex
|
|
4
|
+
import threading
|
|
4
5
|
|
|
5
6
|
from decimal import Decimal, ROUND_HALF_UP
|
|
6
7
|
from typing import Mapping
|
|
@@ -8,6 +9,27 @@ from typing import Mapping
|
|
|
8
9
|
logger = logging.getLogger(__name__)
|
|
9
10
|
|
|
10
11
|
|
|
12
|
+
class SingletonMeta(type):
|
|
13
|
+
"""
|
|
14
|
+
A metaclass that creates a Singleton base class when called.
|
|
15
|
+
"""
|
|
16
|
+
_instances = {}
|
|
17
|
+
_lock = threading.Lock()
|
|
18
|
+
|
|
19
|
+
def __call__(cls, *args, **kwargs):
|
|
20
|
+
try:
|
|
21
|
+
return cls._instances[cls]
|
|
22
|
+
except KeyError:
|
|
23
|
+
with cls._lock:
|
|
24
|
+
try:
|
|
25
|
+
return cls._instances[cls]
|
|
26
|
+
except KeyError:
|
|
27
|
+
instance = super(SingletonMeta, cls).__call__(*args, **kwargs)
|
|
28
|
+
cls._instances[cls] = instance
|
|
29
|
+
|
|
30
|
+
return instance
|
|
31
|
+
|
|
32
|
+
|
|
11
33
|
def date_from_string(string: str, fmt: str = '%d.%m.%Y') -> datetime.date:
|
|
12
34
|
try:
|
|
13
35
|
return datetime.datetime.strptime(string, fmt).date()
|
|
@@ -8,16 +8,17 @@ from minio.datatypes import Object
|
|
|
8
8
|
from minio.deleteobjects import DeleteObject, DeleteError
|
|
9
9
|
|
|
10
10
|
from python3_commons.conf import s3_settings, S3Settings
|
|
11
|
+
from python3_commons.helpers import SingletonMeta
|
|
11
12
|
|
|
12
13
|
logger = logging.getLogger(__name__)
|
|
13
|
-
__CLIENT = None
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
class ObjectStorage(metaclass=SingletonMeta):
|
|
17
|
+
def __init__(self, settings: S3Settings):
|
|
18
|
+
if not s3_settings.s3_endpoint_url:
|
|
19
|
+
raise ValueError('s3_settings.s3_endpoint_url must be set')
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
__CLIENT = Minio(
|
|
21
|
+
self._client = Minio(
|
|
21
22
|
settings.s3_endpoint_url,
|
|
22
23
|
region=settings.s3_region_name,
|
|
23
24
|
access_key=settings.s3_access_key_id.get_secret_value(),
|
|
@@ -26,7 +27,8 @@ def get_s3_client(settings: S3Settings) -> Minio:
|
|
|
26
27
|
cert_check=settings.s3_cert_verify
|
|
27
28
|
)
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
def get_client(self) -> Minio:
|
|
31
|
+
return self._client
|
|
30
32
|
|
|
31
33
|
|
|
32
34
|
def get_absolute_path(path: str) -> str:
|
|
@@ -40,7 +42,7 @@ def get_absolute_path(path: str) -> str:
|
|
|
40
42
|
|
|
41
43
|
|
|
42
44
|
def put_object(bucket_name: str, path: str, data: io.BytesIO, length: int, part_size: int = 0) -> str:
|
|
43
|
-
s3_client =
|
|
45
|
+
s3_client = ObjectStorage(s3_settings).get_client()
|
|
44
46
|
|
|
45
47
|
if s3_client:
|
|
46
48
|
result = s3_client.put_object(bucket_name, path, data, length, part_size=part_size)
|
|
@@ -53,7 +55,7 @@ def put_object(bucket_name: str, path: str, data: io.BytesIO, length: int, part_
|
|
|
53
55
|
|
|
54
56
|
|
|
55
57
|
def get_object_stream(bucket_name: str, path: str):
|
|
56
|
-
s3_client =
|
|
58
|
+
s3_client = ObjectStorage(s3_settings).get_client()
|
|
57
59
|
|
|
58
60
|
if s3_client:
|
|
59
61
|
logger.debug(f'Getting object from object storage: {bucket_name}:{path}')
|
|
@@ -85,7 +87,7 @@ def get_object(bucket_name: str, path: str) -> bytes:
|
|
|
85
87
|
|
|
86
88
|
|
|
87
89
|
def list_objects(bucket_name: str, prefix: str, recursive: bool = True) -> Generator[Object, None, None]:
|
|
88
|
-
s3_client =
|
|
90
|
+
s3_client = ObjectStorage(s3_settings).get_client()
|
|
89
91
|
|
|
90
92
|
yield from s3_client.list_objects(bucket_name, prefix=prefix, recursive=recursive)
|
|
91
93
|
|
|
@@ -104,13 +106,13 @@ def get_objects(bucket_name: str, path: str,
|
|
|
104
106
|
|
|
105
107
|
|
|
106
108
|
def remove_object(bucket_name: str, object_name: str):
|
|
107
|
-
s3_client =
|
|
109
|
+
s3_client = ObjectStorage(s3_settings).get_client()
|
|
108
110
|
s3_client.remove_object(bucket_name, object_name)
|
|
109
111
|
|
|
110
112
|
|
|
111
113
|
def remove_objects(bucket_name: str, prefix: str = None,
|
|
112
114
|
object_names: Iterable[str] = None) -> Iterable[DeleteError] | None:
|
|
113
|
-
s3_client =
|
|
115
|
+
s3_client = ObjectStorage(s3_settings).get_client()
|
|
114
116
|
|
|
115
117
|
if prefix:
|
|
116
118
|
delete_object_list = map(
|
{python3_commons-0.5.16 → python3_commons-0.5.17}/src/python3_commons/serializers/msgspec.py
RENAMED
|
@@ -40,8 +40,12 @@ def ext_hook(code: int, data: memoryview) -> Any:
|
|
|
40
40
|
raise NotImplementedError(f'Extension type code {code} is not supported')
|
|
41
41
|
|
|
42
42
|
|
|
43
|
+
MSGPACK_ENCODER = msgpack.Encoder(enc_hook=enc_hook)
|
|
44
|
+
MSGPACK_DECODER = msgpack.Decoder(ext_hook=ext_hook)
|
|
45
|
+
|
|
46
|
+
|
|
43
47
|
def serialize_msgpack(data) -> bytes:
|
|
44
|
-
result =
|
|
48
|
+
result = MSGPACK_ENCODER.encode(data)
|
|
45
49
|
|
|
46
50
|
return result
|
|
47
51
|
|
|
@@ -50,6 +54,6 @@ def deserialize_msgpack(data: bytes, data_type=None):
|
|
|
50
54
|
if data_type:
|
|
51
55
|
result = msgpack.decode(data, type=data_type)
|
|
52
56
|
else:
|
|
53
|
-
result =
|
|
57
|
+
result = MSGPACK_DECODER.decode(data)
|
|
54
58
|
|
|
55
59
|
return result
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import tarfile
|
|
4
|
+
from io import BytesIO
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FileStream(object):
|
|
8
|
+
def __init__(self):
|
|
9
|
+
self.buffer = BytesIO()
|
|
10
|
+
self.offset = 0
|
|
11
|
+
|
|
12
|
+
def write(self, chunk):
|
|
13
|
+
self.buffer.write(chunk)
|
|
14
|
+
self.offset += len(chunk)
|
|
15
|
+
|
|
16
|
+
def tell(self):
|
|
17
|
+
return self.offset
|
|
18
|
+
|
|
19
|
+
def close(self):
|
|
20
|
+
self.buffer.close()
|
|
21
|
+
|
|
22
|
+
def pop(self):
|
|
23
|
+
value = self.buffer.getvalue()
|
|
24
|
+
self.buffer.seek(0)
|
|
25
|
+
self.buffer.truncate()
|
|
26
|
+
|
|
27
|
+
return value
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def stream_build_tar(in_filename, streaming_fp):
|
|
31
|
+
tar = tarfile.TarFile.open(out_filename, 'w|gz', streaming_fp)
|
|
32
|
+
|
|
33
|
+
stat = os.stat(in_filename)
|
|
34
|
+
|
|
35
|
+
tar_info = tarfile.TarInfo(in_filename)
|
|
36
|
+
|
|
37
|
+
# Note that you can get this information from the storage backend,
|
|
38
|
+
# but it's valid for either to raise a NotImplementedError, so it's
|
|
39
|
+
# important to check.
|
|
40
|
+
#
|
|
41
|
+
# Things like the mode or ownership won't be available.
|
|
42
|
+
tar_info.mtime = stat.st_mtime
|
|
43
|
+
tar_info.size = stat.st_size
|
|
44
|
+
|
|
45
|
+
# Note that we don't pass a fileobj, so we don't write any data
|
|
46
|
+
# through addfile. We'll do this ourselves.
|
|
47
|
+
tar.addfile(tar_info)
|
|
48
|
+
|
|
49
|
+
yield
|
|
50
|
+
|
|
51
|
+
with open(in_filename, 'rb') as in_fp:
|
|
52
|
+
total_size = 0
|
|
53
|
+
|
|
54
|
+
while True:
|
|
55
|
+
s = in_fp.read(BLOCK_SIZE)
|
|
56
|
+
|
|
57
|
+
if len(s) > 0:
|
|
58
|
+
tar.fileobj.write(s)
|
|
59
|
+
|
|
60
|
+
yield
|
|
61
|
+
|
|
62
|
+
if len(s) < BLOCK_SIZE:
|
|
63
|
+
blocks, remainder = divmod(tar_info.size, tarfile.BLOCKSIZE)
|
|
64
|
+
|
|
65
|
+
if remainder > 0:
|
|
66
|
+
tar.fileobj.write(tarfile.NUL *
|
|
67
|
+
(tarfile.BLOCKSIZE - remainder))
|
|
68
|
+
|
|
69
|
+
yield
|
|
70
|
+
|
|
71
|
+
blocks += 1
|
|
72
|
+
|
|
73
|
+
tar.offset += blocks * tarfile.BLOCKSIZE
|
|
74
|
+
break
|
|
75
|
+
|
|
76
|
+
tar.close()
|
|
77
|
+
|
|
78
|
+
yield
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
BLOCK_SIZE = 4096
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
if len(sys.argv) != 3:
|
|
85
|
+
print('Usage: %s in_filename out_filename' % sys.argv[0])
|
|
86
|
+
sys.exit(1)
|
|
87
|
+
|
|
88
|
+
in_filename = sys.argv[1]
|
|
89
|
+
out_filename = sys.argv[2]
|
|
90
|
+
|
|
91
|
+
streaming_fp = FileStream()
|
|
92
|
+
|
|
93
|
+
with open(out_filename, 'wb') as out_fp:
|
|
94
|
+
for i in stream_build_tar(in_filename, streaming_fp):
|
|
95
|
+
s = streaming_fp.pop()
|
|
96
|
+
|
|
97
|
+
if len(s) > 0:
|
|
98
|
+
print('Writing %d bytes...' % len(s))
|
|
99
|
+
out_fp.write(s)
|
|
100
|
+
out_fp.flush()
|
|
101
|
+
|
|
102
|
+
print('Wrote tar file to %s' % out_filename)
|
|
@@ -26,6 +26,7 @@ src/python3_commons/db.py
|
|
|
26
26
|
src/python3_commons/fs.py
|
|
27
27
|
src/python3_commons/helpers.py
|
|
28
28
|
src/python3_commons/object_storage.py
|
|
29
|
+
src/python3_commons/stream_tar.py
|
|
29
30
|
src/python3_commons.egg-info/PKG-INFO
|
|
30
31
|
src/python3_commons.egg-info/SOURCES.txt
|
|
31
32
|
src/python3_commons.egg-info/dependency_links.txt
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python3_commons-0.5.16 → python3_commons-0.5.17}/src/python3_commons/serializers/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{python3_commons-0.5.16 → python3_commons-0.5.17}/src/python3_commons/serializers/msgpack.py
RENAMED
|
File without changes
|
{python3_commons-0.5.16 → python3_commons-0.5.17}/src/python3_commons.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python3_commons-0.5.16 → python3_commons-0.5.17}/src/python3_commons.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|