python3-commons 0.18.7__tar.gz → 0.18.8__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.7/src/python3_commons.egg-info → python3_commons-0.18.8}/PKG-INFO +1 -1
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/soap_client.py +97 -110
- {python3_commons-0.18.7 → python3_commons-0.18.8/src/python3_commons.egg-info}/PKG-INFO +1 -1
- {python3_commons-0.18.7 → python3_commons-0.18.8}/.coveragerc +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/.devcontainer/Dockerfile +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/.devcontainer/devcontainer.json +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/.devcontainer/docker-compose.yml +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/.env_template +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/.github/workflows/checks.yml +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/.github/workflows/python-publish.yaml +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/.github/workflows/release-on-tag-push.yml +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/.gitignore +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/.pre-commit-config.yaml +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/.python-version +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/AUTHORS.rst +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/CHANGELOG.rst +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/LICENSE +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/README.md +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/README.rst +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/docs/Makefile +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/docs/_static/.gitignore +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/docs/authors.rst +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/docs/changelog.rst +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/docs/conf.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/docs/index.rst +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/docs/license.rst +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/pyproject.toml +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/setup.cfg +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/__init__.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/api_client.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/async_functools.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/audit.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/auth.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/cache.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/conf.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/db/__init__.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/db/helpers.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/db/models/__init__.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/db/models/auth.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/db/models/common.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/db/models/rbac.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/db/models/users.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/exceptions.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/fs.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/generators.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/helpers.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/log/__init__.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/log/filters.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/log/formatters.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/object_storage.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/permissions.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/serializers/__init__.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/serializers/common.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/serializers/json.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/serializers/msgpack.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/serializers/msgspec.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons.egg-info/SOURCES.txt +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons.egg-info/dependency_links.txt +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons.egg-info/requires.txt +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons.egg-info/top_level.txt +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/tests/__init__.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/tests/integration/__init__.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/tests/integration/test_cache.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/tests/integration/test_osc.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/tests/unit/__init__.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/tests/unit/conftest.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/tests/unit/log/__init__.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/tests/unit/log/test_formatters.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/tests/unit/test_async_functools.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/tests/unit/test_audit.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/tests/unit/test_helpers.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/tests/unit/test_msgpack.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/tests/unit/test_msgspec.py +0 -0
- {python3_commons-0.18.7 → python3_commons-0.18.8}/uv.lock +0 -0
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Async SOAP client built on aiohttp + zeep.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
1
|
from __future__ import annotations
|
|
6
2
|
|
|
7
3
|
import asyncio
|
|
8
|
-
import concurrent.futures
|
|
9
4
|
import logging
|
|
10
5
|
import ssl
|
|
11
6
|
from collections.abc import AsyncIterator, Sequence
|
|
@@ -13,10 +8,9 @@ from contextlib import asynccontextmanager
|
|
|
13
8
|
from dataclasses import dataclass
|
|
14
9
|
from typing import TYPE_CHECKING, Any, Self
|
|
15
10
|
|
|
16
|
-
import certifi
|
|
17
|
-
|
|
18
11
|
try:
|
|
19
12
|
import aiohttp
|
|
13
|
+
import certifi
|
|
20
14
|
from aiohttp import ClientSession, ClientTimeout, TCPConnector
|
|
21
15
|
from requests import Response
|
|
22
16
|
from requests.cookies import RequestsCookieJar
|
|
@@ -38,55 +32,48 @@ logger = logging.getLogger(__name__)
|
|
|
38
32
|
|
|
39
33
|
def _make_ssl_context(*, verify: bool) -> ssl.SSLContext | bool:
|
|
40
34
|
if not verify:
|
|
41
|
-
return False
|
|
35
|
+
return False
|
|
42
36
|
|
|
43
37
|
return ssl.create_default_context(cafile=certifi.where())
|
|
44
38
|
|
|
45
39
|
|
|
46
40
|
@dataclass(frozen=True, slots=True)
|
|
47
41
|
class TransportConfig:
|
|
48
|
-
"""Immutable transport settings passed to AsyncTransport."""
|
|
49
|
-
|
|
50
42
|
timeout: int = 300
|
|
51
|
-
"""Total timeout in seconds for WSDL fetches."""
|
|
52
|
-
|
|
53
43
|
operation_timeout: int = 60
|
|
54
|
-
"""Total timeout in seconds for SOAP operation calls."""
|
|
55
|
-
|
|
56
44
|
verify_ssl: bool = True
|
|
57
45
|
proxy: str | None = None
|
|
58
46
|
|
|
59
47
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
Usage::
|
|
48
|
+
# -------------------------
|
|
49
|
+
# Transport
|
|
50
|
+
# -------------------------
|
|
65
51
|
|
|
66
|
-
async with soap_client("https://example.com/service?wsdl") as client:
|
|
67
|
-
result = await client.service.SomeOperation(...)
|
|
68
|
-
"""
|
|
69
52
|
|
|
53
|
+
class AsyncTransport(Transport):
|
|
70
54
|
def __init__(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
55
|
+
self,
|
|
56
|
+
*,
|
|
57
|
+
session: ClientSession,
|
|
58
|
+
config: TransportConfig,
|
|
59
|
+
loop: asyncio.AbstractEventLoop,
|
|
60
|
+
owns_session: bool = False,
|
|
76
61
|
) -> None:
|
|
77
62
|
super().__init__()
|
|
78
63
|
self._session = session
|
|
79
64
|
self._config = config
|
|
80
|
-
self.
|
|
65
|
+
self._loop = loop
|
|
66
|
+
self._owns_session = owns_session
|
|
81
67
|
|
|
82
68
|
@classmethod
|
|
83
69
|
def from_config(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
) ->
|
|
70
|
+
cls,
|
|
71
|
+
config: TransportConfig | None = None,
|
|
72
|
+
*,
|
|
73
|
+
session: ClientSession | None = None,
|
|
74
|
+
) -> AsyncTransport:
|
|
89
75
|
config = config or TransportConfig()
|
|
76
|
+
loop = asyncio.get_running_loop()
|
|
90
77
|
owns_session = session is None
|
|
91
78
|
|
|
92
79
|
if owns_session:
|
|
@@ -96,7 +83,12 @@ class AsyncTransport(Transport):
|
|
|
96
83
|
headers={'User-Agent': f'Zeep/{get_version()} (www.python-zeep.org)'},
|
|
97
84
|
)
|
|
98
85
|
|
|
99
|
-
return cls(
|
|
86
|
+
return cls(
|
|
87
|
+
session=session,
|
|
88
|
+
config=config,
|
|
89
|
+
loop=loop,
|
|
90
|
+
owns_session=owns_session,
|
|
91
|
+
)
|
|
100
92
|
|
|
101
93
|
async def aclose(self) -> None:
|
|
102
94
|
if self._owns_session:
|
|
@@ -110,7 +102,6 @@ class AsyncTransport(Transport):
|
|
|
110
102
|
|
|
111
103
|
@staticmethod
|
|
112
104
|
def _build_response(response: aiohttp.ClientResponse, body: bytes) -> Response:
|
|
113
|
-
"""Convert an aiohttp response into a requests.Response for zeep."""
|
|
114
105
|
r = Response()
|
|
115
106
|
r.status_code = response.status
|
|
116
107
|
r._content = body # noqa: SLF001
|
|
@@ -127,116 +118,112 @@ class AsyncTransport(Transport):
|
|
|
127
118
|
|
|
128
119
|
return r
|
|
129
120
|
|
|
121
|
+
async def _fetch(self, url: str) -> bytes:
|
|
122
|
+
async with self._session.get(
|
|
123
|
+
url,
|
|
124
|
+
proxy=self._config.proxy,
|
|
125
|
+
timeout=ClientTimeout(total=self._config.timeout),
|
|
126
|
+
) as resp:
|
|
127
|
+
body = await resp.read()
|
|
128
|
+
|
|
129
|
+
if resp.status >= 400:
|
|
130
|
+
raise TransportError(
|
|
131
|
+
status_code=resp.status,
|
|
132
|
+
message=body.decode(errors='ignore'),
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
return body
|
|
136
|
+
|
|
130
137
|
def load(self, url: str) -> bytes:
|
|
131
138
|
"""
|
|
132
|
-
|
|
139
|
+
Called synchronously by zeep during WSDL parsing.
|
|
133
140
|
|
|
134
|
-
|
|
135
|
-
is no cross-loop session sharing with the operational session.
|
|
141
|
+
We safely hop into the main loop.
|
|
136
142
|
"""
|
|
137
143
|
if not url:
|
|
138
144
|
return b''
|
|
139
145
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
timeout=ClientTimeout(total=self._config.timeout),
|
|
145
|
-
headers={'User-Agent': f'Zeep/{get_version()} (www.python-zeep.org)'},
|
|
146
|
-
) as session,
|
|
147
|
-
session.get(url, proxy=self._config.proxy) as resp,
|
|
148
|
-
):
|
|
149
|
-
content = await resp.read()
|
|
150
|
-
|
|
151
|
-
if resp.status >= 400:
|
|
152
|
-
raise TransportError(
|
|
153
|
-
status_code=resp.status,
|
|
154
|
-
message=content.decode(errors='ignore'),
|
|
155
|
-
)
|
|
156
|
-
|
|
157
|
-
return content
|
|
158
|
-
|
|
159
|
-
# load() is always called from within a running event loop (during
|
|
160
|
-
# AsyncClient.__init__ inside the soap_client context manager), so
|
|
161
|
-
# run_until_complete on the running loop would raise. Always delegate
|
|
162
|
-
# to a thread with its own fresh event loop.
|
|
163
|
-
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
|
|
164
|
-
return pool.submit(asyncio.run, _fetch()).result()
|
|
165
|
-
|
|
166
|
-
async def post(
|
|
167
|
-
self,
|
|
168
|
-
address: str,
|
|
169
|
-
message: bytes,
|
|
170
|
-
headers: dict[str, str],
|
|
171
|
-
*,
|
|
172
|
-
timeout: int | None = None,
|
|
173
|
-
) -> Response:
|
|
174
|
-
logger.debug('SOAP POST → %s\n%s', address, message)
|
|
146
|
+
future = asyncio.run_coroutine_threadsafe(
|
|
147
|
+
self._fetch(url),
|
|
148
|
+
self._loop,
|
|
149
|
+
)
|
|
175
150
|
|
|
176
|
-
|
|
177
|
-
address,
|
|
178
|
-
data=message,
|
|
179
|
-
headers=headers,
|
|
180
|
-
proxy=self._config.proxy,
|
|
181
|
-
timeout=ClientTimeout(total=timeout) if timeout is not None else None,
|
|
182
|
-
) as resp:
|
|
183
|
-
body = await resp.read()
|
|
184
|
-
logger.debug('SOAP ← %s (HTTP %d)\n%s', address, resp.status, body)
|
|
185
|
-
return self._build_response(resp, body)
|
|
151
|
+
return future.result()
|
|
186
152
|
|
|
187
153
|
def post_xml(
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
154
|
+
self,
|
|
155
|
+
address: str,
|
|
156
|
+
envelope: Any,
|
|
157
|
+
headers: dict[str, str],
|
|
192
158
|
) -> Response:
|
|
193
|
-
"""
|
|
194
|
-
Sync entry-point zeep calls from a thread executor during send_async.
|
|
195
|
-
Bridges back into the running event loop via run_coroutine_threadsafe.
|
|
196
|
-
"""
|
|
197
|
-
loop = asyncio.get_event_loop()
|
|
198
159
|
future = asyncio.run_coroutine_threadsafe(
|
|
199
160
|
self.post(address, etree_to_string(envelope), headers),
|
|
200
|
-
|
|
161
|
+
self._loop,
|
|
201
162
|
)
|
|
163
|
+
|
|
202
164
|
return future.result()
|
|
203
165
|
|
|
166
|
+
async def post(
|
|
167
|
+
self,
|
|
168
|
+
address: str,
|
|
169
|
+
message: bytes,
|
|
170
|
+
headers: dict[str, str],
|
|
171
|
+
*,
|
|
172
|
+
timeout: int | None = None,
|
|
173
|
+
) -> Response:
|
|
174
|
+
logger.debug('SOAP POST → %s', address)
|
|
175
|
+
|
|
176
|
+
async with self._session.post(
|
|
177
|
+
address,
|
|
178
|
+
data=message,
|
|
179
|
+
headers=headers,
|
|
180
|
+
proxy=self._config.proxy,
|
|
181
|
+
timeout=ClientTimeout(total=timeout) if timeout is not None else None,
|
|
182
|
+
) as resp:
|
|
183
|
+
body = await resp.read()
|
|
184
|
+
logger.debug('SOAP ← %s (%d)', address, resp.status)
|
|
185
|
+
return self._build_response(resp, body)
|
|
186
|
+
|
|
204
187
|
async def get(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
188
|
+
self,
|
|
189
|
+
address: str,
|
|
190
|
+
params: dict[str, str],
|
|
191
|
+
headers: dict[str, str],
|
|
209
192
|
) -> Response:
|
|
210
193
|
async with self._session.get(
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
194
|
+
address,
|
|
195
|
+
params=params,
|
|
196
|
+
headers=headers,
|
|
197
|
+
proxy=self._config.proxy,
|
|
215
198
|
) as resp:
|
|
216
199
|
body = await resp.read()
|
|
217
200
|
return self._build_response(resp, body)
|
|
218
201
|
|
|
219
202
|
|
|
220
203
|
def build_soap_client(
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
204
|
+
wsdl_url: str,
|
|
205
|
+
transport: AsyncTransport,
|
|
206
|
+
plugins: Sequence[Plugin] | None = None,
|
|
224
207
|
) -> AsyncClient:
|
|
225
208
|
if not wsdl_url:
|
|
226
209
|
msg = 'wsdl_url must be a non-empty string.'
|
|
227
210
|
|
|
228
211
|
raise ValueError(msg)
|
|
229
212
|
|
|
230
|
-
return AsyncClient(
|
|
213
|
+
return AsyncClient(
|
|
214
|
+
wsdl_url,
|
|
215
|
+
transport=transport,
|
|
216
|
+
plugins=list(plugins or []),
|
|
217
|
+
)
|
|
231
218
|
|
|
232
219
|
|
|
233
220
|
@asynccontextmanager
|
|
234
221
|
async def soap_client(
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
222
|
+
wsdl_url: str,
|
|
223
|
+
*,
|
|
224
|
+
config: TransportConfig | None = None,
|
|
225
|
+
session: ClientSession | None = None,
|
|
226
|
+
plugins: Sequence[Plugin] | None = None,
|
|
240
227
|
) -> AsyncIterator[AsyncClient]:
|
|
241
228
|
"""
|
|
242
229
|
Async context manager yielding a ready-to-use zeep AsyncClient.
|
|
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
|
|
File without changes
|
|
File without changes
|
{python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/serializers/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/serializers/msgpack.py
RENAMED
|
File without changes
|
{python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons/serializers/msgspec.py
RENAMED
|
File without changes
|
|
File without changes
|
{python3_commons-0.18.7 → python3_commons-0.18.8}/src/python3_commons.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
{python3_commons-0.18.7 → python3_commons-0.18.8}/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
|