python3-commons 0.22.5__py3-none-any.whl → 0.22.7__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.
- python3_commons/auth.py +44 -24
- python3_commons-0.22.7.dist-info/METADATA +310 -0
- {python3_commons-0.22.5.dist-info → python3_commons-0.22.7.dist-info}/RECORD +9 -7
- {python3_commons-0.22.5.dist-info → python3_commons-0.22.7.dist-info}/WHEEL +1 -1
- python3_commons-0.22.7.dist-info/scm_file_list.json +76 -0
- python3_commons-0.22.7.dist-info/scm_version.json +8 -0
- python3_commons-0.22.5.dist-info/METADATA +0 -54
- {python3_commons-0.22.5.dist-info → python3_commons-0.22.7.dist-info}/licenses/AUTHORS.rst +0 -0
- {python3_commons-0.22.5.dist-info → python3_commons-0.22.7.dist-info}/licenses/LICENSE +0 -0
- {python3_commons-0.22.5.dist-info → python3_commons-0.22.7.dist-info}/top_level.txt +0 -0
python3_commons/auth.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import threading
|
|
3
|
-
from collections.abc import
|
|
3
|
+
from collections.abc import Sequence
|
|
4
|
+
from time import monotonic
|
|
4
5
|
from typing import Any, Self, TypeVar
|
|
5
6
|
|
|
6
7
|
from pydantic import HttpUrl
|
|
@@ -18,9 +19,8 @@ except ImportError as e:
|
|
|
18
19
|
import msgspec
|
|
19
20
|
|
|
20
21
|
logger = logging.getLogger(__name__)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
_OIDC_SESSION_LOCK = threading.Lock()
|
|
22
|
+
|
|
23
|
+
DEFAULT_JWKS_CACHE_TTL = 300.0
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class TokenData(msgspec.Struct):
|
|
@@ -81,6 +81,7 @@ class OIDCClient:
|
|
|
81
81
|
timeout: float = 10.0,
|
|
82
82
|
verify_cert: bool = True,
|
|
83
83
|
connection_limit: int = 100,
|
|
84
|
+
jwks_cache_ttl: float = DEFAULT_JWKS_CACHE_TTL,
|
|
84
85
|
authority_internal_host: HttpUrl | None = None,
|
|
85
86
|
audit_name: str | None = None,
|
|
86
87
|
) -> None:
|
|
@@ -97,17 +98,22 @@ class OIDCClient:
|
|
|
97
98
|
self._timeout = timeout
|
|
98
99
|
self._verify_cert = verify_cert
|
|
99
100
|
|
|
100
|
-
self.
|
|
101
|
-
self.
|
|
101
|
+
self._session_lock = threading.Lock()
|
|
102
|
+
self._config: dict[str, Any] | None = None
|
|
103
|
+
self._config_lock = threading.Lock()
|
|
104
|
+
self._jwks: dict[str, Any] | None = None
|
|
105
|
+
self._jwks_lock = threading.Lock()
|
|
106
|
+
self._jwks_cache_ttl = jwks_cache_ttl
|
|
107
|
+
self._jwks_fetched_at: float | None = None
|
|
102
108
|
self._audit_name: str | None = audit_name
|
|
103
109
|
|
|
104
110
|
def _get_session(self) -> aiohttp.ClientSession:
|
|
105
|
-
if self._session:
|
|
106
|
-
return
|
|
111
|
+
if (session := self._session) and not session.closed:
|
|
112
|
+
return session
|
|
107
113
|
|
|
108
|
-
with
|
|
109
|
-
if self._session:
|
|
110
|
-
return
|
|
114
|
+
with self._session_lock:
|
|
115
|
+
if (session := self._session) and not session.closed:
|
|
116
|
+
return session
|
|
111
117
|
|
|
112
118
|
connector = aiohttp.TCPConnector(verify_ssl=self._verify_cert, limit=self._connection_limit)
|
|
113
119
|
timeout = aiohttp.ClientTimeout(total=self._timeout)
|
|
@@ -137,20 +143,20 @@ class OIDCClient:
|
|
|
137
143
|
) as response:
|
|
138
144
|
return await response.json()
|
|
139
145
|
|
|
140
|
-
async def get_config(self) ->
|
|
141
|
-
if self._config:
|
|
142
|
-
return
|
|
146
|
+
async def get_config(self) -> dict[str, Any]:
|
|
147
|
+
if config := self._config:
|
|
148
|
+
return config
|
|
143
149
|
|
|
144
|
-
with
|
|
145
|
-
if self._config:
|
|
146
|
-
return
|
|
150
|
+
with self._config_lock:
|
|
151
|
+
if config := self._config:
|
|
152
|
+
return config
|
|
147
153
|
|
|
148
154
|
config = await self._fetch_config()
|
|
149
155
|
self._config = config
|
|
150
156
|
|
|
151
157
|
return config
|
|
152
158
|
|
|
153
|
-
async def _fetch_jwks(self, jwks_uri: str) -> dict:
|
|
159
|
+
async def _fetch_jwks(self, jwks_uri: str) -> dict[str, Any]:
|
|
154
160
|
"""
|
|
155
161
|
Fetch the JSON Web Key Set (JWKS) for validating the token's signature.
|
|
156
162
|
"""
|
|
@@ -163,18 +169,32 @@ class OIDCClient:
|
|
|
163
169
|
async with api_client.request(self._get_session(), jwks_uri, '', audit_name=self._audit_name) as response:
|
|
164
170
|
return await response.json()
|
|
165
171
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
172
|
+
def _is_fresh_jwks(self) -> bool:
|
|
173
|
+
fetched_at = self._jwks_fetched_at
|
|
174
|
+
|
|
175
|
+
if self._jwks is None or fetched_at is None:
|
|
176
|
+
return False
|
|
177
|
+
|
|
178
|
+
return (monotonic() - fetched_at) < self._jwks_cache_ttl
|
|
179
|
+
|
|
180
|
+
async def get_jwks(self, *, force_refresh: bool = False) -> dict[str, Any]:
|
|
181
|
+
if not force_refresh and (jwks := self._jwks) and self._is_fresh_jwks():
|
|
182
|
+
return jwks
|
|
183
|
+
|
|
184
|
+
fetched_at = self._jwks_fetched_at
|
|
185
|
+
|
|
186
|
+
with self._jwks_lock:
|
|
187
|
+
if (jwks := self._jwks) and self._jwks_fetched_at != fetched_at:
|
|
188
|
+
return jwks
|
|
169
189
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return self._jwks
|
|
190
|
+
if not force_refresh and jwks and self._is_fresh_jwks():
|
|
191
|
+
return jwks
|
|
173
192
|
|
|
174
193
|
oidc_config = await self.get_config()
|
|
175
194
|
|
|
176
195
|
jwks = await self._fetch_jwks(oidc_config['jwks_uri'])
|
|
177
196
|
self._jwks = jwks
|
|
197
|
+
self._jwks_fetched_at = monotonic()
|
|
178
198
|
|
|
179
199
|
return jwks
|
|
180
200
|
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python3-commons
|
|
3
|
+
Version: 0.22.7
|
|
4
|
+
Summary: Re-usable Python3 code
|
|
5
|
+
Author-email: Oleg Korsak <kamikaze.is.waiting.you@gmail.com>
|
|
6
|
+
License-Expression: GPL-3.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/kamikaze/python3-commons
|
|
8
|
+
Project-URL: Documentation, https://github.com/kamikaze/python3-commons/wiki
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Programming Language :: Python
|
|
11
|
+
Requires-Python: <3.15.0,>=3.14.0
|
|
12
|
+
Description-Content-Type: text/x-rst
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
License-File: AUTHORS.rst
|
|
15
|
+
Requires-Dist: msgpack~=1.2.1
|
|
16
|
+
Requires-Dist: msgspec~=0.21.1
|
|
17
|
+
Requires-Dist: pydantic-settings~=2.14.2
|
|
18
|
+
Provides-Extra: all
|
|
19
|
+
Requires-Dist: python3_commons[api-client,audit,authn,authz,cache,database,object-storage,soap-client]; extra == "all"
|
|
20
|
+
Provides-Extra: api-client
|
|
21
|
+
Requires-Dist: aiohttp[speedups]<3.15.0,>=3.14.1; extra == "api-client"
|
|
22
|
+
Requires-Dist: certifi~=2026.6.17; extra == "api-client"
|
|
23
|
+
Requires-Dist: python3_commons[audit,object-storage]; extra == "api-client"
|
|
24
|
+
Provides-Extra: audit
|
|
25
|
+
Requires-Dist: lxml~=6.1.1; extra == "audit"
|
|
26
|
+
Requires-Dist: zeep[async]~=4.3.3; extra == "audit"
|
|
27
|
+
Requires-Dist: python3_commons[object-storage]; extra == "audit"
|
|
28
|
+
Provides-Extra: authn
|
|
29
|
+
Requires-Dist: aiohttp[speedups]<3.15.0,>=3.14.1; extra == "authn"
|
|
30
|
+
Requires-Dist: certifi~=2026.6.17; extra == "authn"
|
|
31
|
+
Requires-Dist: python3_commons[api-client]; extra == "authn"
|
|
32
|
+
Provides-Extra: authz
|
|
33
|
+
Requires-Dist: python3_commons[database]; extra == "authz"
|
|
34
|
+
Requires-Dist: python3_commons[api-client]; extra == "authz"
|
|
35
|
+
Provides-Extra: cache
|
|
36
|
+
Requires-Dist: valkey[libvalkey]~=6.1.1; extra == "cache"
|
|
37
|
+
Provides-Extra: database
|
|
38
|
+
Requires-Dist: asyncpg~=0.31.0; extra == "database"
|
|
39
|
+
Requires-Dist: SQLAlchemy[asyncio]~=2.0.51; extra == "database"
|
|
40
|
+
Provides-Extra: object-storage
|
|
41
|
+
Requires-Dist: aiobotocore~=3.7.0; extra == "object-storage"
|
|
42
|
+
Requires-Dist: object-storage-client~=0.0.38; extra == "object-storage"
|
|
43
|
+
Provides-Extra: soap-client
|
|
44
|
+
Requires-Dist: aiohttp[speedups]<3.15.0,>=3.14.1; extra == "soap-client"
|
|
45
|
+
Requires-Dist: certifi~=2026.6.17; extra == "soap-client"
|
|
46
|
+
Requires-Dist: lxml~=6.1.1; extra == "soap-client"
|
|
47
|
+
Requires-Dist: requests~=2.34.2; extra == "soap-client"
|
|
48
|
+
Requires-Dist: zeep[async]~=4.3.3; extra == "soap-client"
|
|
49
|
+
Dynamic: license-file
|
|
50
|
+
|
|
51
|
+
Re-usable Python3 code
|
|
52
|
+
======================
|
|
53
|
+
|
|
54
|
+
Re-usable code for Python 3 projects.
|
|
55
|
+
|
|
56
|
+
Installation
|
|
57
|
+
------------
|
|
58
|
+
|
|
59
|
+
.. code-block:: bash
|
|
60
|
+
|
|
61
|
+
uv add python3-commons
|
|
62
|
+
|
|
63
|
+
Optional Dependencies
|
|
64
|
+
~~~~~~~~~~~~~~~~~~~~~
|
|
65
|
+
|
|
66
|
+
Some features require extra dependencies. You can install them individually or all at once:
|
|
67
|
+
|
|
68
|
+
- ``api-client``: For ``python3_commons.api_client``
|
|
69
|
+
- ``audit``: For ``python3_commons.audit``
|
|
70
|
+
- ``authn``: For ``python3_commons.auth``
|
|
71
|
+
- ``authz``: For ``python3_commons.permissions``
|
|
72
|
+
- ``cache``: For ``python3_commons.cache``
|
|
73
|
+
- ``database``: For ``python3_commons.db``
|
|
74
|
+
- ``object-storage``: For ``python3_commons.object_storage``
|
|
75
|
+
- ``soap-client``: For ``python3_commons.soap_client``
|
|
76
|
+
- ``all``: Install all optional dependencies
|
|
77
|
+
|
|
78
|
+
.. code-block:: bash
|
|
79
|
+
|
|
80
|
+
uv add "python3-commons[all]"
|
|
81
|
+
|
|
82
|
+
Features
|
|
83
|
+
--------
|
|
84
|
+
|
|
85
|
+
Async LRU Cache
|
|
86
|
+
~~~~~~~~~~~~~~~
|
|
87
|
+
|
|
88
|
+
An LRU cache decorator for asynchronous functions that handles ``await`` and prevents the dogpile effect (mitigating multiple concurrent calls for the same key by waiting for the first one to finish).
|
|
89
|
+
|
|
90
|
+
.. code-block:: python
|
|
91
|
+
|
|
92
|
+
from python3_commons.async_functools import async_lru_cache
|
|
93
|
+
|
|
94
|
+
@async_lru_cache(maxsize=128)
|
|
95
|
+
async def fetch_expensive_data(item_id: int):
|
|
96
|
+
# This function will only be called once for a given item_id
|
|
97
|
+
# even if multiple tasks await it simultaneously.
|
|
98
|
+
return await db.get(item_id)
|
|
99
|
+
|
|
100
|
+
# Usage
|
|
101
|
+
result = await fetch_expensive_data(42)
|
|
102
|
+
|
|
103
|
+
API Client
|
|
104
|
+
~~~~~~~~~~
|
|
105
|
+
|
|
106
|
+
A context manager for ``aiohttp`` requests with built-in audit logging to S3 and standardized error mapping to Python exceptions.
|
|
107
|
+
|
|
108
|
+
.. code-block:: python
|
|
109
|
+
|
|
110
|
+
from python3_commons.api_client import request
|
|
111
|
+
from aiohttp import ClientSession
|
|
112
|
+
|
|
113
|
+
async with ClientSession() as session:
|
|
114
|
+
async with request(
|
|
115
|
+
session,
|
|
116
|
+
base_url="https://api.example.com",
|
|
117
|
+
uri="/data",
|
|
118
|
+
method="get",
|
|
119
|
+
audit_name="my_service_audit"
|
|
120
|
+
) as response:
|
|
121
|
+
data = await response.json()
|
|
122
|
+
|
|
123
|
+
SOAP Client
|
|
124
|
+
~~~~~~~~~~~
|
|
125
|
+
|
|
126
|
+
Async SOAP client support for ``zeep`` with S3 auditing capabilities.
|
|
127
|
+
|
|
128
|
+
.. code-block:: python
|
|
129
|
+
|
|
130
|
+
from python3_commons.soap_client import soap_client
|
|
131
|
+
|
|
132
|
+
async with soap_client("https://example.com/service?wsdl") as client:
|
|
133
|
+
result = await client.service.GetData(id=42)
|
|
134
|
+
|
|
135
|
+
Database Management
|
|
136
|
+
~~~~~~~~~~~~~~~~~~~
|
|
137
|
+
|
|
138
|
+
SQLAlchemy async engine and session management with pool tuning, health checks, and dynamic query builders.
|
|
139
|
+
|
|
140
|
+
.. code-block:: python
|
|
141
|
+
|
|
142
|
+
from python3_commons.db import AsyncSessionManager
|
|
143
|
+
from python3_commons.conf import DBSettings
|
|
144
|
+
|
|
145
|
+
# Configuration
|
|
146
|
+
configs = {"default": DBSettings(dsn="postgresql+asyncpg://user:pass@localhost/db")}
|
|
147
|
+
manager = AsyncSessionManager(configs)
|
|
148
|
+
|
|
149
|
+
# Usage
|
|
150
|
+
async with manager.get_session_context("default") as session:
|
|
151
|
+
result = await session.execute(...)
|
|
152
|
+
|
|
153
|
+
# Health check
|
|
154
|
+
from python3_commons.db import is_healthy
|
|
155
|
+
await is_healthy(manager.get_engine("default"))
|
|
156
|
+
|
|
157
|
+
Object Storage
|
|
158
|
+
~~~~~~~~~~~~~~
|
|
159
|
+
|
|
160
|
+
Utilities for async S3 operations using ``aiobotocore``.
|
|
161
|
+
|
|
162
|
+
.. code-block:: python
|
|
163
|
+
|
|
164
|
+
from python3_commons import object_storage
|
|
165
|
+
import io
|
|
166
|
+
|
|
167
|
+
# Upload an object
|
|
168
|
+
await object_storage.put_object(
|
|
169
|
+
bucket_name="my-bucket",
|
|
170
|
+
path="uploads/file.txt",
|
|
171
|
+
data=io.BytesIO(b"Hello World"),
|
|
172
|
+
length=11
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Download an object
|
|
176
|
+
content = await object_storage.get_object("my-bucket", "uploads/file.txt")
|
|
177
|
+
|
|
178
|
+
# List objects
|
|
179
|
+
async for obj in object_storage.list_objects("my-bucket", "uploads/"):
|
|
180
|
+
print(obj['Key'])
|
|
181
|
+
|
|
182
|
+
OIDC Authentication
|
|
183
|
+
~~~~~~~~~~~~~~~~~~~
|
|
184
|
+
|
|
185
|
+
Client for OpenID Connect authentication, supporting configuration fetching, JWKS, and token acquisition.
|
|
186
|
+
|
|
187
|
+
.. code-block:: python
|
|
188
|
+
|
|
189
|
+
from python3_commons.auth import OIDCClient
|
|
190
|
+
from pydantic import HttpUrl
|
|
191
|
+
|
|
192
|
+
client = OIDCClient(
|
|
193
|
+
authority_url=HttpUrl("https://auth.example.com/realms/myrealm"),
|
|
194
|
+
client_id="my-app-client",
|
|
195
|
+
client_secret="secret"
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
async with client:
|
|
199
|
+
token_response = await client.fetch_token(username="user", password="password")
|
|
200
|
+
print(token_response.access_token)
|
|
201
|
+
|
|
202
|
+
Valkey Cache
|
|
203
|
+
~~~~~~~~~~~~
|
|
204
|
+
|
|
205
|
+
Async caching using Valkey (Redis-compatible) with automatic Msgpack serialization for complex types.
|
|
206
|
+
|
|
207
|
+
.. code-block:: python
|
|
208
|
+
|
|
209
|
+
from python3_commons import cache
|
|
210
|
+
|
|
211
|
+
# Store a dictionary
|
|
212
|
+
await cache.store("user:123", {"name": "Alice", "role": "admin"}, ttl=3600)
|
|
213
|
+
|
|
214
|
+
# Retrieve it
|
|
215
|
+
user_data = await cache.get("user:123")
|
|
216
|
+
|
|
217
|
+
# Set operations
|
|
218
|
+
await cache.add_set_item("active_users", "user:123")
|
|
219
|
+
is_active = await cache.has_set_item("active_users", "user:123")
|
|
220
|
+
|
|
221
|
+
Structured Logging
|
|
222
|
+
~~~~~~~~~~~~~~~~~~
|
|
223
|
+
|
|
224
|
+
A ``JSONFormatter`` for structured logging, compatible with standard Python ``logging``.
|
|
225
|
+
|
|
226
|
+
.. code-block:: python
|
|
227
|
+
|
|
228
|
+
import logging
|
|
229
|
+
from python3_commons.log.formatters import JSONFormatter
|
|
230
|
+
|
|
231
|
+
handler = logging.StreamHandler()
|
|
232
|
+
handler.setFormatter(JSONFormatter())
|
|
233
|
+
logging.getLogger().addHandler(handler)
|
|
234
|
+
|
|
235
|
+
logger = logging.getLogger("app")
|
|
236
|
+
logger.info("User logged in", extra={"user_id": "abc-123", "ip": "1.2.3.4"})
|
|
237
|
+
|
|
238
|
+
Serialization
|
|
239
|
+
~~~~~~~~~~~~~
|
|
240
|
+
|
|
241
|
+
Enhanced JSON and Msgpack serialization for types not handled by default (Decimal, datetime, date, dataclasses, Pydantic models).
|
|
242
|
+
|
|
243
|
+
.. code-block:: python
|
|
244
|
+
|
|
245
|
+
from python3_commons.serializers.msgspec import serialize_msgpack, deserialize_msgpack
|
|
246
|
+
from decimal import Decimal
|
|
247
|
+
from datetime import datetime
|
|
248
|
+
|
|
249
|
+
data = {
|
|
250
|
+
"amount": Decimal("150.75"),
|
|
251
|
+
"timestamp": datetime.now(),
|
|
252
|
+
"tags": {"finance", "internal"}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
# Serialize to Msgpack
|
|
256
|
+
binary = serialize_msgpack(data)
|
|
257
|
+
|
|
258
|
+
# Deserialize back
|
|
259
|
+
restored = deserialize_msgpack(binary)
|
|
260
|
+
|
|
261
|
+
RBAC Permissions
|
|
262
|
+
~~~~~~~~~~~~~~~~
|
|
263
|
+
|
|
264
|
+
Database-backed Role-Based Access Control (RBAC) permission checking.
|
|
265
|
+
|
|
266
|
+
.. code-block:: python
|
|
267
|
+
|
|
268
|
+
from python3_commons.permissions import has_user_permission
|
|
269
|
+
from uuid import UUID
|
|
270
|
+
|
|
271
|
+
user_uuid = UUID("...")
|
|
272
|
+
allowed = await has_user_permission(session, user_uuid, "reports.view")
|
|
273
|
+
|
|
274
|
+
General Helpers
|
|
275
|
+
~~~~~~~~~~~~~~~
|
|
276
|
+
|
|
277
|
+
A collection of useful utility functions:
|
|
278
|
+
|
|
279
|
+
- ``to_snake_case(text)``: Converts strings to snake_case.
|
|
280
|
+
- ``round_decimal(value, places)``: Rounds ``Decimal`` values.
|
|
281
|
+
- ``tries(n)``: An async retry decorator.
|
|
282
|
+
- ``log_execution_time``: An async decorator to log how long a function takes.
|
|
283
|
+
- ``date_from_string`` / ``datetime_from_string``: Flexible date/time parsing.
|
|
284
|
+
- ``request_to_curl``: Converts request parameters to a ``curl`` command string.
|
|
285
|
+
|
|
286
|
+
.. code-block:: python
|
|
287
|
+
|
|
288
|
+
from python3_commons.helpers import tries, log_execution_time
|
|
289
|
+
|
|
290
|
+
@tries(3)
|
|
291
|
+
@log_execution_time
|
|
292
|
+
async def flaky_network_call():
|
|
293
|
+
...
|
|
294
|
+
|
|
295
|
+
Async CSV Stream
|
|
296
|
+
~~~~~~~~~~~~~~~~
|
|
297
|
+
|
|
298
|
+
Efficiently generate CSV data as a byte stream from an async generator of tuples.
|
|
299
|
+
|
|
300
|
+
.. code-block:: python
|
|
301
|
+
|
|
302
|
+
from python3_commons.generators import tuple_csv_stream
|
|
303
|
+
|
|
304
|
+
async def generate_rows():
|
|
305
|
+
for i in range(1000):
|
|
306
|
+
yield (i, f"Name {i}", 10.5 * i)
|
|
307
|
+
|
|
308
|
+
async for chunk in tuple_csv_stream(generate_rows(), header=("ID", "Name", "Value")):
|
|
309
|
+
# Send chunk to HTTP response or write to file
|
|
310
|
+
pass
|
|
@@ -2,7 +2,7 @@ python3_commons/__init__.py,sha256=0KgaYU46H_IMKn-BuasoRN3C4Hi45KlkHHoPbU9cwiA,1
|
|
|
2
2
|
python3_commons/api_client.py,sha256=SThSzLhZJxVsKP8n1xDMCczA7SBFbxGFgrhKy9k-9_g,5532
|
|
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=
|
|
5
|
+
python3_commons/auth.py,sha256=9gRdXe54O9e86DS8XgXlX9QDCqrhPIZLJaa4AC9RR_0,8239
|
|
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
|
|
@@ -27,9 +27,11 @@ 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.22.
|
|
31
|
-
python3_commons-0.22.
|
|
32
|
-
python3_commons-0.22.
|
|
33
|
-
python3_commons-0.22.
|
|
34
|
-
python3_commons-0.22.
|
|
35
|
-
python3_commons-0.22.
|
|
30
|
+
python3_commons-0.22.7.dist-info/licenses/AUTHORS.rst,sha256=3R9JnfjfjH5RoPWOeqKFJgxVShSSfzQPIrEr1nxIo9Q,90
|
|
31
|
+
python3_commons-0.22.7.dist-info/licenses/LICENSE,sha256=xxILuojHm4fKQOrMHPSslbyy6WuKAN2RiG74HbrYfzM,34575
|
|
32
|
+
python3_commons-0.22.7.dist-info/METADATA,sha256=QHKCW7uvvkYZHybS_GrgR3t1kSKr3Zdp4oIqN1UuLgs,9472
|
|
33
|
+
python3_commons-0.22.7.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
34
|
+
python3_commons-0.22.7.dist-info/scm_file_list.json,sha256=_VHernCbTfExHtROxsobMxaHoIS1TJJ6S-8WTB3Aqvs,2549
|
|
35
|
+
python3_commons-0.22.7.dist-info/scm_version.json,sha256=Flp3XnyTjvM7r12ci_e2s_UtGH9o8RT-MNUA8D2WUWA,161
|
|
36
|
+
python3_commons-0.22.7.dist-info/top_level.txt,sha256=lJI6sCBf68eUHzupCnn2dzG10lH3jJKTWM_hrN1cQ7M,16
|
|
37
|
+
python3_commons-0.22.7.dist-info/RECORD,,
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"files": [
|
|
3
|
+
".gitignore",
|
|
4
|
+
".python-version",
|
|
5
|
+
"README.md",
|
|
6
|
+
"AUTHORS.rst",
|
|
7
|
+
"CHANGELOG.rst",
|
|
8
|
+
"pyproject.toml",
|
|
9
|
+
".env_template",
|
|
10
|
+
".coveragerc",
|
|
11
|
+
".pre-commit-config.yaml",
|
|
12
|
+
"uv.lock",
|
|
13
|
+
"LICENSE",
|
|
14
|
+
"README.rst",
|
|
15
|
+
"src/python3_commons/conf.py",
|
|
16
|
+
"src/python3_commons/soap_client.py",
|
|
17
|
+
"src/python3_commons/async_functools.py",
|
|
18
|
+
"src/python3_commons/permissions.py",
|
|
19
|
+
"src/python3_commons/fs.py",
|
|
20
|
+
"src/python3_commons/api_client.py",
|
|
21
|
+
"src/python3_commons/audit.py",
|
|
22
|
+
"src/python3_commons/object_storage.py",
|
|
23
|
+
"src/python3_commons/exceptions.py",
|
|
24
|
+
"src/python3_commons/helpers.py",
|
|
25
|
+
"src/python3_commons/generators.py",
|
|
26
|
+
"src/python3_commons/cache.py",
|
|
27
|
+
"src/python3_commons/auth.py",
|
|
28
|
+
"src/python3_commons/__init__.py",
|
|
29
|
+
"src/python3_commons/db/helpers.py",
|
|
30
|
+
"src/python3_commons/db/__init__.py",
|
|
31
|
+
"src/python3_commons/db/models/common.py",
|
|
32
|
+
"src/python3_commons/db/models/rbac.py",
|
|
33
|
+
"src/python3_commons/db/models/auth.py",
|
|
34
|
+
"src/python3_commons/db/models/users.py",
|
|
35
|
+
"src/python3_commons/db/models/__init__.py",
|
|
36
|
+
"src/python3_commons/log/filters.py",
|
|
37
|
+
"src/python3_commons/log/formatters.py",
|
|
38
|
+
"src/python3_commons/log/__init__.py",
|
|
39
|
+
"src/python3_commons/serializers/msgspec.py",
|
|
40
|
+
"src/python3_commons/serializers/json.py",
|
|
41
|
+
"src/python3_commons/serializers/common.py",
|
|
42
|
+
"src/python3_commons/serializers/msgpack.py",
|
|
43
|
+
"src/python3_commons/serializers/__init__.py",
|
|
44
|
+
".devcontainer/docker-compose.yml",
|
|
45
|
+
".devcontainer/Dockerfile",
|
|
46
|
+
".devcontainer/devcontainer.json",
|
|
47
|
+
".github/workflows/checks.yml",
|
|
48
|
+
".github/workflows/release-on-tag-push.yml",
|
|
49
|
+
".github/workflows/python-publish.yaml",
|
|
50
|
+
"tests/__init__.py",
|
|
51
|
+
"tests/integration/conftest.py",
|
|
52
|
+
"tests/integration/test_osc.py",
|
|
53
|
+
"tests/integration/test_cache.py",
|
|
54
|
+
"tests/integration/test_auth.py",
|
|
55
|
+
"tests/integration/__init__.py",
|
|
56
|
+
"tests/unit/test_async_functools.py",
|
|
57
|
+
"tests/unit/conftest.py",
|
|
58
|
+
"tests/unit/test_msgspec.py",
|
|
59
|
+
"tests/unit/test_audit.py",
|
|
60
|
+
"tests/unit/test_auth.py",
|
|
61
|
+
"tests/unit/test_msgpack.py",
|
|
62
|
+
"tests/unit/test_helpers.py",
|
|
63
|
+
"tests/unit/__init__.py",
|
|
64
|
+
"tests/unit/db/test_async_session_manager.py",
|
|
65
|
+
"tests/unit/db/__init__.py",
|
|
66
|
+
"tests/unit/log/test_formatters.py",
|
|
67
|
+
"tests/unit/log/__init__.py",
|
|
68
|
+
"docs/conf.py",
|
|
69
|
+
"docs/Makefile",
|
|
70
|
+
"docs/index.rst",
|
|
71
|
+
"docs/authors.rst",
|
|
72
|
+
"docs/changelog.rst",
|
|
73
|
+
"docs/license.rst",
|
|
74
|
+
"docs/_static/.gitignore"
|
|
75
|
+
]
|
|
76
|
+
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: python3-commons
|
|
3
|
-
Version: 0.22.5
|
|
4
|
-
Summary: Re-usable Python3 code
|
|
5
|
-
Author-email: Oleg Korsak <kamikaze.is.waiting.you@gmail.com>
|
|
6
|
-
License-Expression: GPL-3.0
|
|
7
|
-
Project-URL: Homepage, https://github.com/kamikaze/python3-commons
|
|
8
|
-
Project-URL: Documentation, https://github.com/kamikaze/python3-commons/wiki
|
|
9
|
-
Classifier: Development Status :: 4 - Beta
|
|
10
|
-
Classifier: Programming Language :: Python
|
|
11
|
-
Requires-Python: <3.15.0,>=3.14.0
|
|
12
|
-
Description-Content-Type: text/x-rst
|
|
13
|
-
License-File: LICENSE
|
|
14
|
-
License-File: AUTHORS.rst
|
|
15
|
-
Requires-Dist: msgpack~=1.2.1
|
|
16
|
-
Requires-Dist: msgspec~=0.21.1
|
|
17
|
-
Requires-Dist: pydantic-settings~=2.14.1
|
|
18
|
-
Provides-Extra: all
|
|
19
|
-
Requires-Dist: python3_commons[api-client,audit,authn,authz,cache,database,object-storage,soap-client]; extra == "all"
|
|
20
|
-
Provides-Extra: api-client
|
|
21
|
-
Requires-Dist: aiohttp[speedups]<3.15.0,>=3.14.1; extra == "api-client"
|
|
22
|
-
Requires-Dist: certifi~=2026.6.17; extra == "api-client"
|
|
23
|
-
Requires-Dist: python3_commons[audit,object-storage]; extra == "api-client"
|
|
24
|
-
Provides-Extra: audit
|
|
25
|
-
Requires-Dist: lxml~=6.1.1; extra == "audit"
|
|
26
|
-
Requires-Dist: zeep[async]~=4.3.3; extra == "audit"
|
|
27
|
-
Requires-Dist: python3_commons[object-storage]; extra == "audit"
|
|
28
|
-
Provides-Extra: authn
|
|
29
|
-
Requires-Dist: aiohttp[speedups]<3.15.0,>=3.14.1; extra == "authn"
|
|
30
|
-
Requires-Dist: certifi~=2026.6.17; extra == "authn"
|
|
31
|
-
Requires-Dist: python3_commons[api-client]; extra == "authn"
|
|
32
|
-
Provides-Extra: authz
|
|
33
|
-
Requires-Dist: python3_commons[database]; extra == "authz"
|
|
34
|
-
Requires-Dist: python3_commons[api-client]; extra == "authz"
|
|
35
|
-
Provides-Extra: cache
|
|
36
|
-
Requires-Dist: valkey[libvalkey]~=6.1.1; extra == "cache"
|
|
37
|
-
Provides-Extra: database
|
|
38
|
-
Requires-Dist: asyncpg~=0.31.0; extra == "database"
|
|
39
|
-
Requires-Dist: SQLAlchemy[asyncio]~=2.0.51; extra == "database"
|
|
40
|
-
Provides-Extra: object-storage
|
|
41
|
-
Requires-Dist: aiobotocore~=3.7.0; extra == "object-storage"
|
|
42
|
-
Requires-Dist: object-storage-client~=0.0.37; extra == "object-storage"
|
|
43
|
-
Provides-Extra: soap-client
|
|
44
|
-
Requires-Dist: aiohttp[speedups]<3.15.0,>=3.14.1; extra == "soap-client"
|
|
45
|
-
Requires-Dist: certifi~=2026.6.17; extra == "soap-client"
|
|
46
|
-
Requires-Dist: lxml~=6.1.1; extra == "soap-client"
|
|
47
|
-
Requires-Dist: requests~=2.34.2; extra == "soap-client"
|
|
48
|
-
Requires-Dist: zeep[async]~=4.3.3; extra == "soap-client"
|
|
49
|
-
Dynamic: license-file
|
|
50
|
-
|
|
51
|
-
Re-usable Python3 code
|
|
52
|
-
======================
|
|
53
|
-
|
|
54
|
-
Some description here
|
|
File without changes
|
|
File without changes
|
|
File without changes
|