python3-commons 0.5.14__tar.gz → 0.5.16__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.14/src/python3_commons.egg-info → python3_commons-0.5.16}/PKG-INFO +3 -3
- {python3_commons-0.5.14 → python3_commons-0.5.16}/requirements.txt +2 -2
- {python3_commons-0.5.14 → python3_commons-0.5.16}/setup.cfg +3 -3
- {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/audit.py +29 -30
- {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/helpers.py +17 -1
- {python3_commons-0.5.14 → python3_commons-0.5.16/src/python3_commons.egg-info}/PKG-INFO +3 -3
- {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons.egg-info/SOURCES.txt +1 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons.egg-info/requires.txt +2 -2
- {python3_commons-0.5.14 → python3_commons-0.5.16}/tests/conftest.py +10 -0
- python3_commons-0.5.16/tests/test_audit.py +10 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/.coveragerc +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/.github/workflows/python-publish.yaml +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/.gitignore +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/AUTHORS.rst +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/CHANGELOG.rst +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/LICENSE +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/README.md +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/README.rst +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/docs/Makefile +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/docs/_static/.gitignore +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/docs/authors.rst +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/docs/changelog.rst +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/docs/conf.py +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/docs/index.rst +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/docs/license.rst +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/pyproject.toml +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/requirements_dev.txt +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/requirements_test.txt +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/setup.py +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/__init__.py +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/conf.py +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/db.py +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/fs.py +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/logging/__init__.py +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/logging/filters.py +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/logging/formatter.py +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/object_storage.py +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/serializers/__init__.py +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/serializers/json.py +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/serializers/msgpack.py +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/serializers/msgspec.py +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons.egg-info/dependency_links.txt +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons.egg-info/not-zip-safe +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons.egg-info/top_level.txt +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/tests/test_msgpack.py +0 -0
- {python3_commons-0.5.14 → python3_commons-0.5.16}/tests/test_msgspec.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python3-commons
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.16
|
|
4
4
|
Summary: Re-usable Python3 code
|
|
5
5
|
Home-page: https://github.com/kamikaze/python3-commons
|
|
6
6
|
Author: Oleg Korsak
|
|
@@ -19,8 +19,8 @@ Requires-Dist: lxml==5.2.2
|
|
|
19
19
|
Requires-Dist: minio==7.2.7
|
|
20
20
|
Requires-Dist: msgpack==1.0.8
|
|
21
21
|
Requires-Dist: msgspec==0.18.6
|
|
22
|
-
Requires-Dist: pydantic[email]==2.
|
|
23
|
-
Requires-Dist: pydantic-settings==2.
|
|
22
|
+
Requires-Dist: pydantic[email]==2.8.2
|
|
23
|
+
Requires-Dist: pydantic-settings==2.3.4
|
|
24
24
|
Requires-Dist: zeep==4.2.1
|
|
25
25
|
Provides-Extra: testing
|
|
26
26
|
Requires-Dist: pytest; extra == "testing"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[metadata]
|
|
2
2
|
name = python3-commons
|
|
3
|
-
version = 0.5.
|
|
3
|
+
version = 0.5.16
|
|
4
4
|
description = Re-usable Python3 code
|
|
5
5
|
author = Oleg Korsak
|
|
6
6
|
author_email = kamikaze.is.waiting.you@gmail.com
|
|
@@ -28,8 +28,8 @@ install_requires =
|
|
|
28
28
|
minio==7.2.7
|
|
29
29
|
msgpack==1.0.8
|
|
30
30
|
msgspec==0.18.6
|
|
31
|
-
pydantic[email]==2.
|
|
32
|
-
pydantic-settings==2.
|
|
31
|
+
pydantic[email]==2.8.2
|
|
32
|
+
pydantic-settings==2.3.4
|
|
33
33
|
zeep==4.2.1
|
|
34
34
|
python_requires = >=3.12
|
|
35
35
|
|
|
@@ -3,8 +3,7 @@ import io
|
|
|
3
3
|
import logging
|
|
4
4
|
import tarfile
|
|
5
5
|
from datetime import datetime, timedelta, UTC
|
|
6
|
-
from
|
|
7
|
-
from typing import Generator
|
|
6
|
+
from typing import Generator, Iterable
|
|
8
7
|
from uuid import uuid4
|
|
9
8
|
|
|
10
9
|
from lxml import etree
|
|
@@ -19,7 +18,7 @@ from python3_commons.object_storage import get_s3_client
|
|
|
19
18
|
logger = logging.getLogger(__name__)
|
|
20
19
|
|
|
21
20
|
|
|
22
|
-
class
|
|
21
|
+
class GeneratedStream(io.BytesIO):
|
|
23
22
|
def __init__(self, generator: Generator[bytes, None, None], *args, **kwargs):
|
|
24
23
|
super().__init__(*args, **kwargs)
|
|
25
24
|
self.generator = generator
|
|
@@ -54,33 +53,30 @@ class BytesIOStream(io.BytesIO):
|
|
|
54
53
|
return True
|
|
55
54
|
|
|
56
55
|
|
|
57
|
-
def generate_archive(
|
|
56
|
+
def generate_archive(objects: Iterable[tuple[str, datetime, bytes]],
|
|
57
|
+
chunk_size: int = 4096) -> Generator[bytes, None, None]:
|
|
58
58
|
buffer = io.BytesIO()
|
|
59
59
|
|
|
60
|
-
with tarfile.open(fileobj=buffer, mode='w
|
|
61
|
-
|
|
60
|
+
with tarfile.open(fileobj=buffer, mode='w') as archive:
|
|
61
|
+
for name, last_modified, content in objects:
|
|
62
|
+
logger.info(f'Adding {name} to archive')
|
|
63
|
+
info = tarfile.TarInfo(name)
|
|
64
|
+
info.size = len(content)
|
|
65
|
+
info.mtime = last_modified.timestamp()
|
|
66
|
+
archive.addfile(info, io.BytesIO(content))
|
|
62
67
|
|
|
63
|
-
|
|
64
|
-
logger.info(f'Compacting files in: {date_path}')
|
|
68
|
+
buffer.seek(0)
|
|
65
69
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
info = tarfile.TarInfo(name)
|
|
69
|
-
info.size = len(content)
|
|
70
|
-
info.mtime = last_modified.timestamp()
|
|
71
|
-
archive.addfile(info, io.BytesIO(content))
|
|
72
|
-
buffer.seek(0)
|
|
70
|
+
while True:
|
|
71
|
+
chunk = buffer.read(chunk_size)
|
|
73
72
|
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
if not chunk:
|
|
74
|
+
break
|
|
76
75
|
|
|
77
|
-
|
|
78
|
-
break
|
|
76
|
+
yield chunk
|
|
79
77
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
buffer.seek(0)
|
|
83
|
-
buffer.truncate(0)
|
|
78
|
+
buffer.seek(0)
|
|
79
|
+
buffer.truncate(0)
|
|
84
80
|
|
|
85
81
|
|
|
86
82
|
def write_audit_data_sync(settings: S3Settings, key: str, data: bytes):
|
|
@@ -110,15 +106,18 @@ async def archive_audit_data(root_path: str = 'audit'):
|
|
|
110
106
|
bucket_name = s3_settings.s3_bucket
|
|
111
107
|
date_path = object_storage.get_absolute_path(f'{root_path}/{year}/{month:02}/{day:02}')
|
|
112
108
|
|
|
113
|
-
|
|
114
|
-
|
|
109
|
+
if objects := object_storage.get_objects(bucket_name, date_path, recursive=True):
|
|
110
|
+
logger.info(f'Compacting files in: {date_path}')
|
|
111
|
+
|
|
112
|
+
generator = generate_archive(objects, chunk_size=5*1024*1024)
|
|
113
|
+
archive_stream = GeneratedStream(generator)
|
|
115
114
|
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
archive_path = object_storage.get_absolute_path(f'audit/.archive/{year}_{month:02}_{day:02}.tar.bz2')
|
|
116
|
+
object_storage.put_object(bucket_name, archive_path, archive_stream, -1, part_size=5*1024*1024)
|
|
118
117
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
118
|
+
if errors := object_storage.remove_objects(bucket_name, date_path):
|
|
119
|
+
for error in errors:
|
|
120
|
+
logger.error(f'Failed to delete object in {bucket_name=}: {error}')
|
|
122
121
|
|
|
123
122
|
|
|
124
123
|
class ZeepAuditPlugin(Plugin):
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import logging
|
|
3
|
+
import shlex
|
|
3
4
|
|
|
4
5
|
from decimal import Decimal, ROUND_HALF_UP
|
|
5
|
-
|
|
6
|
+
from typing import Mapping
|
|
6
7
|
|
|
7
8
|
logger = logging.getLogger(__name__)
|
|
8
9
|
|
|
@@ -47,3 +48,18 @@ def round_decimal(value: Decimal, decimal_places=2, rounding_mode=ROUND_HALF_UP)
|
|
|
47
48
|
return value.quantize(Decimal(10) ** -decimal_places, rounding=rounding_mode)
|
|
48
49
|
except AttributeError:
|
|
49
50
|
return value
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def request_to_curl(url: str, method: str, headers: Mapping, body: bytes | None = None) -> str:
|
|
54
|
+
curl_cmd = ['curl', '-i', '-X', method, shlex.quote(url)]
|
|
55
|
+
|
|
56
|
+
for key, value in headers.items():
|
|
57
|
+
header_line = f'{key}: {value}'
|
|
58
|
+
curl_cmd.append('-H')
|
|
59
|
+
curl_cmd.append(shlex.quote(header_line))
|
|
60
|
+
|
|
61
|
+
if body is not None:
|
|
62
|
+
curl_cmd.append('--data')
|
|
63
|
+
curl_cmd.append(shlex.quote(body.decode('utf-8')))
|
|
64
|
+
|
|
65
|
+
return ' '.join(curl_cmd)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python3-commons
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.16
|
|
4
4
|
Summary: Re-usable Python3 code
|
|
5
5
|
Home-page: https://github.com/kamikaze/python3-commons
|
|
6
6
|
Author: Oleg Korsak
|
|
@@ -19,8 +19,8 @@ Requires-Dist: lxml==5.2.2
|
|
|
19
19
|
Requires-Dist: minio==7.2.7
|
|
20
20
|
Requires-Dist: msgpack==1.0.8
|
|
21
21
|
Requires-Dist: msgspec==0.18.6
|
|
22
|
-
Requires-Dist: pydantic[email]==2.
|
|
23
|
-
Requires-Dist: pydantic-settings==2.
|
|
22
|
+
Requires-Dist: pydantic[email]==2.8.2
|
|
23
|
+
Requires-Dist: pydantic-settings==2.3.4
|
|
24
24
|
Requires-Dist: zeep==4.2.1
|
|
25
25
|
Provides-Extra: testing
|
|
26
26
|
Requires-Dist: pytest; extra == "testing"
|
|
@@ -59,3 +59,13 @@ def data_struct():
|
|
|
59
59
|
e=date(2023, 7, 24),
|
|
60
60
|
f=Decimal('1.23')
|
|
61
61
|
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@pytest.fixture
|
|
65
|
+
def s3_file_objects() -> tuple:
|
|
66
|
+
return (
|
|
67
|
+
('file_a.txt', datetime(2024, 1, 1), b'ABCDE', ),
|
|
68
|
+
('file_b.txt', datetime(2024, 1, 2), b'FGHIJ', ),
|
|
69
|
+
('file_c.txt', datetime(2024, 1, 3), b'KLMNO', ),
|
|
70
|
+
('file_d.txt', datetime(2024, 1, 4), b'PQRST', ),
|
|
71
|
+
)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from python3_commons.audit import GeneratedStream, generate_archive
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_generated_stream(s3_file_objects):
|
|
5
|
+
expected_data = b''
|
|
6
|
+
generator = generate_archive(s3_file_objects, chunk_size=5 * 1024 * 1024)
|
|
7
|
+
archive_stream = GeneratedStream(generator)
|
|
8
|
+
archived_data = archive_stream.read()
|
|
9
|
+
|
|
10
|
+
assert archived_data == expected_data
|
|
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.14 → python3_commons-0.5.16}/src/python3_commons/serializers/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/serializers/msgpack.py
RENAMED
|
File without changes
|
{python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons/serializers/msgspec.py
RENAMED
|
File without changes
|
{python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
{python3_commons-0.5.14 → python3_commons-0.5.16}/src/python3_commons.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|