python3-commons 0.18.0__tar.gz → 0.18.1__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.
- {python3_commons-0.18.0/src/python3_commons.egg-info → python3_commons-0.18.1}/PKG-INFO +1 -2
- {python3_commons-0.18.0 → python3_commons-0.18.1}/pyproject.toml +0 -1
- python3_commons-0.18.1/src/python3_commons/audit.py +79 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/soap_client.py +1 -51
- {python3_commons-0.18.0 → python3_commons-0.18.1/src/python3_commons.egg-info}/PKG-INFO +1 -2
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons.egg-info/requires.txt +0 -1
- python3_commons-0.18.0/src/python3_commons/audit.py +0 -22
- {python3_commons-0.18.0 → python3_commons-0.18.1}/.coveragerc +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/.devcontainer/Dockerfile +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/.devcontainer/devcontainer.json +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/.devcontainer/docker-compose.yml +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/.env_template +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/.github/workflows/checks.yml +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/.github/workflows/python-publish.yaml +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/.github/workflows/release-on-tag-push.yml +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/.gitignore +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/.pre-commit-config.yaml +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/.python-version +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/AUTHORS.rst +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/CHANGELOG.rst +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/LICENSE +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/README.md +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/README.rst +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/docs/Makefile +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/docs/_static/.gitignore +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/docs/authors.rst +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/docs/changelog.rst +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/docs/conf.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/docs/index.rst +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/docs/license.rst +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/setup.cfg +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/__init__.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/api_client.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/async_functools.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/auth.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/cache.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/conf.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/db/__init__.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/db/helpers.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/db/models/__init__.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/db/models/auth.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/db/models/common.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/db/models/rbac.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/db/models/users.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/exceptions.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/fs.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/generators.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/helpers.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/log/__init__.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/log/filters.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/log/formatters.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/object_storage.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/permissions.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/serializers/__init__.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/serializers/common.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/serializers/json.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/serializers/msgpack.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/serializers/msgspec.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons.egg-info/SOURCES.txt +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons.egg-info/dependency_links.txt +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons.egg-info/top_level.txt +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/tests/__init__.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/tests/integration/__init__.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/tests/integration/test_cache.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/tests/integration/test_osc.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/tests/unit/__init__.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/tests/unit/conftest.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/tests/unit/log/__init__.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/tests/unit/log/test_formatters.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/tests/unit/test_async_functools.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/tests/unit/test_audit.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/tests/unit/test_helpers.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/tests/unit/test_msgpack.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/tests/unit/test_msgspec.py +0 -0
- {python3_commons-0.18.0 → python3_commons-0.18.1}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python3-commons
|
|
3
|
-
Version: 0.18.
|
|
3
|
+
Version: 0.18.1
|
|
4
4
|
Summary: Re-usable Python3 code
|
|
5
5
|
Author-email: Oleg Korsak <kamikaze.is.waiting.you@gmail.com>
|
|
6
6
|
License-Expression: GPL-3.0
|
|
@@ -19,7 +19,6 @@ Provides-Extra: all
|
|
|
19
19
|
Requires-Dist: python3_commons[api-client,audit,authn,authz,cache,database,object-storage,soap-client]; extra == "all"
|
|
20
20
|
Provides-Extra: api-client
|
|
21
21
|
Requires-Dist: aiohttp[speedups]<3.15.0,>=3.13.5; extra == "api-client"
|
|
22
|
-
Requires-Dist: lxml~=6.1.0; extra == "api-client"
|
|
23
22
|
Requires-Dist: python3_commons[object-storage]; extra == "api-client"
|
|
24
23
|
Provides-Extra: audit
|
|
25
24
|
Requires-Dist: lxml~=6.1.0; extra == "audit"
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import io
|
|
3
|
+
import logging
|
|
4
|
+
from datetime import UTC, datetime
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
from uuid import uuid4
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
from lxml import etree
|
|
10
|
+
from zeep.plugins import HistoryPlugin, Plugin
|
|
11
|
+
except ImportError as e:
|
|
12
|
+
msg = 'Install python3-commons[audit] to use this feature'
|
|
13
|
+
|
|
14
|
+
raise RuntimeError(msg) from e
|
|
15
|
+
|
|
16
|
+
from python3_commons import object_storage
|
|
17
|
+
from python3_commons.conf import s3_settings
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from zeep import AsyncClient
|
|
21
|
+
from zeep.wsdl.definitions import AbstractOperation
|
|
22
|
+
|
|
23
|
+
from python3_commons.conf import S3Settings
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
async def write_audit_data(settings: S3Settings, key: str, data: bytes) -> None:
|
|
29
|
+
if settings.aws_secret_access_key:
|
|
30
|
+
try:
|
|
31
|
+
await object_storage.put_object(settings.s3_bucket, f'audit/{key}', io.BytesIO(data), len(data))
|
|
32
|
+
except Exception:
|
|
33
|
+
logger.exception('Failed storing object in storage.')
|
|
34
|
+
else:
|
|
35
|
+
logger.debug('Stored object in storage: %s', key)
|
|
36
|
+
else:
|
|
37
|
+
logger.debug('S3 is not configured, not storing object in storage: %s', key)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ZeepAuditPlugin(Plugin):
|
|
41
|
+
def __init__(self, audit_name: str = 'zeep') -> None:
|
|
42
|
+
super().__init__()
|
|
43
|
+
self.audit_name = audit_name
|
|
44
|
+
|
|
45
|
+
def store_audit_in_s3(self, envelope, operation: AbstractOperation, direction: str) -> None:
|
|
46
|
+
xml = etree.tostring(envelope, encoding='UTF-8', pretty_print=True)
|
|
47
|
+
now = datetime.now(tz=UTC)
|
|
48
|
+
date_path = now.strftime('%Y/%m/%d')
|
|
49
|
+
timestamp = now.strftime('%H%M%S')
|
|
50
|
+
path = f'{date_path}/{self.audit_name}/{operation.name}/{timestamp}_{str(uuid4())[-12:]}_{direction}.xml'
|
|
51
|
+
coro = write_audit_data(s3_settings, path, xml)
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
loop = asyncio.get_running_loop()
|
|
55
|
+
except RuntimeError:
|
|
56
|
+
loop = None
|
|
57
|
+
|
|
58
|
+
if loop and loop.is_running():
|
|
59
|
+
loop.create_task(coro)
|
|
60
|
+
else:
|
|
61
|
+
asyncio.run(coro)
|
|
62
|
+
|
|
63
|
+
def ingress(self, envelope, http_headers, operation: AbstractOperation):
|
|
64
|
+
self.store_audit_in_s3(envelope, operation, 'ingress')
|
|
65
|
+
|
|
66
|
+
return envelope, http_headers
|
|
67
|
+
|
|
68
|
+
def egress(self, envelope, http_headers, operation: AbstractOperation, binding_options):
|
|
69
|
+
self.store_audit_in_s3(envelope, operation, 'egress')
|
|
70
|
+
|
|
71
|
+
return envelope, http_headers
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def get_history_plugin(client: AsyncClient) -> HistoryPlugin | None:
|
|
75
|
+
"""Return the first HistoryPlugin attached to *client*, or None."""
|
|
76
|
+
return next(
|
|
77
|
+
(p for p in client.plugins if isinstance(p, HistoryPlugin)),
|
|
78
|
+
None,
|
|
79
|
+
)
|
|
@@ -4,24 +4,19 @@ Async SOAP client built on aiohttp + zeep.
|
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
|
-
import asyncio
|
|
8
7
|
import logging
|
|
9
8
|
from collections.abc import AsyncIterator, Sequence
|
|
10
9
|
from contextlib import asynccontextmanager
|
|
11
10
|
from dataclasses import dataclass
|
|
12
|
-
from datetime import UTC, datetime
|
|
13
11
|
from typing import TYPE_CHECKING, Any, Self
|
|
14
|
-
from uuid import uuid4
|
|
15
12
|
|
|
16
13
|
try:
|
|
17
14
|
import aiohttp
|
|
18
15
|
from aiohttp import ClientSession, ClientTimeout, TCPConnector
|
|
19
|
-
from lxml import etree
|
|
20
16
|
from requests import Response
|
|
21
17
|
from requests.cookies import RequestsCookieJar
|
|
22
18
|
from zeep import AsyncClient
|
|
23
19
|
from zeep.exceptions import TransportError
|
|
24
|
-
from zeep.plugins import HistoryPlugin, Plugin
|
|
25
20
|
from zeep.transports import Transport
|
|
26
21
|
from zeep.utils import get_version
|
|
27
22
|
from zeep.wsdl.utils import etree_to_string
|
|
@@ -30,49 +25,12 @@ except ImportError as e:
|
|
|
30
25
|
|
|
31
26
|
raise RuntimeError(msg) from e
|
|
32
27
|
|
|
33
|
-
from python3_commons.audit import write_audit_data
|
|
34
|
-
from python3_commons.conf import s3_settings
|
|
35
|
-
|
|
36
28
|
if TYPE_CHECKING:
|
|
37
|
-
from zeep.
|
|
29
|
+
from zeep.plugins import Plugin
|
|
38
30
|
|
|
39
31
|
logger = logging.getLogger(__name__)
|
|
40
32
|
|
|
41
33
|
|
|
42
|
-
class ZeepAuditPlugin(Plugin):
|
|
43
|
-
def __init__(self, audit_name: str = 'zeep') -> None:
|
|
44
|
-
super().__init__()
|
|
45
|
-
self.audit_name = audit_name
|
|
46
|
-
|
|
47
|
-
def store_audit_in_s3(self, envelope, operation: AbstractOperation, direction: str) -> None:
|
|
48
|
-
xml = etree.tostring(envelope, encoding='UTF-8', pretty_print=True)
|
|
49
|
-
now = datetime.now(tz=UTC)
|
|
50
|
-
date_path = now.strftime('%Y/%m/%d')
|
|
51
|
-
timestamp = now.strftime('%H%M%S')
|
|
52
|
-
path = f'{date_path}/{self.audit_name}/{operation.name}/{timestamp}_{str(uuid4())[-12:]}_{direction}.xml'
|
|
53
|
-
coro = write_audit_data(s3_settings, path, xml)
|
|
54
|
-
|
|
55
|
-
try:
|
|
56
|
-
loop = asyncio.get_running_loop()
|
|
57
|
-
except RuntimeError:
|
|
58
|
-
loop = None
|
|
59
|
-
|
|
60
|
-
if loop and loop.is_running():
|
|
61
|
-
loop.create_task(coro)
|
|
62
|
-
else:
|
|
63
|
-
asyncio.run(coro)
|
|
64
|
-
|
|
65
|
-
def ingress(self, envelope, http_headers, operation: AbstractOperation):
|
|
66
|
-
self.store_audit_in_s3(envelope, operation, 'ingress')
|
|
67
|
-
|
|
68
|
-
return envelope, http_headers
|
|
69
|
-
|
|
70
|
-
def egress(self, envelope, http_headers, operation: AbstractOperation, binding_options):
|
|
71
|
-
self.store_audit_in_s3(envelope, operation, 'egress')
|
|
72
|
-
|
|
73
|
-
return envelope, http_headers
|
|
74
|
-
|
|
75
|
-
|
|
76
34
|
@dataclass(frozen=True, slots=True)
|
|
77
35
|
class TransportConfig:
|
|
78
36
|
"""Immutable transport settings passed to AsyncTransport."""
|
|
@@ -300,11 +258,3 @@ async def soap_client(
|
|
|
300
258
|
|
|
301
259
|
async with transport:
|
|
302
260
|
yield build_soap_client(wsdl_url, transport, plugins)
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
def get_history_plugin(client: AsyncClient) -> HistoryPlugin | None:
|
|
306
|
-
"""Return the first HistoryPlugin attached to *client*, or None."""
|
|
307
|
-
return next(
|
|
308
|
-
(p for p in client.plugins if isinstance(p, HistoryPlugin)),
|
|
309
|
-
None,
|
|
310
|
-
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python3-commons
|
|
3
|
-
Version: 0.18.
|
|
3
|
+
Version: 0.18.1
|
|
4
4
|
Summary: Re-usable Python3 code
|
|
5
5
|
Author-email: Oleg Korsak <kamikaze.is.waiting.you@gmail.com>
|
|
6
6
|
License-Expression: GPL-3.0
|
|
@@ -19,7 +19,6 @@ Provides-Extra: all
|
|
|
19
19
|
Requires-Dist: python3_commons[api-client,audit,authn,authz,cache,database,object-storage,soap-client]; extra == "all"
|
|
20
20
|
Provides-Extra: api-client
|
|
21
21
|
Requires-Dist: aiohttp[speedups]<3.15.0,>=3.13.5; extra == "api-client"
|
|
22
|
-
Requires-Dist: lxml~=6.1.0; extra == "api-client"
|
|
23
22
|
Requires-Dist: python3_commons[object-storage]; extra == "api-client"
|
|
24
23
|
Provides-Extra: audit
|
|
25
24
|
Requires-Dist: lxml~=6.1.0; extra == "audit"
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import io
|
|
2
|
-
import logging
|
|
3
|
-
from typing import TYPE_CHECKING
|
|
4
|
-
|
|
5
|
-
from python3_commons import object_storage
|
|
6
|
-
|
|
7
|
-
if TYPE_CHECKING:
|
|
8
|
-
from python3_commons.conf import S3Settings
|
|
9
|
-
|
|
10
|
-
logger = logging.getLogger(__name__)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
async def write_audit_data(settings: S3Settings, key: str, data: bytes) -> None:
|
|
14
|
-
if settings.aws_secret_access_key:
|
|
15
|
-
try:
|
|
16
|
-
await object_storage.put_object(settings.s3_bucket, f'audit/{key}', io.BytesIO(data), len(data))
|
|
17
|
-
except Exception:
|
|
18
|
-
logger.exception('Failed storing object in storage.')
|
|
19
|
-
else:
|
|
20
|
-
logger.debug('Stored object in storage: %s', key)
|
|
21
|
-
else:
|
|
22
|
-
logger.debug('S3 is not configured, not storing object in storage: %s', key)
|
|
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
|
|
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.18.0 → python3_commons-0.18.1}/src/python3_commons/serializers/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/serializers/msgpack.py
RENAMED
|
File without changes
|
{python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons/serializers/msgspec.py
RENAMED
|
File without changes
|
|
File without changes
|
{python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{python3_commons-0.18.0 → python3_commons-0.18.1}/src/python3_commons.egg-info/top_level.txt
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|