python3-commons 0.17.4__tar.gz → 0.17.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.17.4/src/python3_commons.egg-info → python3_commons-0.17.5}/PKG-INFO +1 -1
- python3_commons-0.17.5/src/python3_commons/auth.py +178 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/conf.py +6 -5
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/object_storage.py +4 -4
- {python3_commons-0.17.4 → python3_commons-0.17.5/src/python3_commons.egg-info}/PKG-INFO +1 -1
- python3_commons-0.17.4/src/python3_commons/auth.py +0 -79
- {python3_commons-0.17.4 → python3_commons-0.17.5}/.coveragerc +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/.devcontainer/Dockerfile +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/.devcontainer/devcontainer.json +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/.devcontainer/docker-compose.yml +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/.env_template +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/.github/workflows/checks.yml +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/.github/workflows/python-publish.yaml +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/.github/workflows/release-on-tag-push.yml +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/.gitignore +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/.pre-commit-config.yaml +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/.python-version +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/AUTHORS.rst +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/CHANGELOG.rst +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/LICENSE +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/README.md +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/README.rst +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/docs/Makefile +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/docs/_static/.gitignore +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/docs/authors.rst +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/docs/changelog.rst +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/docs/conf.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/docs/index.rst +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/docs/license.rst +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/pyproject.toml +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/setup.cfg +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/__init__.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/api_client.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/async_functools.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/audit.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/cache.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/db/__init__.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/db/helpers.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/db/models/__init__.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/db/models/auth.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/db/models/common.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/db/models/rbac.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/db/models/users.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/exceptions.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/fs.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/generators.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/helpers.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/log/__init__.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/log/filters.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/log/formatters.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/permissions.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/serializers/__init__.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/serializers/common.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/serializers/json.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/serializers/msgpack.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/serializers/msgspec.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons.egg-info/SOURCES.txt +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons.egg-info/dependency_links.txt +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons.egg-info/requires.txt +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons.egg-info/top_level.txt +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/tests/__init__.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/tests/integration/__init__.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/tests/integration/test_cache.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/tests/integration/test_osc.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/tests/unit/__init__.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/tests/unit/conftest.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/tests/unit/log/__init__.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/tests/unit/log/test_formatters.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/tests/unit/test_async_functools.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/tests/unit/test_audit.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/tests/unit/test_helpers.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/tests/unit/test_msgpack.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/tests/unit/test_msgspec.py +0 -0
- {python3_commons-0.17.4 → python3_commons-0.17.5}/uv.lock +0 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from collections.abc import Sequence
|
|
5
|
+
from http import HTTPStatus
|
|
6
|
+
from typing import Self, TypeVar
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
import aiohttp
|
|
10
|
+
except ImportError as e:
|
|
11
|
+
msg = 'Install python3-commons[authn] to use this feature'
|
|
12
|
+
raise RuntimeError(msg) from e
|
|
13
|
+
|
|
14
|
+
import msgspec
|
|
15
|
+
|
|
16
|
+
from python3_commons.conf import oidc_settings
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class TokenData(msgspec.Struct):
|
|
22
|
+
exp: int
|
|
23
|
+
iat: int
|
|
24
|
+
iss: str
|
|
25
|
+
sub: str
|
|
26
|
+
aud: str | Sequence[str] | None = None
|
|
27
|
+
email: str | None = None
|
|
28
|
+
name: str | None = None
|
|
29
|
+
preferred_username: str | None = None
|
|
30
|
+
realm_access: dict[str, Sequence[str]] | None = None
|
|
31
|
+
resource_access: dict[str, dict[str, Sequence[str]]] | None = None
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def roles(self) -> list[str]:
|
|
35
|
+
roles_list = []
|
|
36
|
+
|
|
37
|
+
if self.realm_access:
|
|
38
|
+
roles_list.extend(self.realm_access.get('roles', []))
|
|
39
|
+
|
|
40
|
+
if self.resource_access:
|
|
41
|
+
for client in self.resource_access.values():
|
|
42
|
+
roles_list.extend(client.get('roles', []))
|
|
43
|
+
|
|
44
|
+
return list(set(roles_list))
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
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
|
+
|
|
53
|
+
class OIDCTokenResponse(msgspec.Struct):
|
|
54
|
+
access_token: str
|
|
55
|
+
token_type: str
|
|
56
|
+
expires_in: int
|
|
57
|
+
refresh_token: str
|
|
58
|
+
scope: str
|
|
59
|
+
id_token: str
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
async def fetch_openid_config() -> dict:
|
|
63
|
+
"""
|
|
64
|
+
Fetch the OpenID configuration (including JWKS URI) from OIDC authority.
|
|
65
|
+
"""
|
|
66
|
+
async with aiohttp.ClientSession() as session, session.get(OIDC_CONFIG_URL) as response:
|
|
67
|
+
if response.status != HTTPStatus.OK:
|
|
68
|
+
_msg = 'Failed to fetch OpenID configuration'
|
|
69
|
+
|
|
70
|
+
raise RuntimeError(_msg)
|
|
71
|
+
|
|
72
|
+
return await response.json()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
async def fetch_jwks(jwks_uri: str) -> dict:
|
|
76
|
+
"""
|
|
77
|
+
Fetch the JSON Web Key Set (JWKS) for validating the token's signature.
|
|
78
|
+
"""
|
|
79
|
+
if oidc_settings.authority_internal_url:
|
|
80
|
+
jwks_uri = jwks_uri.replace(str(oidc_settings.authority_url), str(oidc_settings.authority_internal_url))
|
|
81
|
+
|
|
82
|
+
async with aiohttp.ClientSession() as session, session.get(jwks_uri) as response:
|
|
83
|
+
if response.status != HTTPStatus.OK:
|
|
84
|
+
_msg = 'Failed to fetch JWKS'
|
|
85
|
+
|
|
86
|
+
raise RuntimeError(_msg)
|
|
87
|
+
|
|
88
|
+
return await response.json()
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class OIDCError(Exception):
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class OIDCAuthError(OIDCError):
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class OIDCClient:
|
|
100
|
+
def __init__(
|
|
101
|
+
self,
|
|
102
|
+
token_url: str,
|
|
103
|
+
client_id: str,
|
|
104
|
+
client_secret: str | None = None,
|
|
105
|
+
*,
|
|
106
|
+
timeout: float = 10.0,
|
|
107
|
+
session: aiohttp.ClientSession | None = None,
|
|
108
|
+
) -> None:
|
|
109
|
+
self._token_url = token_url
|
|
110
|
+
self._client_id = client_id
|
|
111
|
+
self._client_secret = client_secret
|
|
112
|
+
self._timeout = aiohttp.ClientTimeout(total=timeout)
|
|
113
|
+
self._session = session
|
|
114
|
+
self._owns_session = session is None
|
|
115
|
+
|
|
116
|
+
async def __aenter__(self) -> Self:
|
|
117
|
+
if self._session is None:
|
|
118
|
+
self._session = aiohttp.ClientSession(timeout=self._timeout)
|
|
119
|
+
return self
|
|
120
|
+
|
|
121
|
+
async def __aexit__(self, *_: object) -> None:
|
|
122
|
+
if self._owns_session and self._session:
|
|
123
|
+
await self._session.close()
|
|
124
|
+
|
|
125
|
+
async def fetch_token(
|
|
126
|
+
self,
|
|
127
|
+
*,
|
|
128
|
+
username: str,
|
|
129
|
+
password: str,
|
|
130
|
+
scope: str = 'openid profile email',
|
|
131
|
+
) -> OIDCTokenResponse:
|
|
132
|
+
if self._session is None:
|
|
133
|
+
msg = 'ClientSession not initialized'
|
|
134
|
+
|
|
135
|
+
raise RuntimeError(msg)
|
|
136
|
+
|
|
137
|
+
data = {
|
|
138
|
+
'grant_type': 'password',
|
|
139
|
+
'username': username,
|
|
140
|
+
'password': password,
|
|
141
|
+
'client_id': self._client_id,
|
|
142
|
+
'scope': scope,
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if self._client_secret:
|
|
146
|
+
data['client_secret'] = self._client_secret
|
|
147
|
+
|
|
148
|
+
try:
|
|
149
|
+
async with self._session.post(
|
|
150
|
+
self._token_url,
|
|
151
|
+
data=data,
|
|
152
|
+
headers={'Content-Type': 'application/x-www-form-urlencoded'},
|
|
153
|
+
) as resp:
|
|
154
|
+
payload = await resp.json(content_type=None)
|
|
155
|
+
|
|
156
|
+
except TimeoutError as e:
|
|
157
|
+
msg = 'OIDC request timed out'
|
|
158
|
+
|
|
159
|
+
raise OIDCError(msg) from e
|
|
160
|
+
except aiohttp.ClientError as e:
|
|
161
|
+
msg = 'OIDC transport error'
|
|
162
|
+
|
|
163
|
+
raise OIDCError(msg) from e
|
|
164
|
+
|
|
165
|
+
if not resp.ok:
|
|
166
|
+
error = payload.get('error')
|
|
167
|
+
description = payload.get('error_description')
|
|
168
|
+
|
|
169
|
+
if error in {'invalid_grant', 'invalid_client'}:
|
|
170
|
+
msg = f'{error}: {description}'
|
|
171
|
+
|
|
172
|
+
raise OIDCAuthError(msg)
|
|
173
|
+
|
|
174
|
+
msg = f'{error}: {description}'
|
|
175
|
+
|
|
176
|
+
raise OIDCError(msg)
|
|
177
|
+
|
|
178
|
+
return payload
|
|
@@ -21,6 +21,7 @@ class OIDCSettings(BaseSettings):
|
|
|
21
21
|
authority_url: HttpUrl | None = None
|
|
22
22
|
authority_internal_url: HttpUrl | None = None
|
|
23
23
|
client_id: str | None = None
|
|
24
|
+
client_secret: SecretStr = SecretStr('')
|
|
24
25
|
redirect_uri: str | None = None
|
|
25
26
|
scope: StringSeq = (
|
|
26
27
|
'openid',
|
|
@@ -58,11 +59,11 @@ class DBSettings(BaseSettings):
|
|
|
58
59
|
@model_validator(mode='after')
|
|
59
60
|
def build_dsn_if_missing(self) -> DBSettings:
|
|
60
61
|
if self.dsn is None and all(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
(
|
|
63
|
+
self.user,
|
|
64
|
+
self.password,
|
|
65
|
+
self.name,
|
|
66
|
+
)
|
|
66
67
|
):
|
|
67
68
|
self.dsn = PostgresDsn.build(
|
|
68
69
|
scheme=self.scheme,
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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()
|
|
@@ -173,7 +173,7 @@ async def remove_object(bucket_name: str, object_name: str) -> None:
|
|
|
173
173
|
|
|
174
174
|
|
|
175
175
|
async def remove_objects(
|
|
176
|
-
|
|
176
|
+
bucket_name: str, prefix: str | None = None, object_names: Iterable[str] | None = None
|
|
177
177
|
) -> Sequence[Mapping] | None:
|
|
178
178
|
storage = ObjectStorage(s3_settings)
|
|
179
179
|
|
|
@@ -196,7 +196,7 @@ async def remove_objects(
|
|
|
196
196
|
chunk_size = 1000
|
|
197
197
|
|
|
198
198
|
for i in range(0, len(objects_to_delete), chunk_size):
|
|
199
|
-
chunk = objects_to_delete[i: i + chunk_size]
|
|
199
|
+
chunk = objects_to_delete[i : i + chunk_size]
|
|
200
200
|
|
|
201
201
|
response = await s3_client.delete_objects(Bucket=bucket_name, Delete={'Objects': chunk})
|
|
202
202
|
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
from collections.abc import Sequence
|
|
5
|
-
from http import HTTPStatus
|
|
6
|
-
from typing import TypeVar
|
|
7
|
-
|
|
8
|
-
try:
|
|
9
|
-
import aiohttp
|
|
10
|
-
except ImportError as e:
|
|
11
|
-
msg = 'Install python3-commons[authn] to use this feature'
|
|
12
|
-
raise RuntimeError(msg) from e
|
|
13
|
-
|
|
14
|
-
import msgspec
|
|
15
|
-
|
|
16
|
-
from python3_commons.conf import oidc_settings
|
|
17
|
-
|
|
18
|
-
logger = logging.getLogger(__name__)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class TokenData(msgspec.Struct):
|
|
22
|
-
exp: int
|
|
23
|
-
iat: int
|
|
24
|
-
iss: str
|
|
25
|
-
sub: str
|
|
26
|
-
aud: str | Sequence[str] | None = None
|
|
27
|
-
email: str | None = None
|
|
28
|
-
name: str | None = None
|
|
29
|
-
preferred_username: str | None = None
|
|
30
|
-
realm_access: dict[str, Sequence[str]] | None = None
|
|
31
|
-
resource_access: dict[str, dict[str, Sequence[str]]] | None = None
|
|
32
|
-
|
|
33
|
-
@property
|
|
34
|
-
def roles(self) -> list[str]:
|
|
35
|
-
roles_list = []
|
|
36
|
-
|
|
37
|
-
if self.realm_access:
|
|
38
|
-
roles_list.extend(self.realm_access.get('roles', []))
|
|
39
|
-
|
|
40
|
-
if self.resource_access:
|
|
41
|
-
for client in self.resource_access.values():
|
|
42
|
-
roles_list.extend(client.get('roles', []))
|
|
43
|
-
|
|
44
|
-
return list(set(roles_list))
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
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
|
-
|
|
53
|
-
async def fetch_openid_config() -> dict:
|
|
54
|
-
"""
|
|
55
|
-
Fetch the OpenID configuration (including JWKS URI) from OIDC authority.
|
|
56
|
-
"""
|
|
57
|
-
async with aiohttp.ClientSession() as session, session.get(OIDC_CONFIG_URL) as response:
|
|
58
|
-
if response.status != HTTPStatus.OK:
|
|
59
|
-
_msg = 'Failed to fetch OpenID configuration'
|
|
60
|
-
|
|
61
|
-
raise RuntimeError(_msg)
|
|
62
|
-
|
|
63
|
-
return await response.json()
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
async def fetch_jwks(jwks_uri: str) -> dict:
|
|
67
|
-
"""
|
|
68
|
-
Fetch the JSON Web Key Set (JWKS) for validating the token's signature.
|
|
69
|
-
"""
|
|
70
|
-
if oidc_settings.authority_internal_url:
|
|
71
|
-
jwks_uri = jwks_uri.replace(str(oidc_settings.authority_url), str(oidc_settings.authority_internal_url))
|
|
72
|
-
|
|
73
|
-
async with aiohttp.ClientSession() as session, session.get(jwks_uri) as response:
|
|
74
|
-
if response.status != HTTPStatus.OK:
|
|
75
|
-
_msg = 'Failed to fetch JWKS'
|
|
76
|
-
|
|
77
|
-
raise RuntimeError(_msg)
|
|
78
|
-
|
|
79
|
-
return await response.json()
|
|
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.17.4 → python3_commons-0.17.5}/src/python3_commons/serializers/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/serializers/msgpack.py
RENAMED
|
File without changes
|
{python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons/serializers/msgspec.py
RENAMED
|
File without changes
|
|
File without changes
|
{python3_commons-0.17.4 → python3_commons-0.17.5}/src/python3_commons.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
{python3_commons-0.17.4 → python3_commons-0.17.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
|