python3-commons 0.17.8__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.
Files changed (74) hide show
  1. {python3_commons-0.17.8/src/python3_commons.egg-info → python3_commons-0.18.1}/PKG-INFO +8 -5
  2. {python3_commons-0.17.8 → python3_commons-0.18.1}/pyproject.toml +8 -4
  3. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/audit.py +15 -3
  4. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/auth.py +15 -15
  5. python3_commons-0.18.1/src/python3_commons/soap_client.py +260 -0
  6. {python3_commons-0.17.8 → python3_commons-0.18.1/src/python3_commons.egg-info}/PKG-INFO +8 -5
  7. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons.egg-info/SOURCES.txt +1 -0
  8. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons.egg-info/requires.txt +8 -4
  9. {python3_commons-0.17.8 → python3_commons-0.18.1}/uv.lock +71 -9
  10. {python3_commons-0.17.8 → python3_commons-0.18.1}/.coveragerc +0 -0
  11. {python3_commons-0.17.8 → python3_commons-0.18.1}/.devcontainer/Dockerfile +0 -0
  12. {python3_commons-0.17.8 → python3_commons-0.18.1}/.devcontainer/devcontainer.json +0 -0
  13. {python3_commons-0.17.8 → python3_commons-0.18.1}/.devcontainer/docker-compose.yml +0 -0
  14. {python3_commons-0.17.8 → python3_commons-0.18.1}/.env_template +0 -0
  15. {python3_commons-0.17.8 → python3_commons-0.18.1}/.github/workflows/checks.yml +0 -0
  16. {python3_commons-0.17.8 → python3_commons-0.18.1}/.github/workflows/python-publish.yaml +0 -0
  17. {python3_commons-0.17.8 → python3_commons-0.18.1}/.github/workflows/release-on-tag-push.yml +0 -0
  18. {python3_commons-0.17.8 → python3_commons-0.18.1}/.gitignore +0 -0
  19. {python3_commons-0.17.8 → python3_commons-0.18.1}/.pre-commit-config.yaml +0 -0
  20. {python3_commons-0.17.8 → python3_commons-0.18.1}/.python-version +0 -0
  21. {python3_commons-0.17.8 → python3_commons-0.18.1}/AUTHORS.rst +0 -0
  22. {python3_commons-0.17.8 → python3_commons-0.18.1}/CHANGELOG.rst +0 -0
  23. {python3_commons-0.17.8 → python3_commons-0.18.1}/LICENSE +0 -0
  24. {python3_commons-0.17.8 → python3_commons-0.18.1}/README.md +0 -0
  25. {python3_commons-0.17.8 → python3_commons-0.18.1}/README.rst +0 -0
  26. {python3_commons-0.17.8 → python3_commons-0.18.1}/docs/Makefile +0 -0
  27. {python3_commons-0.17.8 → python3_commons-0.18.1}/docs/_static/.gitignore +0 -0
  28. {python3_commons-0.17.8 → python3_commons-0.18.1}/docs/authors.rst +0 -0
  29. {python3_commons-0.17.8 → python3_commons-0.18.1}/docs/changelog.rst +0 -0
  30. {python3_commons-0.17.8 → python3_commons-0.18.1}/docs/conf.py +0 -0
  31. {python3_commons-0.17.8 → python3_commons-0.18.1}/docs/index.rst +0 -0
  32. {python3_commons-0.17.8 → python3_commons-0.18.1}/docs/license.rst +0 -0
  33. {python3_commons-0.17.8 → python3_commons-0.18.1}/setup.cfg +0 -0
  34. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/__init__.py +0 -0
  35. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/api_client.py +0 -0
  36. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/async_functools.py +0 -0
  37. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/cache.py +0 -0
  38. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/conf.py +0 -0
  39. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/db/__init__.py +0 -0
  40. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/db/helpers.py +0 -0
  41. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/db/models/__init__.py +0 -0
  42. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/db/models/auth.py +0 -0
  43. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/db/models/common.py +0 -0
  44. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/db/models/rbac.py +0 -0
  45. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/db/models/users.py +0 -0
  46. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/exceptions.py +0 -0
  47. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/fs.py +0 -0
  48. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/generators.py +0 -0
  49. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/helpers.py +0 -0
  50. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/log/__init__.py +0 -0
  51. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/log/filters.py +0 -0
  52. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/log/formatters.py +0 -0
  53. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/object_storage.py +0 -0
  54. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/permissions.py +0 -0
  55. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/serializers/__init__.py +0 -0
  56. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/serializers/common.py +0 -0
  57. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/serializers/json.py +0 -0
  58. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/serializers/msgpack.py +0 -0
  59. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons/serializers/msgspec.py +0 -0
  60. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons.egg-info/dependency_links.txt +0 -0
  61. {python3_commons-0.17.8 → python3_commons-0.18.1}/src/python3_commons.egg-info/top_level.txt +0 -0
  62. {python3_commons-0.17.8 → python3_commons-0.18.1}/tests/__init__.py +0 -0
  63. {python3_commons-0.17.8 → python3_commons-0.18.1}/tests/integration/__init__.py +0 -0
  64. {python3_commons-0.17.8 → python3_commons-0.18.1}/tests/integration/test_cache.py +0 -0
  65. {python3_commons-0.17.8 → python3_commons-0.18.1}/tests/integration/test_osc.py +0 -0
  66. {python3_commons-0.17.8 → python3_commons-0.18.1}/tests/unit/__init__.py +0 -0
  67. {python3_commons-0.17.8 → python3_commons-0.18.1}/tests/unit/conftest.py +0 -0
  68. {python3_commons-0.17.8 → python3_commons-0.18.1}/tests/unit/log/__init__.py +0 -0
  69. {python3_commons-0.17.8 → python3_commons-0.18.1}/tests/unit/log/test_formatters.py +0 -0
  70. {python3_commons-0.17.8 → python3_commons-0.18.1}/tests/unit/test_async_functools.py +0 -0
  71. {python3_commons-0.17.8 → python3_commons-0.18.1}/tests/unit/test_audit.py +0 -0
  72. {python3_commons-0.17.8 → python3_commons-0.18.1}/tests/unit/test_helpers.py +0 -0
  73. {python3_commons-0.17.8 → python3_commons-0.18.1}/tests/unit/test_msgpack.py +0 -0
  74. {python3_commons-0.17.8 → python3_commons-0.18.1}/tests/unit/test_msgspec.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python3-commons
