python3-commons 0.18.0__py3-none-any.whl → 0.18.2__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.
- python3_commons/audit.py +57 -0
- python3_commons/soap_client.py +21 -51
- {python3_commons-0.18.0.dist-info → python3_commons-0.18.2.dist-info}/METADATA +1 -2
- {python3_commons-0.18.0.dist-info → python3_commons-0.18.2.dist-info}/RECORD +8 -8
- {python3_commons-0.18.0.dist-info → python3_commons-0.18.2.dist-info}/WHEEL +0 -0
- {python3_commons-0.18.0.dist-info → python3_commons-0.18.2.dist-info}/licenses/AUTHORS.rst +0 -0
- {python3_commons-0.18.0.dist-info → python3_commons-0.18.2.dist-info}/licenses/LICENSE +0 -0
- {python3_commons-0.18.0.dist-info → python3_commons-0.18.2.dist-info}/top_level.txt +0 -0
python3_commons/audit.py
CHANGED
|
@@ -1,10 +1,25 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
import io
|
|
2
3
|
import logging
|
|
4
|
+
from datetime import UTC, datetime
|
|
3
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
|
|
4
15
|
|
|
5
16
|
from python3_commons import object_storage
|
|
17
|
+
from python3_commons.conf import s3_settings
|
|
6
18
|
|
|
7
19
|
if TYPE_CHECKING:
|
|
20
|
+
from zeep import AsyncClient
|
|
21
|
+
from zeep.wsdl.definitions import AbstractOperation
|
|
22
|
+
|
|
8
23
|
from python3_commons.conf import S3Settings
|
|
9
24
|
|
|
10
25
|
logger = logging.getLogger(__name__)
|
|
@@ -20,3 +35,45 @@ async def write_audit_data(settings: S3Settings, key: str, data: bytes) -> None:
|
|
|
20
35
|
logger.debug('Stored object in storage: %s', key)
|
|
21
36
|
else:
|
|
22
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
|
+
)
|
python3_commons/soap_client.py
CHANGED
|
@@ -5,23 +5,20 @@ Async SOAP client built on aiohttp + zeep.
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
7
|
import asyncio
|
|
8
|
+
import concurrent.futures
|
|
8
9
|
import logging
|
|
9
10
|
from collections.abc import AsyncIterator, Sequence
|
|
10
11
|
from contextlib import asynccontextmanager
|
|
11
12
|
from dataclasses import dataclass
|
|
12
|
-
from datetime import UTC, datetime
|
|
13
13
|
from typing import TYPE_CHECKING, Any, Self
|
|
14
|
-
from uuid import uuid4
|
|
15
14
|
|
|
16
15
|
try:
|
|
17
16
|
import aiohttp
|
|
18
17
|
from aiohttp import ClientSession, ClientTimeout, TCPConnector
|
|
19
|
-
from lxml import etree
|
|
20
18
|
from requests import Response
|
|
21
19
|
from requests.cookies import RequestsCookieJar
|
|
22
20
|
from zeep import AsyncClient
|
|
23
21
|
from zeep.exceptions import TransportError
|
|
24
|
-
from zeep.plugins import HistoryPlugin, Plugin
|
|
25
22
|
from zeep.transports import Transport
|
|
26
23
|
from zeep.utils import get_version
|
|
27
24
|
from zeep.wsdl.utils import etree_to_string
|
|
@@ -30,49 +27,12 @@ except ImportError as e:
|
|
|
30
27
|
|
|
31
28
|
raise RuntimeError(msg) from e
|
|
32
29
|
|
|
33
|
-
from python3_commons.audit import write_audit_data
|
|
34
|
-
from python3_commons.conf import s3_settings
|
|
35
|
-
|
|
36
30
|
if TYPE_CHECKING:
|
|
37
|
-
from zeep.
|
|
31
|
+
from zeep.plugins import Plugin
|
|
38
32
|
|
|
39
33
|
logger = logging.getLogger(__name__)
|
|
40
34
|
|
|
41
35
|
|
|
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
36
|
@dataclass(frozen=True, slots=True)
|
|
77
37
|
class TransportConfig:
|
|
78
38
|
"""Immutable transport settings passed to AsyncTransport."""
|
|
@@ -207,6 +167,20 @@ class AsyncTransport(Transport):
|
|
|
207
167
|
|
|
208
168
|
return await _fetch()
|
|
209
169
|
|
|
170
|
+
def load(self, url: str) -> bytes:
|
|
171
|
+
"""Sync entry-point zeep calls during WSDL document init."""
|
|
172
|
+
if not url:
|
|
173
|
+
return b''
|
|
174
|
+
|
|
175
|
+
try:
|
|
176
|
+
loop = asyncio.get_event_loop()
|
|
177
|
+
|
|
178
|
+
return loop.run_until_complete(self._load_remote_data(url))
|
|
179
|
+
except RuntimeError:
|
|
180
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
|
|
181
|
+
future = pool.submit(asyncio.run, self._load_remote_data(url))
|
|
182
|
+
return future.result()
|
|
183
|
+
|
|
210
184
|
async def post(
|
|
211
185
|
self,
|
|
212
186
|
address: str,
|
|
@@ -298,13 +272,9 @@ async def soap_client(
|
|
|
298
272
|
"""
|
|
299
273
|
transport = AsyncTransport.from_config(config, session=session)
|
|
300
274
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
275
|
+
try:
|
|
276
|
+
client = build_soap_client(wsdl_url, transport, plugins) # WSDL loaded here (sync via load())
|
|
304
277
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
(p for p in client.plugins if isinstance(p, HistoryPlugin)),
|
|
309
|
-
None,
|
|
310
|
-
)
|
|
278
|
+
yield client
|
|
279
|
+
finally:
|
|
280
|
+
await transport.aclose()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python3-commons
|
|
3
|
-
Version: 0.18.
|
|
3
|
+
Version: 0.18.2
|
|
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,7 +1,7 @@
|
|
|
1
1
|
python3_commons/__init__.py,sha256=0KgaYU46H_IMKn-BuasoRN3C4Hi45KlkHHoPbU9cwiA,189
|
|
2
2
|
python3_commons/api_client.py,sha256=WuP2SDikWvdrJ6yCLUVrtUBxv0UDfcFKxLZwj-It6R8,5418
|
|
3
3
|
python3_commons/async_functools.py,sha256=A2HvwFzZHxOWTp4IQM5UiBY2yg1S_0U1CWra5BWK0gk,9101
|
|
4
|
-
python3_commons/audit.py,sha256=
|
|
4
|
+
python3_commons/audit.py,sha256=uGoCwenDJ0Gdwbr_VNOZm5scT8luxW1weprJbbMoHo0,2608
|
|
5
5
|
python3_commons/auth.py,sha256=oihQ1sNnK_bi9JhQf_NCu4JVWmWrwVfSHD_zrHwRfzA,5044
|
|
6
6
|
python3_commons/cache.py,sha256=lowiXJqFgFy1Yg86wi9IhuoNqIUGP6nc5eNibmf0dfY,8018
|
|
7
7
|
python3_commons/conf.py,sha256=qy5asXYVaquo_qPnA3L7Hcqk9a4wZukEVK3xmCuF-LU,2867
|
|
@@ -11,7 +11,7 @@ python3_commons/generators.py,sha256=P6sKdCFhHT79-DZgzPU-qmwZvHg3wU8a75UFIwrycOY
|
|
|
11
11
|
python3_commons/helpers.py,sha256=2U0XyiBhylPrPIdPkZ16jrJRDZBE-yKv9GjRIY4C-EA,4541
|
|
12
12
|
python3_commons/object_storage.py,sha256=JB39Wb6rluAHa9oprsmB8d9UlY3sehsjX-aFmY_A-us,7219
|
|
13
13
|
python3_commons/permissions.py,sha256=gaMKSWg0MgPQTdP1voll4ItXcblXku9BlD0Lq3Xv64U,1724
|
|
14
|
-
python3_commons/soap_client.py,sha256=
|
|
14
|
+
python3_commons/soap_client.py,sha256=dZZWkg2RxFWr5yHfzcDK7SCy21FMfxctuDCoYiRBJII,8296
|
|
15
15
|
python3_commons/db/__init__.py,sha256=i60JvWiUY8kz8nEaVjc5MA1IN_P5g3tK7jX8KgcUd0Y,3105
|
|
16
16
|
python3_commons/db/helpers.py,sha256=xRpWs4aVkBge6HCLjb6OLSnWc1nlV6rKGyaVhZ_x12w,2001
|
|
17
17
|
python3_commons/db/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -27,9 +27,9 @@ python3_commons/serializers/common.py,sha256=VkA7C6wODvHk0QBXVX_x2JieDstihx3U__U
|
|
|
27
27
|
python3_commons/serializers/json.py,sha256=UPkC3ps13x2C_NxwVV-K7Ewp4VjkVHSSUkJVw5k7Wiw,712
|
|
28
28
|
python3_commons/serializers/msgpack.py,sha256=zESFBX34GsZ8rDu6Zk5V6CLT6P0mPilU0r04Ka6TblI,1474
|
|
29
29
|
python3_commons/serializers/msgspec.py,sha256=upy5CBmK66-8hYnK5bAM_sZvZY5CAqZmzCw9GIF346I,2988
|
|
30
|
-
python3_commons-0.18.
|
|
31
|
-
python3_commons-0.18.
|
|
32
|
-
python3_commons-0.18.
|
|
33
|
-
python3_commons-0.18.
|
|
34
|
-
python3_commons-0.18.
|
|
35
|
-
python3_commons-0.18.
|
|
30
|
+
python3_commons-0.18.2.dist-info/licenses/AUTHORS.rst,sha256=3R9JnfjfjH5RoPWOeqKFJgxVShSSfzQPIrEr1nxIo9Q,90
|
|
31
|
+
python3_commons-0.18.2.dist-info/licenses/LICENSE,sha256=xxILuojHm4fKQOrMHPSslbyy6WuKAN2RiG74HbrYfzM,34575
|
|
32
|
+
python3_commons-0.18.2.dist-info/METADATA,sha256=kIEA0rAgEB2EWJF1BCMXFPhRmxKK4oVasgPbNyZQAoo,2160
|
|
33
|
+
python3_commons-0.18.2.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
34
|
+
python3_commons-0.18.2.dist-info/top_level.txt,sha256=lJI6sCBf68eUHzupCnn2dzG10lH3jJKTWM_hrN1cQ7M,16
|
|
35
|
+
python3_commons-0.18.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|