python3-commons 0.18.4__tar.gz → 0.18.5__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.4/src/python3_commons.egg-info → python3_commons-0.18.5}/PKG-INFO +1 -1
- python3_commons-0.18.5/src/python3_commons/soap_client.py +243 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5/src/python3_commons.egg-info}/PKG-INFO +1 -1
- python3_commons-0.18.4/src/python3_commons/soap_client.py +0 -299
- {python3_commons-0.18.4 → python3_commons-0.18.5}/.coveragerc +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/.devcontainer/Dockerfile +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/.devcontainer/devcontainer.json +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/.devcontainer/docker-compose.yml +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/.env_template +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/.github/workflows/checks.yml +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/.github/workflows/python-publish.yaml +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/.github/workflows/release-on-tag-push.yml +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/.gitignore +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/.pre-commit-config.yaml +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/.python-version +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/AUTHORS.rst +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/CHANGELOG.rst +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/LICENSE +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/README.md +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/README.rst +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/docs/Makefile +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/docs/_static/.gitignore +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/docs/authors.rst +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/docs/changelog.rst +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/docs/conf.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/docs/index.rst +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/docs/license.rst +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/pyproject.toml +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/setup.cfg +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/__init__.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/api_client.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/async_functools.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/audit.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/auth.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/cache.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/conf.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/db/__init__.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/db/helpers.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/db/models/__init__.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/db/models/auth.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/db/models/common.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/db/models/rbac.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/db/models/users.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/exceptions.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/fs.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/generators.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/helpers.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/log/__init__.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/log/filters.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/log/formatters.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/object_storage.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/permissions.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/serializers/__init__.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/serializers/common.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/serializers/json.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/serializers/msgpack.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/serializers/msgspec.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons.egg-info/SOURCES.txt +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons.egg-info/dependency_links.txt +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons.egg-info/requires.txt +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons.egg-info/top_level.txt +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/tests/__init__.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/tests/integration/__init__.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/tests/integration/test_cache.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/tests/integration/test_osc.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/tests/unit/__init__.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/tests/unit/conftest.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/tests/unit/log/__init__.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/tests/unit/log/test_formatters.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/tests/unit/test_async_functools.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/tests/unit/test_audit.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/tests/unit/test_helpers.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/tests/unit/test_msgpack.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/tests/unit/test_msgspec.py +0 -0
- {python3_commons-0.18.4 → python3_commons-0.18.5}/uv.lock +0 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Async SOAP client built on aiohttp + zeep.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
import concurrent.futures
|
|
9
|
+
import logging
|
|
10
|
+
from collections.abc import AsyncIterator, Sequence
|
|
11
|
+
from contextlib import asynccontextmanager
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from typing import TYPE_CHECKING, Any, Self
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
import aiohttp
|
|
17
|
+
from aiohttp import ClientSession, ClientTimeout, TCPConnector
|
|
18
|
+
from requests import Response
|
|
19
|
+
from requests.cookies import RequestsCookieJar
|
|
20
|
+
from zeep import AsyncClient
|
|
21
|
+
from zeep.exceptions import TransportError
|
|
22
|
+
from zeep.transports import Transport
|
|
23
|
+
from zeep.utils import get_version
|
|
24
|
+
from zeep.wsdl.utils import etree_to_string
|
|
25
|
+
except ImportError as e:
|
|
26
|
+
msg = 'Install python3-commons[soap-client] to use this feature'
|
|
27
|
+
|
|
28
|
+
raise RuntimeError(msg) from e
|
|
29
|
+
|
|
30
|
+
if TYPE_CHECKING:
|
|
31
|
+
from zeep.plugins import Plugin
|
|
32
|
+
|
|
33
|
+
logger = logging.getLogger(__name__)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass(frozen=True, slots=True)
|
|
37
|
+
class TransportConfig:
|
|
38
|
+
"""Immutable transport settings passed to AsyncTransport."""
|
|
39
|
+
|
|
40
|
+
timeout: int = 300
|
|
41
|
+
"""Total timeout in seconds for WSDL fetches."""
|
|
42
|
+
|
|
43
|
+
operation_timeout: int = 60
|
|
44
|
+
"""Total timeout in seconds for SOAP operation calls."""
|
|
45
|
+
|
|
46
|
+
verify_ssl: bool = True
|
|
47
|
+
proxy: str | None = None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class AsyncTransport(Transport):
|
|
51
|
+
"""
|
|
52
|
+
Async transport for zeep using aiohttp.
|
|
53
|
+
|
|
54
|
+
Usage::
|
|
55
|
+
|
|
56
|
+
async with soap_client("https://example.com/service?wsdl") as client:
|
|
57
|
+
result = await client.service.SomeOperation(...)
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
def __init__(
|
|
61
|
+
self,
|
|
62
|
+
*,
|
|
63
|
+
session: ClientSession,
|
|
64
|
+
config: TransportConfig,
|
|
65
|
+
_owns_session: bool = False,
|
|
66
|
+
) -> None:
|
|
67
|
+
super().__init__()
|
|
68
|
+
self._session = session
|
|
69
|
+
self._config = config
|
|
70
|
+
self._owns_session = _owns_session
|
|
71
|
+
|
|
72
|
+
@classmethod
|
|
73
|
+
def from_config(
|
|
74
|
+
cls,
|
|
75
|
+
config: TransportConfig | None = None,
|
|
76
|
+
*,
|
|
77
|
+
session: ClientSession | None = None,
|
|
78
|
+
) -> Self:
|
|
79
|
+
"""
|
|
80
|
+
Create a transport, optionally sharing an existing ClientSession.
|
|
81
|
+
|
|
82
|
+
If *session* is omitted the transport owns (and will close) the
|
|
83
|
+
session it creates.
|
|
84
|
+
"""
|
|
85
|
+
config = config or TransportConfig()
|
|
86
|
+
owns_session = session is None
|
|
87
|
+
|
|
88
|
+
if owns_session:
|
|
89
|
+
session = ClientSession(
|
|
90
|
+
connector=TCPConnector(ssl=config.verify_ssl),
|
|
91
|
+
timeout=ClientTimeout(total=config.operation_timeout),
|
|
92
|
+
headers={'User-Agent': f'Zeep/{get_version()} (www.python-zeep.org)'},
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
return cls(session=session, config=config, _owns_session=owns_session)
|
|
96
|
+
|
|
97
|
+
async def aclose(self) -> None:
|
|
98
|
+
if self._owns_session:
|
|
99
|
+
await self._session.close()
|
|
100
|
+
|
|
101
|
+
async def __aenter__(self) -> Self:
|
|
102
|
+
return self
|
|
103
|
+
|
|
104
|
+
async def __aexit__(self, *_: object) -> None:
|
|
105
|
+
await self.aclose()
|
|
106
|
+
|
|
107
|
+
@staticmethod
|
|
108
|
+
def _build_response(response: aiohttp.ClientResponse, body: bytes) -> Response:
|
|
109
|
+
"""Convert an aiohttp response into a requests.Response for zeep."""
|
|
110
|
+
r = Response()
|
|
111
|
+
r.status_code = response.status
|
|
112
|
+
r._content = body # noqa: SLF001
|
|
113
|
+
r.headers = dict(response.headers)
|
|
114
|
+
r.encoding = response.charset
|
|
115
|
+
r.url = str(response.url)
|
|
116
|
+
|
|
117
|
+
jar = RequestsCookieJar()
|
|
118
|
+
|
|
119
|
+
for name, morsel in response.cookies.items():
|
|
120
|
+
jar.set(name, morsel.value)
|
|
121
|
+
|
|
122
|
+
r.cookies = jar
|
|
123
|
+
|
|
124
|
+
return r
|
|
125
|
+
|
|
126
|
+
def load(self, url: str) -> bytes:
|
|
127
|
+
"""
|
|
128
|
+
Sync entry-point zeep calls during WSDL document init.
|
|
129
|
+
|
|
130
|
+
Creates a short-lived session confined to its own event loop so there
|
|
131
|
+
is no cross-loop session sharing with the operational session.
|
|
132
|
+
"""
|
|
133
|
+
if not url:
|
|
134
|
+
return b''
|
|
135
|
+
|
|
136
|
+
async def _fetch() -> bytes:
|
|
137
|
+
async with (
|
|
138
|
+
ClientSession(
|
|
139
|
+
connector=TCPConnector(ssl=self._config.verify_ssl),
|
|
140
|
+
timeout=ClientTimeout(total=self._config.timeout),
|
|
141
|
+
headers={'User-Agent': f'Zeep/{get_version()} (www.python-zeep.org)'},
|
|
142
|
+
) as session,
|
|
143
|
+
session.get(url, proxy=self._config.proxy) as resp,
|
|
144
|
+
):
|
|
145
|
+
content = await resp.read()
|
|
146
|
+
|
|
147
|
+
if resp.status >= 400:
|
|
148
|
+
raise TransportError(
|
|
149
|
+
status_code=resp.status,
|
|
150
|
+
message=content.decode(errors='ignore'),
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
return content
|
|
154
|
+
|
|
155
|
+
# load() is always called from within a running event loop (during
|
|
156
|
+
# AsyncClient.__init__ inside the soap_client context manager), so
|
|
157
|
+
# run_until_complete on the running loop would raise. Always delegate
|
|
158
|
+
# to a thread with its own fresh event loop.
|
|
159
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
|
|
160
|
+
return pool.submit(asyncio.run, _fetch()).result()
|
|
161
|
+
|
|
162
|
+
async def post(
|
|
163
|
+
self,
|
|
164
|
+
address: str,
|
|
165
|
+
message: bytes,
|
|
166
|
+
headers: dict[str, str],
|
|
167
|
+
*,
|
|
168
|
+
timeout: int | None = None,
|
|
169
|
+
) -> Response:
|
|
170
|
+
logger.debug('SOAP POST → %s\n%s', address, message)
|
|
171
|
+
|
|
172
|
+
async with self._session.post(
|
|
173
|
+
address,
|
|
174
|
+
data=message,
|
|
175
|
+
headers=headers,
|
|
176
|
+
proxy=self._config.proxy,
|
|
177
|
+
timeout=ClientTimeout(total=timeout) if timeout is not None else None,
|
|
178
|
+
) as resp:
|
|
179
|
+
body = await resp.read()
|
|
180
|
+
logger.debug('SOAP ← %s (HTTP %d)\n%s', address, resp.status, body)
|
|
181
|
+
|
|
182
|
+
return self._build_response(resp, body)
|
|
183
|
+
|
|
184
|
+
async def post_xml(
|
|
185
|
+
self,
|
|
186
|
+
address: str,
|
|
187
|
+
envelope: Any,
|
|
188
|
+
headers: dict[str, str],
|
|
189
|
+
) -> Response:
|
|
190
|
+
return await self.post(address, etree_to_string(envelope), headers)
|
|
191
|
+
|
|
192
|
+
async def get(
|
|
193
|
+
self,
|
|
194
|
+
address: str,
|
|
195
|
+
params: dict[str, str],
|
|
196
|
+
headers: dict[str, str],
|
|
197
|
+
) -> Response:
|
|
198
|
+
async with self._session.get(
|
|
199
|
+
address,
|
|
200
|
+
params=params,
|
|
201
|
+
headers=headers,
|
|
202
|
+
proxy=self._config.proxy,
|
|
203
|
+
) as resp:
|
|
204
|
+
body = await resp.read()
|
|
205
|
+
|
|
206
|
+
return self._build_response(resp, body)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def build_soap_client(
|
|
210
|
+
wsdl_url: str,
|
|
211
|
+
transport: AsyncTransport,
|
|
212
|
+
plugins: Sequence[Plugin] | None = None,
|
|
213
|
+
) -> AsyncClient:
|
|
214
|
+
if not wsdl_url:
|
|
215
|
+
msg = 'wsdl_url must be a non-empty string.'
|
|
216
|
+
|
|
217
|
+
raise ValueError(msg)
|
|
218
|
+
|
|
219
|
+
return AsyncClient(wsdl_url, transport=transport, plugins=list(plugins or []))
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
@asynccontextmanager
|
|
223
|
+
async def soap_client(
|
|
224
|
+
wsdl_url: str,
|
|
225
|
+
*,
|
|
226
|
+
config: TransportConfig | None = None,
|
|
227
|
+
session: ClientSession | None = None,
|
|
228
|
+
plugins: Sequence[Plugin] | None = None,
|
|
229
|
+
) -> AsyncIterator[AsyncClient]:
|
|
230
|
+
"""
|
|
231
|
+
Async context manager yielding a ready-to-use zeep AsyncClient.
|
|
232
|
+
|
|
233
|
+
Example::
|
|
234
|
+
|
|
235
|
+
async with soap_client("https://example.com/service?wsdl") as client:
|
|
236
|
+
result = await client.service.GetData(id=42)
|
|
237
|
+
"""
|
|
238
|
+
transport = AsyncTransport.from_config(config, session=session)
|
|
239
|
+
|
|
240
|
+
try:
|
|
241
|
+
yield build_soap_client(wsdl_url, transport, plugins)
|
|
242
|
+
finally:
|
|
243
|
+
await transport.aclose()
|
|
@@ -1,299 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Async SOAP client built on aiohttp + zeep.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from __future__ import annotations
|
|
6
|
-
|
|
7
|
-
import asyncio
|
|
8
|
-
import concurrent.futures
|
|
9
|
-
import logging
|
|
10
|
-
from collections.abc import AsyncIterator, Sequence
|
|
11
|
-
from contextlib import asynccontextmanager
|
|
12
|
-
from dataclasses import dataclass
|
|
13
|
-
from typing import TYPE_CHECKING, Any, Self
|
|
14
|
-
|
|
15
|
-
try:
|
|
16
|
-
import aiohttp
|
|
17
|
-
from aiohttp import ClientSession, ClientTimeout, TCPConnector
|
|
18
|
-
from requests import Response
|
|
19
|
-
from requests.cookies import RequestsCookieJar
|
|
20
|
-
from zeep import AsyncClient
|
|
21
|
-
from zeep.exceptions import TransportError
|
|
22
|
-
from zeep.transports import Transport
|
|
23
|
-
from zeep.utils import get_version
|
|
24
|
-
from zeep.wsdl.utils import etree_to_string
|
|
25
|
-
except ImportError as e:
|
|
26
|
-
msg = 'Install python3-commons[soap-client] to use this feature'
|
|
27
|
-
|
|
28
|
-
raise RuntimeError(msg) from e
|
|
29
|
-
|
|
30
|
-
if TYPE_CHECKING:
|
|
31
|
-
from zeep.plugins import Plugin
|
|
32
|
-
|
|
33
|
-
logger = logging.getLogger(__name__)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
@dataclass(frozen=True, slots=True)
|
|
37
|
-
class TransportConfig:
|
|
38
|
-
"""Immutable transport settings passed to AsyncTransport."""
|
|
39
|
-
|
|
40
|
-
timeout: int = 300
|
|
41
|
-
"""Total timeout in seconds for WSDL fetches."""
|
|
42
|
-
|
|
43
|
-
operation_timeout: int = 60
|
|
44
|
-
"""Total timeout in seconds for SOAP operation calls."""
|
|
45
|
-
|
|
46
|
-
verify_ssl: bool = True
|
|
47
|
-
proxy: str | None = None
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
class AsyncTransport(Transport):
|
|
51
|
-
"""
|
|
52
|
-
Async transport for zeep using aiohttp.
|
|
53
|
-
|
|
54
|
-
Usage::
|
|
55
|
-
|
|
56
|
-
async with AsyncTransport.from_config(config) as transport:
|
|
57
|
-
client = AsyncClient(wsdl_url, transport=transport)
|
|
58
|
-
result = await client.service.SomeOperation(...)
|
|
59
|
-
"""
|
|
60
|
-
|
|
61
|
-
def __init__(
|
|
62
|
-
self,
|
|
63
|
-
*,
|
|
64
|
-
session: ClientSession,
|
|
65
|
-
wsdl_session: ClientSession,
|
|
66
|
-
config: TransportConfig,
|
|
67
|
-
_owns_session: bool = False,
|
|
68
|
-
_owns_wsdl_session: bool = False,
|
|
69
|
-
) -> None:
|
|
70
|
-
super().__init__()
|
|
71
|
-
self._session = session
|
|
72
|
-
self._wsdl_session = wsdl_session
|
|
73
|
-
self._config = config
|
|
74
|
-
self._owns_session = _owns_session
|
|
75
|
-
self._owns_wsdl_session = _owns_wsdl_session
|
|
76
|
-
|
|
77
|
-
@classmethod
|
|
78
|
-
def from_config(
|
|
79
|
-
cls,
|
|
80
|
-
config: TransportConfig | None = None,
|
|
81
|
-
*,
|
|
82
|
-
session: ClientSession | None = None,
|
|
83
|
-
wsdl_session: ClientSession | None = None,
|
|
84
|
-
) -> AsyncTransport:
|
|
85
|
-
"""
|
|
86
|
-
Create a transport, optionally sharing an existing ClientSession.
|
|
87
|
-
|
|
88
|
-
If *session* / *wsdl_session* are omitted the transport owns (and
|
|
89
|
-
will close) the sessions it creates.
|
|
90
|
-
"""
|
|
91
|
-
config = config or TransportConfig()
|
|
92
|
-
connector = TCPConnector(ssl=config.verify_ssl)
|
|
93
|
-
user_agent = f'Zeep/{get_version()} (www.python-zeep.org)'
|
|
94
|
-
|
|
95
|
-
owns_session = session is None
|
|
96
|
-
owns_wsdl_session = wsdl_session is None
|
|
97
|
-
|
|
98
|
-
if owns_session:
|
|
99
|
-
session = ClientSession(
|
|
100
|
-
connector=connector,
|
|
101
|
-
timeout=ClientTimeout(total=config.operation_timeout),
|
|
102
|
-
headers={'User-Agent': user_agent},
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
if owns_wsdl_session:
|
|
106
|
-
wsdl_session = ClientSession(
|
|
107
|
-
connector=connector,
|
|
108
|
-
timeout=ClientTimeout(total=config.timeout),
|
|
109
|
-
headers={'User-Agent': user_agent},
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
return cls(
|
|
113
|
-
session=session,
|
|
114
|
-
wsdl_session=wsdl_session,
|
|
115
|
-
config=config,
|
|
116
|
-
_owns_session=owns_session,
|
|
117
|
-
_owns_wsdl_session=owns_wsdl_session,
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
async def aclose(self) -> None:
|
|
121
|
-
if self._owns_session:
|
|
122
|
-
await self._session.close()
|
|
123
|
-
if self._owns_wsdl_session:
|
|
124
|
-
await self._wsdl_session.close()
|
|
125
|
-
|
|
126
|
-
async def __aenter__(self) -> Self:
|
|
127
|
-
return self
|
|
128
|
-
|
|
129
|
-
async def __aexit__(self, *_: object) -> None:
|
|
130
|
-
await self.aclose()
|
|
131
|
-
|
|
132
|
-
@staticmethod
|
|
133
|
-
def _build_response(response: aiohttp.ClientResponse, body: bytes) -> Response:
|
|
134
|
-
"""Convert an aiohttp response into a requests.Response for zeep."""
|
|
135
|
-
r = Response()
|
|
136
|
-
r.status_code = response.status
|
|
137
|
-
r._content = body # noqa: SLF001 (zeep reads this attribute directly)
|
|
138
|
-
r.headers = dict(response.headers)
|
|
139
|
-
r.encoding = response.charset
|
|
140
|
-
r.url = str(response.url)
|
|
141
|
-
|
|
142
|
-
# Bridge aiohttp SimpleCookie → RequestsCookieJar so zeep / requests
|
|
143
|
-
# cookie handling works correctly.
|
|
144
|
-
jar = RequestsCookieJar()
|
|
145
|
-
|
|
146
|
-
for name, morsel in response.cookies.items():
|
|
147
|
-
jar.set(name, morsel.value)
|
|
148
|
-
|
|
149
|
-
r.cookies = jar
|
|
150
|
-
|
|
151
|
-
return r
|
|
152
|
-
|
|
153
|
-
async def _load_remote_data(self, url: str) -> bytes:
|
|
154
|
-
"""Fetch WSDL / XSD documents (called by zeep during init)."""
|
|
155
|
-
|
|
156
|
-
async def _fetch() -> bytes:
|
|
157
|
-
async with self._wsdl_session.get(url, proxy=self._config.proxy) as resp:
|
|
158
|
-
content = await resp.read()
|
|
159
|
-
|
|
160
|
-
if resp.status >= 400:
|
|
161
|
-
raise TransportError(
|
|
162
|
-
status_code=resp.status,
|
|
163
|
-
message=content.decode(errors='ignore'),
|
|
164
|
-
)
|
|
165
|
-
|
|
166
|
-
return content
|
|
167
|
-
|
|
168
|
-
return await _fetch()
|
|
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
|
-
async def _fetch() -> bytes:
|
|
176
|
-
connector = TCPConnector(ssl=self._config.verify_ssl)
|
|
177
|
-
|
|
178
|
-
async with ClientSession(
|
|
179
|
-
connector=connector,
|
|
180
|
-
timeout=ClientTimeout(total=self._config.timeout),
|
|
181
|
-
headers={'User-Agent': f'Zeep/{get_version()} (www.python-zeep.org)'},
|
|
182
|
-
) as session, session.get(url, proxy=self._config.proxy) as resp:
|
|
183
|
-
content = await resp.read()
|
|
184
|
-
|
|
185
|
-
if resp.status >= 400:
|
|
186
|
-
raise TransportError(
|
|
187
|
-
status_code=resp.status,
|
|
188
|
-
message=content.decode(errors='ignore'),
|
|
189
|
-
)
|
|
190
|
-
|
|
191
|
-
return content
|
|
192
|
-
|
|
193
|
-
try:
|
|
194
|
-
loop = asyncio.get_event_loop()
|
|
195
|
-
|
|
196
|
-
return loop.run_until_complete(_fetch())
|
|
197
|
-
except RuntimeError:
|
|
198
|
-
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
|
|
199
|
-
future = pool.submit(asyncio.run, _fetch())
|
|
200
|
-
|
|
201
|
-
return future.result()
|
|
202
|
-
|
|
203
|
-
async def post(
|
|
204
|
-
self,
|
|
205
|
-
address: str,
|
|
206
|
-
message: bytes,
|
|
207
|
-
headers: dict[str, str],
|
|
208
|
-
*,
|
|
209
|
-
timeout: int | None = None,
|
|
210
|
-
) -> Response:
|
|
211
|
-
logger.debug('SOAP POST → %s\n%s', address, message)
|
|
212
|
-
|
|
213
|
-
request_timeout = ClientTimeout(total=timeout) if timeout is not None else None
|
|
214
|
-
|
|
215
|
-
async def _post() -> Response:
|
|
216
|
-
async with self._session.post(
|
|
217
|
-
address,
|
|
218
|
-
data=message,
|
|
219
|
-
headers=headers,
|
|
220
|
-
proxy=self._config.proxy,
|
|
221
|
-
timeout=request_timeout,
|
|
222
|
-
) as resp:
|
|
223
|
-
body = await resp.read()
|
|
224
|
-
logger.debug('SOAP ← %s (HTTP %d)\n%s', address, resp.status, body)
|
|
225
|
-
|
|
226
|
-
return self._build_response(resp, body)
|
|
227
|
-
|
|
228
|
-
return await _post()
|
|
229
|
-
|
|
230
|
-
async def post_xml(
|
|
231
|
-
self,
|
|
232
|
-
address: str,
|
|
233
|
-
envelope: Any,
|
|
234
|
-
headers: dict[str, str],
|
|
235
|
-
) -> Response:
|
|
236
|
-
message = etree_to_string(envelope)
|
|
237
|
-
|
|
238
|
-
return await self.post(address, message, headers)
|
|
239
|
-
|
|
240
|
-
async def get(
|
|
241
|
-
self,
|
|
242
|
-
address: str,
|
|
243
|
-
params: dict[str, str],
|
|
244
|
-
headers: dict[str, str],
|
|
245
|
-
) -> Response:
|
|
246
|
-
async with self._session.get(
|
|
247
|
-
address,
|
|
248
|
-
params=params,
|
|
249
|
-
headers=headers,
|
|
250
|
-
proxy=self._config.proxy,
|
|
251
|
-
) as resp:
|
|
252
|
-
body = await resp.read()
|
|
253
|
-
|
|
254
|
-
return self._build_response(resp, body)
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
def build_soap_client(
|
|
258
|
-
wsdl_url: str,
|
|
259
|
-
transport: AsyncTransport,
|
|
260
|
-
plugins: Sequence[Plugin] | None = None,
|
|
261
|
-
) -> AsyncClient:
|
|
262
|
-
"""
|
|
263
|
-
Construct a zeep AsyncClient with the supplied transport and plugins.
|
|
264
|
-
|
|
265
|
-
Raises ValueError if *wsdl_url* is empty or None.
|
|
266
|
-
"""
|
|
267
|
-
if not wsdl_url:
|
|
268
|
-
msg = 'wsdl_url must be a non-empty string.'
|
|
269
|
-
|
|
270
|
-
raise ValueError(msg)
|
|
271
|
-
|
|
272
|
-
return AsyncClient(wsdl_url, transport=transport, plugins=list(plugins or []))
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
@asynccontextmanager
|
|
276
|
-
async def soap_client(
|
|
277
|
-
wsdl_url: str,
|
|
278
|
-
*,
|
|
279
|
-
config: TransportConfig | None = None,
|
|
280
|
-
session: ClientSession | None = None,
|
|
281
|
-
plugins: Sequence[Plugin] | None = None,
|
|
282
|
-
) -> AsyncIterator[AsyncClient]:
|
|
283
|
-
"""
|
|
284
|
-
Async context manager that yields a ready-to-use zeep AsyncClient and
|
|
285
|
-
cleans up the transport on exit.
|
|
286
|
-
|
|
287
|
-
Example::
|
|
288
|
-
|
|
289
|
-
async with soap_client('https://example.com/service?wsdl') as client:
|
|
290
|
-
result = await client.service.GetData(id=42)
|
|
291
|
-
"""
|
|
292
|
-
transport = AsyncTransport.from_config(config, session=session)
|
|
293
|
-
|
|
294
|
-
try:
|
|
295
|
-
client = build_soap_client(wsdl_url, transport, plugins) # WSDL loaded here (sync via load())
|
|
296
|
-
|
|
297
|
-
yield client
|
|
298
|
-
finally:
|
|
299
|
-
await transport.aclose()
|
|
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.4 → python3_commons-0.18.5}/src/python3_commons/serializers/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/serializers/msgpack.py
RENAMED
|
File without changes
|
{python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons/serializers/msgspec.py
RENAMED
|
File without changes
|
|
File without changes
|
{python3_commons-0.18.4 → python3_commons-0.18.5}/src/python3_commons.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
{python3_commons-0.18.4 → python3_commons-0.18.5}/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
|