3
- Version: 0.17.8
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
@@ -16,15 +16,13 @@ Requires-Dist: msgpack~=1.1.2
16
16
  Requires-Dist: msgspec==0.21.1
17
17
  Requires-Dist: pydantic-settings~=2.14.0
18
18
  Provides-Extra: all
19
- Requires-Dist: python3_commons[api-client,audit,authn,authz,cache,database,object-storage]; extra == "all"
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
- Requires-Dist: zeep~=4.3.2; extra == "api-client"
24
22
  Requires-Dist: python3_commons[object-storage]; extra == "api-client"
25
23
  Provides-Extra: audit
26
24
  Requires-Dist: lxml~=6.1.0; extra == "audit"
27
- Requires-Dist: zeep~=4.3.2; extra == "audit"
25
+ Requires-Dist: zeep[async]~=4.3.2; extra == "audit"
28
26
  Requires-Dist: python3_commons[object-storage]; extra == "audit"
29
27
  Provides-Extra: authn
30
28
  Requires-Dist: aiohttp[speedups]<3.15.0,>=3.13.5; extra == "authn"
@@ -40,6 +38,11 @@ Requires-Dist: SQLAlchemy[asyncio]~=2.0.49; extra == "database"
40
38
  Provides-Extra: object-storage
41
39
  Requires-Dist: aiobotocore~=3.5.0; extra == "object-storage"
42
40
  Requires-Dist: object-storage-client==0.0.23; extra == "object-storage"
41
+ Provides-Extra: soap-client
42
+ Requires-Dist: aiohttp[speedups]<3.15.0,>=3.13.5; extra == "soap-client"
43
+ Requires-Dist: lxml~=6.1.0; extra == "soap-client"
44
+ Requires-Dist: requests~=2.33.1; extra == "soap-client"
45
+ Requires-Dist: zeep[async]~=4.3.2; extra == "soap-client"
43
46
  Dynamic: license-file
44
47
 
45
48
  Re-usable Python3 code
