python3-commons 0.20.7__py3-none-any.whl → 0.20.9__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.
@@ -60,7 +60,7 @@ async def request(
60
60
  request_id = str(uuid4())[-12:]
61
61
  uri_path = uri.removesuffix('/')
62
62
  uri_path = uri_path.removeprefix('/')
63
- url = f'{u[:-1] if (u := str(base_url)).endswith("/") else u}{uri}'
63
+ url = f'{u[:-1] if (u := str(base_url)).endswith("/") else u}{uri}' if uri else base_url
64
64
 
65
65
  if audit_name:
66
66
  curl_request = None
python3_commons/auth.py CHANGED
@@ -1,7 +1,6 @@
1
1
  import logging
2
2
  import threading
3
3
  from collections.abc import Mapping, Sequence
4
- from http import HTTPStatus
5
4
  from typing import Any, Self, TypeVar
6
5
 
7
6
  from pydantic import HttpUrl
@@ -10,8 +9,10 @@ from python3_commons.helpers import replace_origin
10
9
 
11
10
  try:
12
11
  import aiohttp
12
+
13
+ from python3_commons import api_client
13
14
  except ImportError as e:
14
- msg = 'Install python3-commons[authn] to use this feature'
15
+ msg = 'Install python3-commons[api-client] to use this feature'
15
16
  raise RuntimeError(msg) from e
16
17
 
17
18
  import msgspec
@@ -70,7 +71,6 @@ class OIDCAuthError(OIDCError):
70
71
  pass
71
72
 
72
73
 
73
- # TODO: use api_client
74
74
  class OIDCClient:
75
75
  def __init__(
76
76
  self,
@@ -82,6 +82,7 @@ class OIDCClient:
82
82
  verify_cert: bool = True,
83
83
  connection_limit: int = 100,
84
84
  authority_internal_host: HttpUrl | None = None,
85
+ audit_name: str | None = None,
85
86
  ) -> None:
86
87
  if authority_internal_host:
87
88
  authority_url = replace_origin(authority_url, authority_internal_host)
@@ -98,8 +99,9 @@ class OIDCClient:
98
99
 
99
100
  self._config: Mapping[str, Any] | None = None
100
101
  self._jwks: Mapping[str, Any] | None = None
102
+ self._audit_name: str | None = audit_name
101
103
 
102
- def get_session(self) -> aiohttp.ClientSession:
104
+ def _get_session(self) -> aiohttp.ClientSession:
103
105
  if self._session:
104
106
  return self._session
105
107
 
@@ -115,7 +117,7 @@ class OIDCClient:
115
117
  return session
116
118
 
117
119
  async def __aenter__(self) -> Self:
118
- self.get_session()
120
+ self._get_session()
119
121
 
120
122
  return self
121
123
 
@@ -127,20 +129,12 @@ class OIDCClient:
127
129
  """
128
130
  Fetch the OpenID configuration (including JWKS URI) from OIDC authority.
129
131
  """
130
- if self._session is None:
131
- msg = 'ClientSession not initialized'
132
- raise RuntimeError(msg)
133
-
134
- oidc_config_url = f'{self._authority_url}/.well-known/openid-configuration'
135
-
136
- logger.debug('Fetching OpenID configuration from: %s', oidc_config_url)
137
-
138
- async with self._session.get(oidc_config_url) as response:
139
- if response.status != HTTPStatus.OK:
140
- _msg = 'Failed to fetch OpenID configuration'
141
-
142
- raise RuntimeError(_msg)
143
-
132
+ async with api_client.request(
133
+ self._get_session(),
134
+ str(self._authority_url),
135
+ '/.well-known/openid-configuration',
136
+ audit_name=self._audit_name,
137
+ ) as response:
144
138
  return await response.json()
145
139
 
146
140
  async def get_config(self) -> Mapping[str, Any]:
@@ -160,22 +154,13 @@ class OIDCClient:
160
154
  """
161
155
  Fetch the JSON Web Key Set (JWKS) for validating the token's signature.
162
156
  """
163
- if self._session is None:
164
- msg = 'ClientSession not initialized'
165
- raise RuntimeError(msg)
166
-
167
157
  if authority_internal_host := self._authority_internal_host:
168
158
  logger.debug('Received jwks_uri: %s', jwks_uri)
169
159
  logger.debug('Replacing OIDC authority host with: %s', authority_internal_host)
170
160
  jwks_uri = str(replace_origin(HttpUrl(jwks_uri), authority_internal_host))
171
161
  logger.debug('Modified jwks_uri: %s', jwks_uri)
172
162
 
173
- async with self._session.get(jwks_uri) as response:
174
- if response.status != HTTPStatus.OK:
175
- _msg = 'Failed to fetch JWKS'
176
-
177
- raise RuntimeError(_msg)
178
-
163
+ async with api_client.request(self._get_session(), jwks_uri, '', audit_name=self._audit_name) as response:
179
164
  return await response.json()
180
165
 
181
166
  async def get_jwks(self) -> Mapping[str, Any]:
@@ -200,10 +185,6 @@ class OIDCClient:
200
185
  password: str,
201
186
  scope: str = 'openid profile email',
202
187
  ) -> OIDCTokenResponse:
