python3-commons 0.18.18__tar.gz → 0.18.20__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.18.18/src/python3_commons.egg-info → python3_commons-0.18.20}/PKG-INFO +1 -1
  2. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/auth.py +19 -6
  3. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/conf.py +2 -2
  4. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/helpers.py +17 -0
  5. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/object_storage.py +4 -4
  6. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/soap_client.py +16 -14
  7. {python3_commons-0.18.18 → python3_commons-0.18.20/src/python3_commons.egg-info}/PKG-INFO +1 -1
  8. {python3_commons-0.18.18 → python3_commons-0.18.20}/.coveragerc +0 -0
  9. {python3_commons-0.18.18 → python3_commons-0.18.20}/.devcontainer/Dockerfile +0 -0
  10. {python3_commons-0.18.18 → python3_commons-0.18.20}/.devcontainer/devcontainer.json +0 -0
  11. {python3_commons-0.18.18 → python3_commons-0.18.20}/.devcontainer/docker-compose.yml +0 -0
  12. {python3_commons-0.18.18 → python3_commons-0.18.20}/.env_template +0 -0
  13. {python3_commons-0.18.18 → python3_commons-0.18.20}/.github/workflows/checks.yml +0 -0
  14. {python3_commons-0.18.18 → python3_commons-0.18.20}/.github/workflows/python-publish.yaml +0 -0
  15. {python3_commons-0.18.18 → python3_commons-0.18.20}/.github/workflows/release-on-tag-push.yml +0 -0
  16. {python3_commons-0.18.18 → python3_commons-0.18.20}/.gitignore +0 -0
  17. {python3_commons-0.18.18 → python3_commons-0.18.20}/.pre-commit-config.yaml +0 -0
  18. {python3_commons-0.18.18 → python3_commons-0.18.20}/.python-version +0 -0
  19. {python3_commons-0.18.18 → python3_commons-0.18.20}/AUTHORS.rst +0 -0
  20. {python3_commons-0.18.18 → python3_commons-0.18.20}/CHANGELOG.rst +0 -0
  21. {python3_commons-0.18.18 → python3_commons-0.18.20}/LICENSE +0 -0
  22. {python3_commons-0.18.18 → python3_commons-0.18.20}/README.md +0 -0
  23. {python3_commons-0.18.18 → python3_commons-0.18.20}/README.rst +0 -0
  24. {python3_commons-0.18.18 → python3_commons-0.18.20}/docs/Makefile +0 -0
  25. {python3_commons-0.18.18 → python3_commons-0.18.20}/docs/_static/.gitignore +0 -0
  26. {python3_commons-0.18.18 → python3_commons-0.18.20}/docs/authors.rst +0 -0
  27. {python3_commons-0.18.18 → python3_commons-0.18.20}/docs/changelog.rst +0 -0
  28. {python3_commons-0.18.18 → python3_commons-0.18.20}/docs/conf.py +0 -0
  29. {python3_commons-0.18.18 → python3_commons-0.18.20}/docs/index.rst +0 -0
  30. {python3_commons-0.18.18 → python3_commons-0.18.20}/docs/license.rst +0 -0
  31. {python3_commons-0.18.18 → python3_commons-0.18.20}/pyproject.toml +0 -0
  32. {python3_commons-0.18.18 → python3_commons-0.18.20}/setup.cfg +0 -0
  33. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/__init__.py +0 -0
  34. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/api_client.py +0 -0
  35. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/async_functools.py +0 -0
  36. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/audit.py +0 -0
  37. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/cache.py +0 -0
  38. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/db/__init__.py +0 -0
  39. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/db/helpers.py +0 -0
  40. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/db/models/__init__.py +0 -0
  41. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/db/models/auth.py +0 -0
  42. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/db/models/common.py +0 -0
  43. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/db/models/rbac.py +0 -0
  44. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/db/models/users.py +0 -0
  45. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/exceptions.py +0 -0
  46. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/fs.py +0 -0
  47. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/generators.py +0 -0
  48. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/log/__init__.py +0 -0
  49. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/log/filters.py +0 -0
  50. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/log/formatters.py +0 -0
  51. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/permissions.py +0 -0
  52. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/serializers/__init__.py +0 -0
  53. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/serializers/common.py +0 -0
  54. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/serializers/json.py +0 -0
  55. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/serializers/msgpack.py +0 -0
  56. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons/serializers/msgspec.py +0 -0
  57. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons.egg-info/SOURCES.txt +0 -0
  58. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons.egg-info/dependency_links.txt +0 -0
  59. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons.egg-info/requires.txt +0 -0
  60. {python3_commons-0.18.18 → python3_commons-0.18.20}/src/python3_commons.egg-info/top_level.txt +0 -0
  61. {python3_commons-0.18.18 → python3_commons-0.18.20}/tests/__init__.py +0 -0
  62. {python3_commons-0.18.18 → python3_commons-0.18.20}/tests/integration/__init__.py +0 -0
  63. {python3_commons-0.18.18 → python3_commons-0.18.20}/tests/integration/test_cache.py +0 -0
  64. {python3_commons-0.18.18 → python3_commons-0.18.20}/tests/integration/test_osc.py +0 -0
  65. {python3_commons-0.18.18 → python3_commons-0.18.20}/tests/unit/__init__.py +0 -0
  66. {python3_commons-0.18.18 → python3_commons-0.18.20}/tests/unit/conftest.py +0 -0
  67. {python3_commons-0.18.18 → python3_commons-0.18.20}/tests/unit/log/__init__.py +0 -0
  68. {python3_commons-0.18.18 → python3_commons-0.18.20}/tests/unit/log/test_formatters.py +0 -0
  69. {python3_commons-0.18.18 → python3_commons-0.18.20}/tests/unit/test_async_functools.py +0 -0
  70. {python3_commons-0.18.18 → python3_commons-0.18.20}/tests/unit/test_audit.py +0 -0
  71. {python3_commons-0.18.18 → python3_commons-0.18.20}/tests/unit/test_helpers.py +0 -0
  72. {python3_commons-0.18.18 → python3_commons-0.18.20}/tests/unit/test_msgpack.py +0 -0
  73. {python3_commons-0.18.18 → python3_commons-0.18.20}/tests/unit/test_msgspec.py +0 -0
  74. {python3_commons-0.18.18 → python3_commons-0.18.20}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python3-commons