@@ -25,17 +25,15 @@ dependencies = [
25
25
 
26
26
  [project.optional-dependencies]
27
27
  all = [
28
- "python3_commons[api-client,audit,authn,authz,cache,database,object-storage]"
28
+ "python3_commons[api-client,audit,authn,authz,cache,database,object-storage,soap-client]"
29
29
  ]
30
30
  api-client = [
31
31
  "aiohttp[speedups]>=3.13.5,<3.15.0",
32
- "lxml~=6.1.0",
33
- "zeep~=4.3.2",
34
32
  "python3_commons[object-storage]"
35
33
  ]
36
34
  audit = [
37
35
  "lxml~=6.1.0",
38
- "zeep~=4.3.2",
36
+ "zeep[async]~=4.3.2",
39
37
  "python3_commons[object-storage]"
40
38
  ]
41
39
  authn = [
@@ -57,6 +55,12 @@ object-storage = [
57
55
  "aiobotocore~=3.5.0",
58
56
  "object-storage-client==0.0.23"
59
57
  ]
58
+ soap-client = [
59
+ "aiohttp[speedups]>=3.13.5,<3.15.0",
60
+ "lxml~=6.1.0",
61
+ "requests~=2.33.1",
62
+ "zeep[async]~=4.3.2",
63
+ ]
60
64
 
61
65
 
62
66
  [dependency-groups]
@@ -7,17 +7,21 @@ from uuid import uuid4
7
7
 
8
8
  try:
9
9
  from lxml import etree
10
- from zeep.plugins import Plugin
10
+ from zeep.plugins import HistoryPlugin, Plugin
11
11
  except ImportError as e:
12
- msg = 'Install python3-commons[api-client] to use this feature'
12
+ msg = 'Install python3-commons[audit] to use this feature'
13
+
13
14
  raise RuntimeError(msg) from e
14
15
 
15
16
  from python3_commons import object_storage
16
- from python3_commons.conf import S3Settings, s3_settings
17
+ from python3_commons.conf import s3_settings
17
18
 
18
19
  if TYPE_CHECKING:
20
+ from zeep import AsyncClient
19
21
  from zeep.wsdl.definitions import AbstractOperation
20
22
 
23
+ from python3_commons.conf import S3Settings
24
+
21
25
  logger = logging.getLogger(__name__)
22
26
 
23
27
 
@@ -65,3 +69,11 @@ class ZeepAuditPlugin(Plugin):
65
69
  self.store_audit_in_s3(envelope, operation, 'egress')
66
70
 
67
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
+ )
@@ -101,13 +101,13 @@ class OIDCAuthError(OIDCError):
101
101
  # TODO: use api_client
102
102
  class OIDCClient:
103
103
  def __init__(
104
- self,
105
- authority_url: str,
106
- client_id: str,
107
- client_secret: str | None = None,
108
- *,
109
- timeout: float = 10.0,
110
- session: aiohttp.ClientSession | None = None,
104
+ self,
105
+ authority_url: str,
106
+ client_id: str,
107
+ client_secret: str | None = None,
108
+ *,
109
+ timeout: float = 10.0,
110
+ session: aiohttp.ClientSession | None = None,
111
111
  ) -> None:
112
112
  self._token_url = f'{authority_url}/protocol/openid-connect/token' # TODO: get it from openid-configuration
113
113
  self._client_id = client_id
@@ -126,11 +126,11 @@ class OIDCClient:
126
126
  await self._session.close()
127
127
 
128
128
  async def fetch_token(
129
- self,
130
- *,
131
- username: str,
132
- password: str,
133
- scope: str = 'openid profile email',
129
+ self,
130
+ *,
131
+ username: str,
132
+ password: str,
133
+ scope: str = 'openid profile email',
134
134
  ) -> OIDCTokenResponse:
135
135
  if self._session is None:
136
136
  msg = 'ClientSession not initialized'
@@ -150,9 +150,9 @@ class OIDCClient:
150
150
 
151
151
  try:
152
152
  async with self._session.post(
153
- self._token_url,
154
- data=data,
155
- headers={'Content-Type': 'application/x-www-form-urlencoded'},
153
+ self._token_url,
154
+ data=data,
155
+ headers={'Content-Type': 'application/x-www-form-urlencoded'},
156
156
  ) as resp:
157
157
  payload = await resp.read()
158
158
  decoder = msgspec.json.Decoder(type=OIDCTokenResponse)
@@ -0,0 +1,260 @@
1
+ """
2
+ Async SOAP client built on aiohttp + zeep.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import logging
8
+ from collections.abc import AsyncIterator, Sequence
9
+ from contextlib import asynccontextmanager
10
+ from dataclasses import dataclass
11
+ from typing import TYPE_CHECKING, Any, Self
12
+
13
+ try:
14
+ import aiohttp
15
+ from aiohttp import ClientSession, ClientTimeout, TCPConnector
16
+ from requests import Response
17
+ from requests.cookies import RequestsCookieJar
18
+ from zeep import AsyncClient
19
+ from zeep.exceptions import TransportError
20
+ from zeep.transports import Transport
21
+ from zeep.utils import get_version
22
+ from zeep.wsdl.utils import etree_to_string
23
+ except ImportError as e:
24
+ msg = 'Install python3-commons[soap-client] to use this feature'
25
+
26
+ raise RuntimeError(msg) from e
27
+
28
+ if TYPE_CHECKING:
29
+ from zeep.plugins import Plugin
30
+
31
+ logger = logging.getLogger(__name__)
32
+
33
+
34
+ @dataclass(frozen=True, slots=True)
35
+ class TransportConfig:
36
+ """Immutable transport settings passed to AsyncTransport."""
37
+
38
+ timeout: int = 300
39
+ """Total timeout in seconds for WSDL fetches."""
40
+
41
+ operation_timeout: int = 60
42
+ """Total timeout in seconds for SOAP operation calls."""
43
+
44
+ verify_ssl: bool = True
45
+ proxy: str | None = None
46
+
47
+
48
+ class AsyncTransport(Transport):
49
+ """
50
+ Async transport for zeep using aiohttp.
51
+
52
+ Usage::
53
+
54
+ async with AsyncTransport.from_config(config) as transport:
55
+ client = AsyncClient(wsdl_url, transport=transport)
56
+ result = await client.service.SomeOperation(...)
57
+ """
58
+
59
+ def __init__(
60
+ self,
61
+ *,
62
+ session: ClientSession,
63
+ wsdl_session: ClientSession,
64
+ config: TransportConfig,
65
+ _owns_session: bool = False,
66
+ _owns_wsdl_session: bool = False,
67
+ ) -> None:
68
+ super().__init__()
69
+ self._session = session
70
+ self._wsdl_session = wsdl_session
71
+ self._config = config
72
+ self._owns_session = _owns_session
73
+ self._owns_wsdl_session = _owns_wsdl_session
74
+
75
+ @classmethod
76
+ def from_config(
77
+ cls,
78
+ config: TransportConfig | None = None,
79
+ *,
80
+ session: ClientSession | None = None,
81
+ wsdl_session: ClientSession | None = None,
82
+ ) -> AsyncTransport:
83
+ """
84
+ Create a transport, optionally sharing an existing ClientSession.
85
+
86
+ If *session* / *wsdl_session* are omitted the transport owns (and
87
+ will close) the sessions it creates.
88
+ """
89
+ config = config or TransportConfig()
90
+ connector = TCPConnector(ssl=config.verify_ssl)
91
+ user_agent = f'Zeep/{get_version()} (www.python-zeep.org)'
92
+
93
+ owns_session = session is None
94
+ owns_wsdl_session = wsdl_session is None
95
+
96
+ if owns_session:
97
+ session = ClientSession(
98
+ connector=connector,
99
+ timeout=ClientTimeout(total=config.operation_timeout),
100
+ headers={'User-Agent': user_agent},
101
+ )
102
+
103
+ if owns_wsdl_session:
104
+ wsdl_session = ClientSession(
105
+ connector=connector,
106
+ timeout=ClientTimeout(total=config.timeout),
107
+ headers={'User-Agent': user_agent},
108
+ )
109
+
110
+ return cls(
111
+ session=session,
112
+ wsdl_session=wsdl_session,
113
+ config=config,
114
+ _owns_session=owns_session,
115
+ _owns_wsdl_session=owns_wsdl_session,
116
+ )
117
+
118
+ async def aclose(self) -> None:
119
+ if self._owns_session:
120
+ await self._session.close()
121
+ if self._owns_wsdl_session:
122
+ await self._wsdl_session.close()
123
+
124
+ async def __aenter__(self) -> Self:
125
+ return self
126
+
127
+ async def __aexit__(self, *_: object) -> None:
128
+ await self.aclose()
129
+
130
+ @staticmethod
131
+ def _build_response(response: aiohttp.ClientResponse, body: bytes) -> Response:
132
+ """Convert an aiohttp response into a requests.Response for zeep."""
133
+ r = Response()
134
+ r.status_code = response.status
135
+ r._content = body # noqa: SLF001 (zeep reads this attribute directly)
136
+ r.headers = dict(response.headers)
137
+ r.encoding = response.charset
138
+ r.url = str(response.url)
139
+
140
+ # Bridge aiohttp SimpleCookie → RequestsCookieJar so zeep / requests
141
+ # cookie handling works correctly.
142
+ jar = RequestsCookieJar()
143
+
144
+ for name, morsel in response.cookies.items():
145
+ jar.set(name, morsel.value)
146
+
147
+ r.cookies = jar
148
+
149
+ return r
150
+
151
+ async def _load_remote_data(self, url: str) -> bytes:
152
+ """Fetch WSDL / XSD documents (called by zeep during init)."""
153
+
154
+ async def _fetch() -> bytes:
155
+ async with self._wsdl_session.get(url, proxy=self._config.proxy) as resp:
156
+ content = await resp.read()
157
+
158
+ if resp.status >= 400:
159
+ raise TransportError(
160
+ status_code=resp.status,
161
+ message=content.decode(errors='ignore'),
162
+ )
163
+
164
+ return content
165
+
166
+ return await _fetch()
167
+
168
+ async def post(
169
+ self,
170
+ address: str,
171
+ message: bytes,
172
+ headers: dict[str, str],
173
+ *,
174
+ timeout: int | None = None,
175
+ ) -> Response:
176
+ logger.debug('SOAP POST → %s\n%s', address, message)
177
+
178
+ request_timeout = ClientTimeout(total=timeout) if timeout is not None else None
179
+
180
+ async def _post() -> Response:
181
+ async with self._session.post(
182
+ address,
183
+ data=message,
184
+ headers=headers,
185
+ proxy=self._config.proxy,
186
+ timeout=request_timeout,
187
+ ) as resp:
188
+ body = await resp.read()
189
+ logger.debug('SOAP ← %s (HTTP %d)\n%s', address, resp.status, body)
190
+
191
+ return self._build_response(resp, body)
192
+
193
+ return await _post()
194
+
195
+ async def post_xml(
196
+ self,
197
+ address: str,
198
+ envelope: Any,
199
+ headers: dict[str, str],
200
+ ) -> Response:
201
+ message = etree_to_string(envelope)
202
+
203
+ return await self.post(address, message, headers)
204
+
205
+ async def get(
206
+ self,
207
+ address: str,
208
+ params: dict[str, str],
209
+ headers: dict[str, str],
210
+ ) -> Response:
211
+ async with self._session.get(
212
+ address,
213
+ params=params,
214
+ headers=headers,
215
+ proxy=self._config.proxy,
216
+ ) as resp:
217
+ body = await resp.read()
218
+
219
+ return self._build_response(resp, body)
220
+
221
+
222
+ def build_soap_client(
223
+ wsdl_url: str,
224
+ transport: AsyncTransport,
225
+ plugins: Sequence[Plugin] | None = None,
226
+ ) -> AsyncClient:
227
+ """
228
+ Construct a zeep AsyncClient with the supplied transport and plugins.
229
+
230
+ Raises ValueError if *wsdl_url* is empty or None.
231
+ """
232
+ if not wsdl_url:
233
+ msg = 'wsdl_url must be a non-empty string.'
234
+
235
+ raise ValueError(msg)
236
+
237
+ return AsyncClient(wsdl_url, transport=transport, plugins=list(plugins or []))
238
+
239
+
240
+ @asynccontextmanager
241
+ async def soap_client(
242
+ wsdl_url: str,
243
+ *,
244
+ config: TransportConfig | None = None,
245
+ session: ClientSession | None = None,
246
+ plugins: Sequence[Plugin] | None = None,
247
+ ) -> AsyncIterator[AsyncClient]:
248
+ """
249
+ Async context manager that yields a ready-to-use zeep AsyncClient and
250
+ cleans up the transport on exit.
251
+
252
+ Example::
253
+
254
+ async with soap_client('https://example.com/service?wsdl') as client:
255
+ result = await client.service.GetData(id=42)
256
+ """
257
+ transport = AsyncTransport.from_config(config, session=session)
258
+
259
+ async with transport:
260
+ yield build_soap_client(wsdl_url, transport, plugins)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python3-commons
3
- Version: 0.17.8
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
@@ -16,15 +16,13 @@ Requires-Dist: msgpack~=1.1.2
16
16
  Requires-Dist: msgspec==0.21.1
17
17
  Requires-Dist: pydantic-settings~=2.14.0
18
18
  Provides-Extra: all
19
- Requires-Dist: python3_commons[api-client,audit,authn,authz,cache,database,object-storage]; extra == "all"
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
- Requires-Dist: zeep~=4.3.2; extra == "api-client"
24
22
  Requires-Dist: python3_commons[object-storage]; extra == "api-client"
25
23
  Provides-Extra: audit
26
24
  Requires-Dist: lxml~=6.1.0; extra == "audit"
27
- Requires-Dist: zeep~=4.3.2; extra == "audit"
25
+ Requires-Dist: zeep[async]~=4.3.2; extra == "audit"
28
26
  Requires-Dist: python3_commons[object-storage]; extra == "audit"
29
27
  Provides-Extra: authn
30
28
  Requires-Dist: aiohttp[speedups]<3.15.0,>=3.13.5; extra == "authn"
@@ -40,6 +38,11 @@ Requires-Dist: SQLAlchemy[asyncio]~=2.0.49; extra == "database"
40
38
  Provides-Extra: object-storage
41
39
  Requires-Dist: aiobotocore~=3.5.0; extra == "object-storage"
42
40
  Requires-Dist: object-storage-client==0.0.23; extra == "object-storage"
41
+ Provides-Extra: soap-client
42
+ Requires-Dist: aiohttp[speedups]<3.15.0,>=3.13.5; extra == "soap-client"
43
+ Requires-Dist: lxml~=6.1.0; extra == "soap-client"
44
+ Requires-Dist: requests~=2.33.1; extra == "soap-client"
45
+ Requires-Dist: zeep[async]~=4.3.2; extra == "soap-client"
43
46
  Dynamic: license-file
44
47
 
45
48
  Re-usable Python3 code
@@ -36,6 +36,7 @@ src/python3_commons/generators.py
36
36
  src/python3_commons/helpers.py
37
37
  src/python3_commons/object_storage.py
38
38
  src/python3_commons/permissions.py
39
+ src/python3_commons/soap_client.py
39
40
  src/python3_commons.egg-info/PKG-INFO
40
41
  src/python3_commons.egg-info/SOURCES.txt
41
42
  src/python3_commons.egg-info/dependency_links.txt
@@ -3,17 +3,15 @@ msgspec==0.21.1
3
3
  pydantic-settings~=2.14.0
4
4
 
5
5
  [all]
6
- python3_commons[api-client,audit,authn,authz,cache,database,object-storage]
6
+ python3_commons[api-client,audit,authn,authz,cache,database,object-storage,soap-client]
7
7
 
8
8
  [api-client]
9
9
  aiohttp[speedups]<3.15.0,>=3.13.5
10
- lxml~=6.1.0
11
- zeep~=4.3.2
12
10
  python3_commons[object-storage]
13
11
 
14
12
  [audit]
15
13
  lxml~=6.1.0
16
- zeep~=4.3.2
14
+ zeep[async]~=4.3.2
17
15
  python3_commons[object-storage]
18
16
 
19
17
  [authn]
@@ -34,3 +32,9 @@ SQLAlchemy[asyncio]~=2.0.49
34
32
  [object-storage]
35
33
  aiobotocore~=3.5.0
36
34
  object-storage-client==0.0.23
35
+
36
+ [soap-client]
37
+ aiohttp[speedups]<3.15.0,>=3.13.5
38
+ lxml~=6.1.0
39
+ requests~=2.33.1
40
+ zeep[async]~=4.3.2
@@ -129,6 +129,18 @@ wheels = [
129
129
  { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
130
130
  ]
131
131
 
132
+ [[package]]
133
+ name = "anyio"
134
+ version = "4.13.0"
135
+ source = { registry = "https://pypi.org/simple" }
136
+ dependencies = [
137
+ { name = "idna" },
138
+ ]
139
+ sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" }
140
+ wheels = [
141
+ { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" },
142
+ ]
143
+
132
144
  [[package]]
133
145
  name = "asyncpg"
134
146
  version = "0.31.0"
@@ -467,6 +479,43 @@ wheels = [
467
479
  { url = "https://files.pythonhosted.org/packages/15/32/77ee8a6c1564fc345a491a4e85b3bf360e4cf26eac98c4532d2fdb96e01f/greenlet-3.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d60097128cb0a1cab9ea541186ea13cd7b847b8449a7787c2e2350da0cb82d86", size = 245324, upload-time = "2026-04-27T12:24:40.295Z" },
468
480
  ]
469
481
 
482
+ [[package]]
483
+ name = "h11"
484
+ version = "0.16.0"
485
+ source = { registry = "https://pypi.org/simple" }
486
+ sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
487
+ wheels = [
488
+ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
489
+ ]
490
+
491
+ [[package]]
492
+ name = "httpcore"
493
+ version = "1.0.9"
494
+ source = { registry = "https://pypi.org/simple" }
495
+ dependencies = [
496
+ { name = "certifi" },
497
+ { name = "h11" },
498
+ ]
499
+ sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
500
+ wheels = [
501
+ { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
502
+ ]
503
+
504
+ [[package]]
505
+ name = "httpx"
506
+ version = "0.28.1"
507
+ source = { registry = "https://pypi.org/simple" }
508
+ dependencies = [
509
+ { name = "anyio" },
510
+ { name = "certifi" },
511
+ { name = "httpcore" },
512
+ { name = "idna" },
513
+ ]
514
+ sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
515
+ wheels = [
516
+ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
517
+ ]
518
+
470
519
  [[package]]
471
520
  name = "identify"
472
521
  version = "2.6.19"
@@ -1004,29 +1053,28 @@ all = [
1004
1053
  { name = "asyncpg" },
1005
1054
  { name = "lxml" },
1006
1055
  { name = "object-storage-client" },
1056
+ { name = "requests" },
1007
1057
  { name = "sqlalchemy", extra = ["asyncio"] },
1008
1058
  { name = "valkey", extra = ["libvalkey"] },
1009
- { name = "zeep" },
1059
+ { name = "zeep", extra = ["async"] },
1010
1060
  ]
1011
1061
  api-client = [
1012
1062
  { name = "aiobotocore" },
1013
1063
  { name = "aiohttp", extra = ["speedups"] },
1014
1064
  { name = "lxml" },
1015
1065
  { name = "object-storage-client" },
1016
- { name = "zeep" },
1017
1066
  ]
1018
1067
  audit = [
1019
1068
  { name = "aiobotocore" },
1020
1069
  { name = "lxml" },
1021
1070
  { name = "object-storage-client" },
1022
- { name = "zeep" },
1071
+ { name = "zeep", extra = ["async"] },
1023
1072
  ]
1024
1073
  authn = [
1025
1074
  { name = "aiobotocore" },
1026
1075
  { name = "aiohttp", extra = ["speedups"] },
1027
1076
  { name = "lxml" },
1028
1077
  { name = "object-storage-client" },
1029
- { name = "zeep" },
1030
1078
  ]
1031
1079
  authz = [
1032
1080
  { name = "aiobotocore" },
@@ -1035,7 +1083,6 @@ authz = [
1035
1083
  { name = "lxml" },
1036
1084
  { name = "object-storage-client" },
1037
1085
  { name = "sqlalchemy", extra = ["asyncio"] },
1038
- { name = "zeep" },
1039
1086
  ]
1040
1087
  cache = [
1041
1088
  { name = "valkey", extra = ["libvalkey"] },
@@ -1048,6 +1095,12 @@ object-storage = [
1048
1095
  { name = "aiobotocore" },
1049
1096
  { name = "object-storage-client" },
1050
1097
  ]
1098
+ soap-client = [
1099
+ { name = "aiohttp", extra = ["speedups"] },
1100
+ { name = "lxml" },
1101
+ { name = "requests" },
1102
+ { name = "zeep", extra = ["async"] },
1103
+ ]
1051
1104
 
1052
1105
  [package.dev-dependencies]
1053
1106
  dev = [
@@ -1071,25 +1124,28 @@ requires-dist = [
1071
1124
  { name = "aiobotocore", marker = "extra == 'object-storage'", specifier = "~=3.5.0" },
1072
1125
  { name = "aiohttp", extras = ["speedups"], marker = "extra == 'api-client'", specifier = ">=3.13.5,<3.15.0" },
1073
1126
  { name = "aiohttp", extras = ["speedups"], marker = "extra == 'authn'", specifier = ">=3.13.5,<3.15.0" },
1127
+ { name = "aiohttp", extras = ["speedups"], marker = "extra == 'soap-client'", specifier = ">=3.13.5,<3.15.0" },
1074
1128
  { name = "asyncpg", marker = "extra == 'database'", specifier = "~=0.31.0" },
1075
1129
  { name = "lxml", marker = "extra == 'api-client'", specifier = "~=6.1.0" },
1076
1130
  { name = "lxml", marker = "extra == 'audit'", specifier = "~=6.1.0" },
1131
+ { name = "lxml", marker = "extra == 'soap-client'", specifier = "~=6.1.0" },
1077
1132
  { name = "msgpack", specifier = "~=1.1.2" },
1078
1133
  { name = "msgspec", specifier = "==0.21.1" },
1079
1134
  { name = "object-storage-client", marker = "extra == 'object-storage'", specifier = "==0.0.23" },
1080
1135
  { name = "pydantic-settings", specifier = "~=2.14.0" },
1081
1136
  { name = "python3-commons", extras = ["api-client"], marker = "extra == 'authn'" },
1082
1137
  { name = "python3-commons", extras = ["api-client"], marker = "extra == 'authz'" },
1083
- { name = "python3-commons", extras = ["api-client", "audit", "authn", "authz", "cache", "database", "object-storage"], marker = "extra == 'all'" },
1138
+ { name = "python3-commons", extras = ["api-client", "audit", "authn", "authz", "cache", "database", "object-storage", "soap-client"], marker = "extra == 'all'" },
1084
1139
  { name = "python3-commons", extras = ["database"], marker = "extra == 'authz'" },
1085
1140
  { name = "python3-commons", extras = ["object-storage"], marker = "extra == 'api-client'" },
1086
1141
  { name = "python3-commons", extras = ["object-storage"], marker = "extra == 'audit'" },
1142
+ { name = "requests", marker = "extra == 'soap-client'", specifier = "~=2.33.1" },
1087
1143
  { name = "sqlalchemy", extras = ["asyncio"], marker = "extra == 'database'", specifier = "~=2.0.49" },
1088
1144
  { name = "valkey", extras = ["libvalkey"], marker = "extra == 'cache'", specifier = "~=6.1.1" },
1089
- { name = "zeep", marker = "extra == 'api-client'", specifier = "~=4.3.2" },
1090
- { name = "zeep", marker = "extra == 'audit'", specifier = "~=4.3.2" },
1145
+ { name = "zeep", extras = ["async"], marker = "extra == 'audit'", specifier = "~=4.3.2" },
1146
+ { name = "zeep", extras = ["async"], marker = "extra == 'soap-client'", specifier = "~=4.3.2" },
1091
1147
  ]
1092
- provides-extras = ["all", "api-client", "audit", "authn", "authz", "cache", "database", "object-storage"]
1148
+ provides-extras = ["all", "api-client", "audit", "authn", "authz", "cache", "database", "object-storage", "soap-client"]
1093
1149
 
1094
1150
  [package.metadata.requires-dev]
1095
1151
  dev = [
@@ -1462,3 +1518,9 @@ sdist = { url = "https://files.pythonhosted.org/packages/e8/06/4f1d3ff61e9301635
1462
1518
  wheels = [
1463
1519
  { url = "https://files.pythonhosted.org/packages/cd/78/f43f3feb70d67cbe260ec5b682ecc3c1850c8f437f1df707495126e51817/zeep-4.3.2-py3-none-any.whl", hash = "sha256:ed08c3179709172bfaaa9b76a6a545f8a57043ec6218e64e9deb81ff1e0ff79b", size = 101853, upload-time = "2025-09-15T10:26:02.12Z" },
1464
1520
  ]
1521
+
1522
+ [package.optional-dependencies]
1523
+ async = [
1524
+ { name = "httpx" },
1525
+ { name = "packaging" },
1526
+ ]