203
- if self._session is None:
204
- msg = 'ClientSession not initialized'
205
- raise RuntimeError(msg)
206
-
207
188
  data = {
208
189
  'grant_type': 'password',
209
190
  'username': username,
@@ -218,9 +199,12 @@ class OIDCClient:
218
199
  openid_config = await self.get_config()
219
200
 
220
201
  try:
221
- async with self._session.post(
202
+ async with api_client.request(
203
+ self._get_session(),
222
204
  openid_config['token_endpoint'],
223
- data=data,
205
+ '',
206
+ method='post',
207
+ json=data,
224
208
  headers={'Content-Type': 'application/x-www-form-urlencoded'},
225
209
  ) as response:
226
210
  payload = await response.read()
@@ -1,4 +1,3 @@
1
- import asyncio
2
1
  import contextlib
3
2
  import logging
4
3
  import time
@@ -159,19 +158,18 @@ class AsyncSessionManager:
159
158
  logger.debug('Acquiring session for %r', name)
160
159
 
161
160
  try:
162
- async with asyncio.timeout(self.pool_acquire_timeout):
163
- async with session_maker() as session:
164
- session_acquired = True
165
- elapsed = time.monotonic() - t0
166
- logger.debug('Session acquired for %r in %.3fs', name, elapsed)
167
-
168
- try:
169
- yield session
170
- except Exception:
171
- logger.exception('Database communication error for %r; rolling back', name)
172
- await session.rollback()
173
-
174
- raise
161
+ async with session_maker() as session:
162
+ session_acquired = True
163
+ elapsed = time.monotonic() - t0
164
+ logger.debug('Session acquired for %r in %.3fs', name, elapsed)
165
+
166
+ try:
167
+ yield session
168
+ except Exception:
169
+ logger.exception('Database communication error for %r; rolling back', name)
170
+ await session.rollback()
171
+
172
+ raise
175
173
  except TimeoutError as e:
176
174
  if session_acquired:
177
175
  raise
@@ -230,10 +228,9 @@ async def is_healthy(
230
228
  t0 = time.monotonic()
231
229
 
232
230
  try:
233
- async with asyncio.timeout(timeout):
234
- async with engine.begin() as conn:
235
- result = await conn.execute(text('SELECT 1'))
236
- healthy = result.scalar() == 1
231
+ async with engine.begin() as conn:
232
+ result = await conn.execute(text('SELECT 1'))
233
+ healthy = result.scalar() == 1
237
234
 
238
235
  elapsed = time.monotonic() - t0
239
236
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python3-commons
3
- Version: 0.20.7
3
+ Version: 0.20.9
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
@@ -36,7 +36,7 @@ Provides-Extra: cache
36
36
  Requires-Dist: valkey[libvalkey]~=6.1.1; extra == "cache"
37
37
  Provides-Extra: database
38
38
  Requires-Dist: asyncpg~=0.31.0; extra == "database"
39
- Requires-Dist: SQLAlchemy[asyncio]~=2.0.49; extra == "database"
39
+ Requires-Dist: SQLAlchemy[asyncio]~=2.0.50; extra == "database"
40
40
  Provides-Extra: object-storage
41
41
  Requires-Dist: aiobotocore~=3.7.0; extra == "object-storage"
42
42
  Requires-Dist: object-storage-client==0.0.30; extra == "object-storage"
@@ -1,8 +1,8 @@
1
1
  python3_commons/__init__.py,sha256=0KgaYU46H_IMKn-BuasoRN3C4Hi45KlkHHoPbU9cwiA,189
2
- python3_commons/api_client.py,sha256=yerFJNY_SHhYo9FGLv29oHVIGgeXDNzTzMNfFYZpZ0w,5501
2
+ python3_commons/api_client.py,sha256=5i2fndIlKFph1iKa1pbnBP0fW271XHY-vColI3leKVs,5522
3
3
  python3_commons/async_functools.py,sha256=A2HvwFzZHxOWTp4IQM5UiBY2yg1S_0U1CWra5BWK0gk,9101
4
4
  python3_commons/audit.py,sha256=uGoCwenDJ0Gdwbr_VNOZm5scT8luxW1weprJbbMoHo0,2608
5
- python3_commons/auth.py,sha256=FruxtIwHCHwxaVeu0bxbqrWXFSdS3suEhOMJlqyI79Q,7761
5
+ python3_commons/auth.py,sha256=gxbp_y3ksAKtBzW17XwVH_oS5sYWEEH8A6RyZGctglU,7328
6
6
  python3_commons/cache.py,sha256=lowiXJqFgFy1Yg86wi9IhuoNqIUGP6nc5eNibmf0dfY,8018
7
7
  python3_commons/conf.py,sha256=1HYtNkNBQc5IIABzhH3kz7GnLeF7rj5aNcOoW8xbEa4,3101
8
8
  python3_commons/exceptions.py,sha256=EGjHZVBnsM6CeBfPMqhL0IPMKjDJ_2-Z-aSPXwq91LE,36
@@ -12,7 +12,7 @@ python3_commons/helpers.py,sha256=d1xdxiOyskwq_rzSprB7e7Fo8Hp0uDlWSmRWFL4vC6A,49
12
12
  python3_commons/object_storage.py,sha256=2l8v1mDB5WWN6jhxH2t5xmHBXaVPRJl0z44wExmlO80,7175
13
13
  python3_commons/permissions.py,sha256=gaMKSWg0MgPQTdP1voll4ItXcblXku9BlD0Lq3Xv64U,1724
14
14
  python3_commons/soap_client.py,sha256=w2lOyhhtkhwiNnHjvir8DfjGBO51XVg5rlDHyuudU2A,7169
15
- python3_commons/db/__init__.py,sha256=V4f9SnQvTYYM_RRqg2-dz6KL6vOmsIrTjS_XbRq2TZ8,9166
15
+ python3_commons/db/__init__.py,sha256=Ze5OtqF-yyD9dY0GNljMX9uZAE9E0aw0XAniwYUkjSM,8983
16
16
  python3_commons/db/helpers.py,sha256=xRpWs4aVkBge6HCLjb6OLSnWc1nlV6rKGyaVhZ_x12w,2001
17
17
  python3_commons/db/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
18
  python3_commons/db/models/auth.py,sha256=t6z_0c7l_1eB-CCdnPSV9OzJ_ymgkwD1cLpecNhQgoA,773
@@ -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.20.7.dist-info/licenses/AUTHORS.rst,sha256=3R9JnfjfjH5RoPWOeqKFJgxVShSSfzQPIrEr1nxIo9Q,90
31
- python3_commons-0.20.7.dist-info/licenses/LICENSE,sha256=xxILuojHm4fKQOrMHPSslbyy6WuKAN2RiG74HbrYfzM,34575
32
- python3_commons-0.20.7.dist-info/METADATA,sha256=dNE1cEVe-kzCqwJzrv9PY-Nfm7YfrD3NcijdddSkrtU,2333
33
- python3_commons-0.20.7.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
34
- python3_commons-0.20.7.dist-info/top_level.txt,sha256=lJI6sCBf68eUHzupCnn2dzG10lH3jJKTWM_hrN1cQ7M,16
35
- python3_commons-0.20.7.dist-info/RECORD,,
30
+ python3_commons-0.20.9.dist-info/licenses/AUTHORS.rst,sha256=3R9JnfjfjH5RoPWOeqKFJgxVShSSfzQPIrEr1nxIo9Q,90
31
+ python3_commons-0.20.9.dist-info/licenses/LICENSE,sha256=xxILuojHm4fKQOrMHPSslbyy6WuKAN2RiG74HbrYfzM,34575
32
+ python3_commons-0.20.9.dist-info/METADATA,sha256=PJvQKs-Xbn_ix2zSbCJ0w3bXtA13t8BWSKaKGt3c20I,2333
33
+ python3_commons-0.20.9.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
34
+ python3_commons-0.20.9.dist-info/top_level.txt,sha256=lJI6sCBf68eUHzupCnn2dzG10lH3jJKTWM_hrN1cQ7M,16
35
+ python3_commons-0.20.9.dist-info/RECORD,,