3
- Version: 0.18.18
3
+ Version: 0.18.20
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
@@ -5,6 +5,10 @@ from collections.abc import Sequence
5
5
  from http import HTTPStatus
6
6
  from typing import Self, TypeVar
7
7
 
8
+ from pydantic import HttpUrl
9
+
10
+ from python3_commons.helpers import replace_origin
11
+
8
12
  try:
9
13
  import aiohttp
10
14
  except ImportError as e:
@@ -45,9 +49,6 @@ class TokenData(msgspec.Struct):
45
49
 
46
50
 
47
51
  T = TypeVar('T', bound=TokenData)
48
- OIDC_CONFIG_URL = (
49
- f'{oidc_settings.authority_internal_url or oidc_settings.authority_url}/.well-known/openid-configuration'
50
- )
51
52
 
52
53
 
53
54
  class OIDCTokenResponse(msgspec.Struct):
@@ -65,7 +66,16 @@ async def fetch_openid_config() -> dict:
65
66
  """
66
67
  Fetch the OpenID configuration (including JWKS URI) from OIDC authority.
67
68
  """
68
- async with aiohttp.ClientSession() as session, session.get(OIDC_CONFIG_URL) as response:
69
+ if (authority_url := oidc_settings.authority_url) is None:
70
+ msg = 'OIDC authority URL is required'
71
+ raise ValueError(msg)
72
+
73
+ if oidc_settings.authority_internal_host:
74
+ authority_url = replace_origin(authority_url, oidc_settings.authority_internal_host)
75
+
76
+ oidc_config_url = f'{authority_url}/.well-known/openid-configuration'
77
+
78
+ async with aiohttp.ClientSession() as session, session.get(oidc_config_url) as response:
69
79
  if response.status != HTTPStatus.OK:
70
80
  _msg = 'Failed to fetch OpenID configuration'
71
81
 
@@ -78,8 +88,11 @@ async def fetch_jwks(jwks_uri: str) -> dict:
78
88
  """
79
89
  Fetch the JSON Web Key Set (JWKS) for validating the token's signature.
80
90
  """
81
- if oidc_settings.authority_internal_url:
82
- jwks_uri = jwks_uri.replace(str(oidc_settings.authority_url), str(oidc_settings.authority_internal_url))
91
+ if authority_internal_host := oidc_settings.authority_internal_host:
92
+ logger.debug('Received jwks_uri: %s', jwks_uri)
93
+ logger.debug('Replacing OIDC authority host with: %s', authority_internal_host)
94
+ jwks_uri = str(replace_origin(HttpUrl(jwks_uri), authority_internal_host))
95
+ logger.debug('Modified jwks_uri: %s', jwks_uri)
83
96
 
84
97
  async with aiohttp.ClientSession() as session, session.get(jwks_uri) as response:
85
98
  if response.status != HTTPStatus.OK:
@@ -18,8 +18,8 @@ class CommonSettings(BaseSettings):
18
18
  class OIDCSettings(BaseSettings):
19
19
  model_config = SettingsConfigDict(env_prefix='OIDC_')
20
20
 
21
- authority_url: HttpUrl | None = None
22
- authority_internal_url: HttpUrl | None = None
21
+ authority_url: HttpUrl | None = None # https://login.externaldomain.com/realm/app
22
+ authority_internal_host: HttpUrl | None = None # http://localhost:8080/ which will substitute host in authority_url
23
23
  client_id: str | None = None
24
24
  client_secret: SecretStr = SecretStr('')
25
25
  redirect_uri: str | None = None
@@ -15,6 +15,8 @@ from json import dumps
15
15
  from typing import ClassVar, Literal
16
16
  from urllib.parse import urlencode
17
17
 
18
+ from pydantic import HttpUrl
19
+
18
20
  from python3_commons.serializers.json import CustomJSONEncoder
19
21
 
20
22
  logger = logging.getLogger(__name__)
@@ -159,3 +161,18 @@ def parse_string_list(v: str | Sequence[str]) -> Sequence[str]:
159
161
  return tuple(map(str.strip, v.split(',')))
160
162
 
161
163
  return v
164
+
165
+
166
+ def replace_origin(url: HttpUrl, host_url: HttpUrl) -> HttpUrl:
167
+ if host_url.host is None:
168
+ msg = 'Host URL must have a host'
169
+ raise ValueError(msg)
170
+
171
+ return HttpUrl.build(
172
+ scheme=host_url.scheme,
173
+ host=host_url.host,
174
+ port=host_url.port,
175
+ path=url.path,
176
+ query=url.query,
177
+ fragment=url.fragment,
178
+ )
@@ -140,7 +140,7 @@ async def list_objects(bucket_name: str, prefix: str, *, recursive: bool = True)
140
140
 
141
141
 
142
142
  async def get_object_streams(
143
- bucket_name: str, path: str, *, recursive: bool = True
143
+ bucket_name: str, path: str, *, recursive: bool = True
144
144
  ) -> AsyncGenerator[tuple[str, datetime, StreamingBody]]:
145
145
  async for obj in list_objects(bucket_name, path, recursive=recursive):
146
146
  object_name = obj['Key']
@@ -151,7 +151,7 @@ async def get_object_streams(
151
151
 
152
152
 
153
153
  async def get_objects(
154
- bucket_name: str, path: str, *, recursive: bool = True
154
+ bucket_name: str, path: str, *, recursive: bool = True
155
155
  ) -> AsyncGenerator[tuple[str, datetime, bytes]]:
156
156
  async for object_name, last_modified, stream in get_object_streams(bucket_name, path, recursive=recursive):
157
157
  data = await stream.read()
@@ -174,7 +174,7 @@ async def remove_object(bucket_name: str, path: str) -> None:
174
174
 
175
175
 
176
176
  async def remove_objects(
177
- bucket_name: str, prefix: str | None = None, object_names: Iterable[str] | None = None
177
+ bucket_name: str, prefix: str | None = None, object_names: Iterable[str] | None = None
178
178
  ) -> Sequence[Mapping] | None:
179
179
  storage = ObjectStorage(s3_settings)
180
180
 
@@ -197,7 +197,7 @@ async def remove_objects(
197
197
  chunk_size = 1000
198
198
 
199
199
  for i in range(0, len(objects_to_delete), chunk_size):
200
- chunk = objects_to_delete[i: i + chunk_size]
200
+ chunk = objects_to_delete[i : i + chunk_size]
201
201
 
202
202
  response = await s3_client.delete_objects(Bucket=bucket_name, Delete={'Objects': chunk})
203
203
 
@@ -145,28 +145,30 @@ class AsyncTransport(Transport):
145
145
  return b''
146
146
 
147
147
  async def _fetch() -> bytes:
148
- async with ClientSession(
148
+ async with (
149
+ ClientSession(
149
150
  connector=TCPConnector(ssl=_make_ssl_context(verify=self._config.verify_ssl)),
150
151
  timeout=ClientTimeout(total=self._config.timeout),
151
152
  headers={'User-Agent': f'Zeep/{get_version()} (www.python-zeep.org)'},
152
- ) as session:
153
- async with session.get(url, proxy=self._config.proxy) as resp:
154
- body = await resp.read()
155
- if resp.status >= 400:
156
- raise TransportError(
157
- status_code=resp.status,
158
- message=body.decode(errors='ignore'),
159
- )
160
- return body
153
+ ) as session,
154
+ session.get(url, proxy=self._config.proxy) as resp,
155
+ ):
156
+ body = await resp.read()
157
+ if resp.status >= 400:
158
+ raise TransportError(
159
+ status_code=resp.status,
160
+ message=body.decode(errors='ignore'),
161
+ )
162
+ return body
161
163
 
162
164
  with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
163
165
  return pool.submit(asyncio.run, _fetch()).result()
164
166
 
165
167
  async def post_xml(
166
- self,
167
- address: str,
168
- envelope: Any,
169
- headers: dict[str, str],
168
+ self,
169
+ address: str,
170
+ envelope: Any,
171
+ headers: dict[str, str],
170
172
  ) -> Response:
171
173
  return await self.post(address, etree_to_string(envelope), headers)
172
174
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python3-commons
3
- Version: 0.18.18
3
+ Version: 0.18.20
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