python3-commons 0.16.8__tar.gz → 0.17.0__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 (76) hide show
  1. python3_commons-0.17.0/PKG-INFO +42 -0
  2. {python3_commons-0.16.8 → python3_commons-0.17.0}/pyproject.toml +30 -9
  3. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/api_client.py +6 -2
  4. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/audit.py +6 -2
  5. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/auth.py +10 -5
  6. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/cache.py +8 -4
  7. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/db/__init__.py +8 -4
  8. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/db/helpers.py +6 -2
  9. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/db/models/auth.py +6 -2
  10. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/db/models/common.py +10 -6
  11. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/db/models/rbac.py +7 -3
  12. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/db/models/users.py +6 -2
  13. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/log/formatters.py +6 -6
  14. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/object_storage.py +11 -7
  15. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/permissions.py +6 -2
  16. python3_commons-0.17.0/src/python3_commons.egg-info/PKG-INFO +42 -0
  17. python3_commons-0.17.0/src/python3_commons.egg-info/requires.txt +29 -0
  18. {python3_commons-0.16.8 → python3_commons-0.17.0}/uv.lock +49 -18
  19. python3_commons-0.16.8/PKG-INFO +0 -31
  20. python3_commons-0.16.8/src/python3_commons.egg-info/PKG-INFO +0 -31
  21. python3_commons-0.16.8/src/python3_commons.egg-info/requires.txt +0 -11
  22. {python3_commons-0.16.8 → python3_commons-0.17.0}/.coveragerc +0 -0
  23. {python3_commons-0.16.8 → python3_commons-0.17.0}/.devcontainer/Dockerfile +0 -0
  24. {python3_commons-0.16.8 → python3_commons-0.17.0}/.devcontainer/devcontainer.json +0 -0
  25. {python3_commons-0.16.8 → python3_commons-0.17.0}/.devcontainer/docker-compose.yml +0 -0
  26. {python3_commons-0.16.8 → python3_commons-0.17.0}/.env_template +0 -0
  27. {python3_commons-0.16.8 → python3_commons-0.17.0}/.github/workflows/checks.yml +0 -0
  28. {python3_commons-0.16.8 → python3_commons-0.17.0}/.github/workflows/python-publish.yaml +0 -0
  29. {python3_commons-0.16.8 → python3_commons-0.17.0}/.github/workflows/release-on-tag-push.yml +0 -0
  30. {python3_commons-0.16.8 → python3_commons-0.17.0}/.gitignore +0 -0
  31. {python3_commons-0.16.8 → python3_commons-0.17.0}/.pre-commit-config.yaml +0 -0
  32. {python3_commons-0.16.8 → python3_commons-0.17.0}/.python-version +0 -0
  33. {python3_commons-0.16.8 → python3_commons-0.17.0}/AUTHORS.rst +0 -0
  34. {python3_commons-0.16.8 → python3_commons-0.17.0}/CHANGELOG.rst +0 -0
  35. {python3_commons-0.16.8 → python3_commons-0.17.0}/LICENSE +0 -0
  36. {python3_commons-0.16.8 → python3_commons-0.17.0}/README.md +0 -0
  37. {python3_commons-0.16.8 → python3_commons-0.17.0}/README.rst +0 -0
  38. {python3_commons-0.16.8 → python3_commons-0.17.0}/docs/Makefile +0 -0
  39. {python3_commons-0.16.8 → python3_commons-0.17.0}/docs/_static/.gitignore +0 -0
  40. {python3_commons-0.16.8 → python3_commons-0.17.0}/docs/authors.rst +0 -0
  41. {python3_commons-0.16.8 → python3_commons-0.17.0}/docs/changelog.rst +0 -0
  42. {python3_commons-0.16.8 → python3_commons-0.17.0}/docs/conf.py +0 -0
  43. {python3_commons-0.16.8 → python3_commons-0.17.0}/docs/index.rst +0 -0
  44. {python3_commons-0.16.8 → python3_commons-0.17.0}/docs/license.rst +0 -0
  45. {python3_commons-0.16.8 → python3_commons-0.17.0}/setup.cfg +0 -0
  46. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/__init__.py +0 -0
  47. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/async_functools.py +0 -0
  48. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/conf.py +0 -0
  49. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/db/models/__init__.py +0 -0
  50. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/exceptions.py +0 -0
  51. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/fs.py +0 -0
  52. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/generators.py +0 -0
  53. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/helpers.py +0 -0
  54. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/log/__init__.py +0 -0
  55. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/log/filters.py +0 -0
  56. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/serializers/__init__.py +0 -0
  57. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/serializers/common.py +0 -0
  58. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/serializers/json.py +0 -0
  59. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/serializers/msgpack.py +0 -0
  60. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons/serializers/msgspec.py +0 -0
  61. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons.egg-info/SOURCES.txt +0 -0
  62. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons.egg-info/dependency_links.txt +0 -0
  63. {python3_commons-0.16.8 → python3_commons-0.17.0}/src/python3_commons.egg-info/top_level.txt +0 -0
  64. {python3_commons-0.16.8 → python3_commons-0.17.0}/tests/__init__.py +0 -0
  65. {python3_commons-0.16.8 → python3_commons-0.17.0}/tests/integration/__init__.py +0 -0
  66. {python3_commons-0.16.8 → python3_commons-0.17.0}/tests/integration/test_cache.py +0 -0
  67. {python3_commons-0.16.8 → python3_commons-0.17.0}/tests/integration/test_osc.py +0 -0
  68. {python3_commons-0.16.8 → python3_commons-0.17.0}/tests/unit/__init__.py +0 -0
  69. {python3_commons-0.16.8 → python3_commons-0.17.0}/tests/unit/conftest.py +0 -0
  70. {python3_commons-0.16.8 → python3_commons-0.17.0}/tests/unit/log/__init__.py +0 -0
  71. {python3_commons-0.16.8 → python3_commons-0.17.0}/tests/unit/log/test_formatters.py +0 -0
  72. {python3_commons-0.16.8 → python3_commons-0.17.0}/tests/unit/test_async_functools.py +0 -0
  73. {python3_commons-0.16.8 → python3_commons-0.17.0}/tests/unit/test_audit.py +0 -0
  74. {python3_commons-0.16.8 → python3_commons-0.17.0}/tests/unit/test_helpers.py +0 -0
  75. {python3_commons-0.16.8 → python3_commons-0.17.0}/tests/unit/test_msgpack.py +0 -0
  76. {python3_commons-0.16.8 → python3_commons-0.17.0}/tests/unit/test_msgspec.py +0 -0
@@ -0,0 +1,42 @@
1
+ Metadata-Version: 2.4
2
+ Name: python3-commons
3
+ Version: 0.17.0
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.1.2
16
+ Requires-Dist: msgspec==0.21.1
17
+ Requires-Dist: pydantic-settings~=2.14.0
18
+ Provides-Extra: all
19
+ Requires-Dist: python3_commons[audit,authn,authz,database,object-storage,valkey]; extra == "all"
20
+ Provides-Extra: api-client
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
+ Requires-Dist: python3_commons[object-storage]; extra == "api-client"
25
+ Provides-Extra: authn
26
+ Requires-Dist: aiohttp[speedups]<3.15.0,>=3.13.5; extra == "authn"
27
+ Provides-Extra: authz
28
+ Requires-Dist: python3_commons[database]; extra == "authz"
29
+ Provides-Extra: cache
30
+ Requires-Dist: valkey[libvalkey]~=6.1.1; extra == "cache"
31
+ Provides-Extra: database
32
+ Requires-Dist: asyncpg~=0.31.0; extra == "database"
33
+ Requires-Dist: SQLAlchemy[asyncio]~=2.0.49; extra == "database"
34
+ Provides-Extra: object-storage
35
+ Requires-Dist: aiobotocore~=3.5.0; extra == "object-storage"
36
+ Requires-Dist: object-storage-client==0.0.22; extra == "object-storage"
37
+ Dynamic: license-file
38
+
39
+ Re-usable Python3 code
40
+ ======================
41
+
42
+ Some description here
@@ -18,18 +18,39 @@ classifiers = [
18
18
  keywords = []
19
19
  requires-python = ">=3.14.0,<3.15.0"
20
20
  dependencies = [
21
- "aiobotocore~=3.5.0",
22
- "aiohttp[speedups]>=3.13.5,<3.15.0",
23
- "asyncpg~=0.31.0",
24
- "lxml~=6.1.0",
25
21
  "msgpack~=1.1.2",
26
22
  "msgspec==0.21.1",
27
- "object-storage-client==0.0.22",
28
- "pydantic-settings~=2.14.0",
29
- "SQLAlchemy[asyncio]~=2.0.49",
30
- "valkey[libvalkey]~=6.1.1",
31
- "zeep~=4.3.2"
23
+ "pydantic-settings~=2.14.0"
24
+ ]
25
+
26
+ [project.optional-dependencies]
27
+ all = [
28
+ "python3_commons[audit,authn,authz,database,object-storage,valkey]"
29
+ ]
30
+ api-client = [
31
+ "aiohttp[speedups]>=3.13.5,<3.15.0",
32
+ "lxml~=6.1.0",
33
+ "zeep~=4.3.2",
34
+ "python3_commons[object-storage]"
32
35
  ]
36
+ authn = [
37
+ "aiohttp[speedups]>=3.13.5,<3.15.0",
38
+ ]
39
+ authz = [
40
+ "python3_commons[database]"
41
+ ]
42
+ cache = [
43
+ "valkey[libvalkey]~=6.1.1"
44
+ ]
45
+ database = [
46
+ "asyncpg~=0.31.0",
47
+ "SQLAlchemy[asyncio]~=2.0.49"
48
+ ]
49
+ object-storage = [
50
+ "aiobotocore~=3.5.0",
51
+ "object-storage-client==0.0.22"
52
+ ]
53
+
33
54
 
34
55
  [dependency-groups]
35
56
  dev = [
@@ -9,8 +9,12 @@ from json import dumps
9
9
  from typing import Literal
10
10
  from uuid import uuid4
11
11
 
12
- from aiohttp import ClientResponse, ClientSession, ClientTimeout, client_exceptions
13
- from aiohttp.abc import URL
12
+ try:
13
+ from aiohttp import ClientResponse, ClientSession, ClientTimeout, client_exceptions
14
+ from aiohttp.abc import URL
15
+ except ImportError as e:
16
+ msg = 'Install python3_commons[api-client] to use this feature'
17
+ raise RuntimeError(msg) from e
14
18
 
15
19
  from python3_commons import audit
16
20
  from python3_commons.conf import s3_settings
@@ -5,8 +5,12 @@ from datetime import UTC, datetime
5
5
  from typing import TYPE_CHECKING
6
6
  from uuid import uuid4
7
7
 
8
- from lxml import etree
9
- from zeep.plugins import Plugin
8
+ try:
9
+ from lxml import etree
10
+ from zeep.plugins import Plugin
11
+ except ImportError as e:
12
+ msg = 'Install python3_commons[api-client] to use this feature'
13
+ raise RuntimeError(msg) from e
10
14
 
11
15
  from python3_commons import object_storage
12
16
  from python3_commons.conf import S3Settings, s3_settings
@@ -5,7 +5,12 @@ from collections.abc import Sequence
5
5
  from http import HTTPStatus
6
6
  from typing import TypeVar
7
7
 
8
- import aiohttp
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
+
9
14
  import msgspec
10
15
 
11
16
  from python3_commons.conf import oidc_settings
@@ -51,9 +56,9 @@ async def fetch_openid_config() -> dict:
51
56
  """
52
57
  async with aiohttp.ClientSession() as session, session.get(OIDC_CONFIG_URL) as response:
53
58
  if response.status != HTTPStatus.OK:
54
- msg = 'Failed to fetch OpenID configuration'
59
+ _msg = 'Failed to fetch OpenID configuration'
55
60
 
56
- raise RuntimeError(msg)
61
+ raise RuntimeError(_msg)
57
62
 
58
63
  return await response.json()
59
64
 
@@ -67,8 +72,8 @@ async def fetch_jwks(jwks_uri: str) -> dict:
67
72
 
68
73
  async with aiohttp.ClientSession() as session, session.get(jwks_uri) as response:
69
74
  if response.status != HTTPStatus.OK:
70
- msg = 'Failed to fetch JWKS'
75
+ _msg = 'Failed to fetch JWKS'
71
76
 
72
- raise RuntimeError(msg)
77
+ raise RuntimeError(_msg)
73
78
 
74
79
  return await response.json()
@@ -4,10 +4,14 @@ from collections.abc import Mapping, Sequence
4
4
  from platform import platform
5
5
  from typing import TYPE_CHECKING, Any
6
6
 
7
- import valkey
8
- from valkey.asyncio import ConnectionPool, Sentinel, StrictValkey, Valkey
9
- from valkey.asyncio.retry import Retry
10
- from valkey.backoff import FullJitterBackoff
7
+ try:
8
+ import valkey
9
+ from valkey.asyncio import ConnectionPool, Sentinel, StrictValkey, Valkey
10
+ from valkey.asyncio.retry import Retry
11
+ from valkey.backoff import FullJitterBackoff
12
+ except ImportError as e:
13
+ msg = 'Install python3_commons[cache] to use this feature'
14
+ raise RuntimeError(msg) from e
11
15
 
12
16
  from python3_commons.conf import valkey_settings
13
17
  from python3_commons.helpers import SingletonMeta
@@ -3,10 +3,14 @@ import logging
3
3
  from collections.abc import AsyncGenerator, Callable, Mapping
4
4
  from typing import TYPE_CHECKING
5
5
 
6
- from sqlalchemy import MetaData
7
- from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_engine_from_config
8
- from sqlalchemy.ext.asyncio.session import async_sessionmaker
9
- from sqlalchemy.orm import declarative_base
6
+ try:
7
+ from sqlalchemy import MetaData
8
+ from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_engine_from_config
9
+ from sqlalchemy.ext.asyncio.session import async_sessionmaker
10
+ from sqlalchemy.orm import declarative_base
11
+ except ImportError as e:
12
+ msg = 'Install python3_commons[database] to use this feature'
13
+ raise RuntimeError(msg) from e
10
14
 
11
15
  if TYPE_CHECKING:
12
16
  from python3_commons.conf import DBSettings
@@ -2,8 +2,12 @@ import logging
2
2
  from collections.abc import Mapping
3
3
  from typing import TYPE_CHECKING
4
4
 
5
- import sqlalchemy as sa
6
- from sqlalchemy import asc, desc, func
5
+ try:
6
+ import sqlalchemy as sa
7
+ from sqlalchemy import asc, desc, func
8
+ except ImportError as e:
9
+ msg = 'Install python3_commons[database] to use this feature'
10
+ raise RuntimeError(msg) from e
7
11
 
8
12
  if TYPE_CHECKING:
9
13
  from sqlalchemy.sql.elements import ColumnElement
@@ -1,8 +1,12 @@
1
1
  import uuid
2
2
  from datetime import datetime
3
3
 
4
- from sqlalchemy import UUID, DateTime, ForeignKey, String
5
- from sqlalchemy.orm import Mapped, mapped_column
4
+ try:
5
+ from sqlalchemy import UUID, DateTime, ForeignKey, String
6
+ from sqlalchemy.orm import Mapped, mapped_column
7
+ except ImportError as e:
8
+ msg = 'Install python3_commons[database] to use this feature'
9
+ raise RuntimeError(msg) from e
6
10
 
7
11
  from python3_commons.db import Base
8
12
  from python3_commons.db.models.common import BaseDBUUIDModel
@@ -1,12 +1,16 @@
1
1
  import uuid
2
2
  from datetime import datetime
3
3
 
4
- from sqlalchemy import BIGINT, DateTime
5
- from sqlalchemy.dialects.postgresql import UUID
6
- from sqlalchemy.ext.compiler import compiles
7
- from sqlalchemy.orm import Mapped, mapped_column
8
- from sqlalchemy.sql import expression
9
- from sqlalchemy.sql.ddl import CreateColumn
4
+ try:
5
+ from sqlalchemy import BIGINT, DateTime
6
+ from sqlalchemy.dialects.postgresql import UUID
7
+ from sqlalchemy.ext.compiler import compiles
8
+ from sqlalchemy.orm import Mapped, mapped_column
9
+ from sqlalchemy.sql import expression
10
+ from sqlalchemy.sql.ddl import CreateColumn
11
+ except ImportError as e:
12
+ msg = 'Install python3_commons[database] to use this feature'
13
+ raise RuntimeError(msg) from e
10
14
 
11
15
 
12
16
  class UTCNow(expression.FunctionElement):
@@ -1,9 +1,13 @@
1
1
  import uuid
2
2
  from datetime import datetime
3
3
 
4
- from sqlalchemy import CheckConstraint, DateTime, ForeignKey, PrimaryKeyConstraint, String
5
- from sqlalchemy.dialects.postgresql import UUID
6
- from sqlalchemy.orm import Mapped, mapped_column
4
+ try:
5
+ from sqlalchemy import CheckConstraint, DateTime, ForeignKey, PrimaryKeyConstraint, String
6
+ from sqlalchemy.dialects.postgresql import UUID
7
+ from sqlalchemy.orm import Mapped, mapped_column
8
+ except ImportError as e:
9
+ msg = 'Install python3_commons[database] to use this feature'
10
+ raise RuntimeError(msg) from e
7
11
 
8
12
  from python3_commons.db import Base
9
13
 
@@ -1,7 +1,11 @@
1
1
  import uuid
2
2
 
3
- from sqlalchemy import UUID, ForeignKey, String
4
- from sqlalchemy.orm import Mapped, mapped_column
3
+ try:
4
+ from sqlalchemy import UUID, ForeignKey, String
5
+ from sqlalchemy.orm import Mapped, mapped_column
6
+ except ImportError as e:
7
+ msg = 'Install python3_commons[database] to use this feature'
8
+ raise RuntimeError(msg) from e
5
9
 
6
10
  from python3_commons.db import Base
7
11
  from python3_commons.db.models.common import BaseDBUUIDModel
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
  import traceback
3
- from datetime import UTC, datetime, date
3
+ from datetime import UTC, date, datetime
4
4
  from decimal import Decimal
5
5
  from typing import Any, Final
6
6
 
@@ -51,13 +51,13 @@ def _normalize(v: Any) -> Any:
51
51
 
52
52
 
53
53
  class JSONFormatter(logging.Formatter):
54
- __slots__ = ('_max_tb_chars', '_encoder')
54
+ __slots__ = ('_encoder', '_max_tb_chars')
55
55
 
56
56
  def __init__(
57
- self,
58
- *,
59
- max_exc_tb_chars: int = _DEFAULT_MAX_TB_CHARS,
60
- **kwargs: Any,
57
+ self,
58
+ *,
59
+ max_exc_tb_chars: int = _DEFAULT_MAX_TB_CHARS,
60
+ **kwargs: Any,
61
61
  ) -> None:
62
62
  super().__init__(**kwargs)
63
63
 
@@ -5,9 +5,13 @@ import threading
5
5
  from contextlib import asynccontextmanager
6
6
  from typing import TYPE_CHECKING
7
7
 
8
- import aiobotocore.session
9
- from botocore.config import Config
10
- from object_storage_client import ObjectStorageClient
8
+ try:
9
+ import aiobotocore.session
10
+ from botocore.config import Config
11
+ from object_storage_client import ObjectStorageClient
12
+ except ImportError as e:
13
+ msg = 'Install python3_commons[object-storage] to use this feature'
14
+ raise RuntimeError(msg) from e
11
15
 
12
16
  if TYPE_CHECKING:
13
17
  import io
@@ -132,7 +136,7 @@ async def list_objects(bucket_name: str, prefix: str, *, recursive: bool = True)
132
136
 
133
137
 
134
138
  async def get_object_streams(
135
- bucket_name: str, path: str, *, recursive: bool = True
139
+ bucket_name: str, path: str, *, recursive: bool = True
136
140
  ) -> AsyncGenerator[tuple[str, datetime, StreamingBody]]:
137
141
  async for obj in list_objects(bucket_name, path, recursive=recursive):
138
142
  object_name = obj['Key']
@@ -143,7 +147,7 @@ async def get_object_streams(
143
147
 
144
148
 
145
149
  async def get_objects(
146
- bucket_name: str, path: str, *, recursive: bool = True
150
+ bucket_name: str, path: str, *, recursive: bool = True
147
151
  ) -> AsyncGenerator[tuple[str, datetime, bytes]]:
148
152
  async for object_name, last_modified, stream in get_object_streams(bucket_name, path, recursive=recursive):
149
153
  data = await stream.read()
@@ -165,7 +169,7 @@ async def remove_object(bucket_name: str, object_name: str) -> None:
165
169
 
166
170
 
167
171
  async def remove_objects(
168
- bucket_name: str, prefix: str | None = None, object_names: Iterable[str] | None = None
172
+ bucket_name: str, prefix: str | None = None, object_names: Iterable[str] | None = None
169
173
  ) -> Sequence[Mapping] | None:
170
174
  storage = ObjectStorage(s3_settings)
171
175
 
@@ -188,7 +192,7 @@ async def remove_objects(
188
192
  chunk_size = 1000
189
193
 
190
194
  for i in range(0, len(objects_to_delete), chunk_size):
191
- chunk = objects_to_delete[i: i + chunk_size]
195
+ chunk = objects_to_delete[i : i + chunk_size]
192
196
 
193
197
  response = await s3_client.delete_objects(Bucket=bucket_name, Delete={'Objects': chunk})
194
198
 
@@ -2,8 +2,12 @@ import logging
2
2
  from typing import TYPE_CHECKING
3
3
  from uuid import UUID
4
4
 
5
- import sqlalchemy as sa
6
- from sqlalchemy import and_, exists, func
5
+ try:
6
+ import sqlalchemy as sa
7
+ from sqlalchemy import and_, exists, func
8
+ except ImportError as e:
9
+ msg = 'Install python3_commons[authz] to use this feature'
10
+ raise RuntimeError(msg) from e
7
11
 
8
12
  from python3_commons.db.models.rbac import RBACApiKeyRole, RBACPermission, RBACRolePermission, RBACUserRole
9
13
 
@@ -0,0 +1,42 @@
1
+ Metadata-Version: 2.4
2
+ Name: python3-commons
3
+ Version: 0.17.0
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.1.2
16
+ Requires-Dist: msgspec==0.21.1
17
+ Requires-Dist: pydantic-settings~=2.14.0
18
+ Provides-Extra: all
19
+ Requires-Dist: python3_commons[audit,authn,authz,database,object-storage,valkey]; extra == "all"
20
+ Provides-Extra: api-client
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
+ Requires-Dist: python3_commons[object-storage]; extra == "api-client"
25
+ Provides-Extra: authn
26
+ Requires-Dist: aiohttp[speedups]<3.15.0,>=3.13.5; extra == "authn"
27
+ Provides-Extra: authz
28
+ Requires-Dist: python3_commons[database]; extra == "authz"
29
+ Provides-Extra: cache
30
+ Requires-Dist: valkey[libvalkey]~=6.1.1; extra == "cache"
31
+ Provides-Extra: database
32
+ Requires-Dist: asyncpg~=0.31.0; extra == "database"
33
+ Requires-Dist: SQLAlchemy[asyncio]~=2.0.49; extra == "database"
34
+ Provides-Extra: object-storage
35
+ Requires-Dist: aiobotocore~=3.5.0; extra == "object-storage"
36
+ Requires-Dist: object-storage-client==0.0.22; extra == "object-storage"
37
+ Dynamic: license-file
38
+
39
+ Re-usable Python3 code
40
+ ======================
41
+
42
+ Some description here
@@ -0,0 +1,29 @@
1
+ msgpack~=1.1.2
2
+ msgspec==0.21.1
3
+ pydantic-settings~=2.14.0
4
+
5
+ [all]
6
+ python3_commons[audit,authn,authz,database,object-storage,valkey]
7
+
8
+ [api-client]
9
+ aiohttp[speedups]<3.15.0,>=3.13.5
10
+ lxml~=6.1.0
11
+ zeep~=4.3.2
12
+ python3_commons[object-storage]
13
+
14
+ [authn]
15
+ aiohttp[speedups]<3.15.0,>=3.13.5
16
+
17
+ [authz]
18
+ python3_commons[database]
19
+
20
+ [cache]
21
+ valkey[libvalkey]~=6.1.1
22
+
23
+ [database]
24
+ asyncpg~=0.31.0
25
+ SQLAlchemy[asyncio]~=2.0.49
26
+
27
+ [object-storage]
28
+ aiobotocore~=3.5.0
29
+ object-storage-client==0.0.22
@@ -992,17 +992,43 @@ wheels = [
992
992
  name = "python3-commons"
993
993
  source = { editable = "." }
994
994
  dependencies = [
995
+ { name = "msgpack" },
996
+ { name = "msgspec" },
997
+ { name = "pydantic-settings" },
998
+ ]
999
+
1000
+ [package.optional-dependencies]
1001
+ all = [
995
1002
  { name = "aiobotocore" },
996
1003
  { name = "aiohttp", extra = ["speedups"] },
997
1004
  { name = "asyncpg" },
1005
+ { name = "object-storage-client" },
1006
+ { name = "sqlalchemy", extra = ["asyncio"] },
1007
+ ]
1008
+ api-client = [
1009
+ { name = "aiobotocore" },
1010
+ { name = "aiohttp", extra = ["speedups"] },
998
1011
  { name = "lxml" },
999
- { name = "msgpack" },
1000
- { name = "msgspec" },
1001
1012
  { name = "object-storage-client" },
1002
- { name = "pydantic-settings" },
1013
+ { name = "zeep" },
1014
+ ]
1015
+ authn = [
1016
+ { name = "aiohttp", extra = ["speedups"] },
1017
+ ]
1018
+ authz = [
1019
+ { name = "asyncpg" },
1003
1020
  { name = "sqlalchemy", extra = ["asyncio"] },
1021
+ ]
1022
+ cache = [
1004
1023
  { name = "valkey", extra = ["libvalkey"] },
1005
- { name = "zeep" },
1024
+ ]
1025
+ database = [
1026
+ { name = "asyncpg" },
1027
+ { name = "sqlalchemy", extra = ["asyncio"] },
1028
+ ]
1029
+ object-storage = [
1030
+ { name = "aiobotocore" },
1031
+ { name = "object-storage-client" },
1006
1032
  ]
1007
1033
 
1008
1034
  [package.dev-dependencies]
@@ -1024,18 +1050,23 @@ testing = [
1024
1050
 
1025
1051
  [package.metadata]
1026
1052
  requires-dist = [
1027
- { name = "aiobotocore", specifier = "~=3.5.0" },
1028
- { name = "aiohttp", extras = ["speedups"], specifier = ">=3.13.5,<3.15.0" },
1029
- { name = "asyncpg", specifier = "~=0.31.0" },
1030
- { name = "lxml", specifier = "~=6.1.0" },
1053
+ { name = "aiobotocore", marker = "extra == 'object-storage'", specifier = "~=3.5.0" },
1054
+ { name = "aiohttp", extras = ["speedups"], marker = "extra == 'api-client'", specifier = ">=3.13.5,<3.15.0" },
1055
+ { name = "aiohttp", extras = ["speedups"], marker = "extra == 'authn'", specifier = ">=3.13.5,<3.15.0" },
1056
+ { name = "asyncpg", marker = "extra == 'database'", specifier = "~=0.31.0" },
1057
+ { name = "lxml", marker = "extra == 'api-client'", specifier = "~=6.1.0" },
1031
1058
  { name = "msgpack", specifier = "~=1.1.2" },
1032
1059
  { name = "msgspec", specifier = "==0.21.1" },
1033
- { name = "object-storage-client", specifier = "==0.0.22" },
1060
+ { name = "object-storage-client", marker = "extra == 'object-storage'", specifier = "==0.0.22" },
1034
1061
  { name = "pydantic-settings", specifier = "~=2.14.0" },
1035
- { name = "sqlalchemy", extras = ["asyncio"], specifier = "~=2.0.49" },
1036
- { name = "valkey", extras = ["libvalkey"], specifier = "~=6.1.1" },
1037
- { name = "zeep", specifier = "~=4.3.2" },
1062
+ { name = "python3-commons", extras = ["audit", "authn", "authz", "database", "object-storage", "valkey"], marker = "extra == 'all'" },
1063
+ { name = "python3-commons", extras = ["database"], marker = "extra == 'authz'" },
1064
+ { name = "python3-commons", extras = ["object-storage"], marker = "extra == 'api-client'" },
1065
+ { name = "sqlalchemy", extras = ["asyncio"], marker = "extra == 'database'", specifier = "~=2.0.49" },
1066
+ { name = "valkey", extras = ["libvalkey"], marker = "extra == 'cache'", specifier = "~=6.1.1" },
1067
+ { name = "zeep", marker = "extra == 'api-client'", specifier = "~=4.3.2" },
1038
1068
  ]
1069
+ provides-extras = ["all", "api-client", "authn", "authz", "cache", "database", "object-storage"]
1039
1070
 
1040
1071
  [package.metadata.requires-dev]
1041
1072
  dev = [
@@ -1193,14 +1224,14 @@ asyncio = [
1193
1224
 
1194
1225
  [[package]]
1195
1226
  name = "types-aiobotocore"
1196
- version = "3.4.0"
1227
+ version = "3.5.0"
1197
1228
  source = { registry = "https://pypi.org/simple" }
1198
1229
  dependencies = [
1199
1230
  { name = "botocore-stubs" },
1200
1231
  ]
1201
- sdist = { url = "https://files.pythonhosted.org/packages/77/95/349d980d5e7da4e3f300bba5f09de71ffd70f3d96ad70b260420a4f4ff89/types_aiobotocore-3.4.0.tar.gz", hash = "sha256:010fa82ddc8aba6084e18edbf22981e541b7efc1f85e49e2320501c22913ef35", size = 87621, upload-time = "2026-04-08T02:46:37.316Z" }
1232
+ sdist = { url = "https://files.pythonhosted.org/packages/d5/99/3863acdc373aa621cf56634bb08145fb54f2213e647d893c1ac7b2636c11/types_aiobotocore-3.5.0.tar.gz", hash = "sha256:8636c9e5a9837d41e45264570349d98c0cdad51fe7961ee19664a11094bb2262", size = 87983, upload-time = "2026-04-23T02:57:02.576Z" }
1202
1233
  wheels = [
1203
- { url = "https://files.pythonhosted.org/packages/a7/e2/f2e1baa388a1ef70522132c287b1d9b23c89b4d6a7ec2981fa2f2d65c1a4/types_aiobotocore-3.4.0-py3-none-any.whl", hash = "sha256:17a52c57d879d5f9fbf7b69671dae8d8f593544a89a6f794b9bed4680e7fd334", size = 54653, upload-time = "2026-04-08T02:46:34.185Z" },
1234
+ { url = "https://files.pythonhosted.org/packages/ed/d7/2b2d4d5b64b81149b08cc3fde6e826788c703c012b687cd4cc4d83742afd/types_aiobotocore-3.5.0-py3-none-any.whl", hash = "sha256:7c75ff73c10098d1d885e5b061f05945afdc4e9d0d5b573274292c329abe8a62", size = 54805, upload-time = "2026-04-23T02:56:59.721Z" },
1204
1235
  ]
1205
1236
 
1206
1237
  [package.optional-dependencies]
@@ -1210,11 +1241,11 @@ s3 = [
1210
1241
 
1211
1242
  [[package]]
1212
1243
  name = "types-aiobotocore-s3"
1213
- version = "3.4.0"
1244
+ version = "3.5.0"
1214
1245
  source = { registry = "https://pypi.org/simple" }
1215
- sdist = { url = "https://files.pythonhosted.org/packages/31/12/070369e99af9c9f29b101386b8bda024e79b93ba926414907b2c1015f854/types_aiobotocore_s3-3.4.0.tar.gz", hash = "sha256:c85ef911ad4206a5af45656fc82f73bd49d364f40823f3cc9fb3887704c3f72b", size = 77156, upload-time = "2026-04-08T02:45:34.329Z" }
1246
+ sdist = { url = "https://files.pythonhosted.org/packages/2d/6b/0d9fd45a69afbd2007480a35e8612121f6e27b988741b3a6d0b05c964229/types_aiobotocore_s3-3.5.0.tar.gz", hash = "sha256:147e2128be67de8c8a33b8a9bcca5fa8e963c6c54e52c58c17fa5c6013195a5a", size = 77175, upload-time = "2026-04-23T02:54:40.652Z" }
1216
1247
  wheels = [
1217
- { url = "https://files.pythonhosted.org/packages/2b/fe/995476195978ddf5441ff5a1204dac0d385dc924d954c529557dd1e20631/types_aiobotocore_s3-3.4.0-py3-none-any.whl", hash = "sha256:4356c21155818fbb75ea60639069441a432c90ed1d4f38364f6ccf090838355b", size = 84917, upload-time = "2026-04-08T02:45:33.313Z" },
1248
+ { url = "https://files.pythonhosted.org/packages/3b/c4/ce16af65a9227fe0ff3e737c0b4317e138fffbd06f7d631fe3a1b5a7f0f3/types_aiobotocore_s3-3.5.0-py3-none-any.whl", hash = "sha256:6fc53a6da5ec5cfb5478197fca74e1d11c389e0e20df7c0e0006a84c56224ada", size = 84930, upload-time = "2026-04-23T02:54:36.283Z" },
1218
1249
  ]
1219
1250
 
1220
1251
  [[package]]
@@ -1,31 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: python3-commons
3
- Version: 0.16.8
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: aiobotocore~=3.5.0
16
- Requires-Dist: aiohttp[speedups]<3.15.0,>=3.13.5
17
- Requires-Dist: asyncpg~=0.31.0
18
- Requires-Dist: lxml~=6.1.0
19
- Requires-Dist: msgpack~=1.1.2
20
- Requires-Dist: msgspec==0.21.1
21
- Requires-Dist: object-storage-client==0.0.22
22
- Requires-Dist: pydantic-settings~=2.14.0
23
- Requires-Dist: SQLAlchemy[asyncio]~=2.0.49
24
- Requires-Dist: valkey[libvalkey]~=6.1.1
25
- Requires-Dist: zeep~=4.3.2
26
- Dynamic: license-file
27
-
28
- Re-usable Python3 code
29
- ======================
30
-
31
- Some description here
@@ -1,31 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: python3-commons
3
- Version: 0.16.8
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: aiobotocore~=3.5.0
16
- Requires-Dist: aiohttp[speedups]<3.15.0,>=3.13.5
17
- Requires-Dist: asyncpg~=0.31.0
18
- Requires-Dist: lxml~=6.1.0
19
- Requires-Dist: msgpack~=1.1.2
20
- Requires-Dist: msgspec==0.21.1
21
- Requires-Dist: object-storage-client==0.0.22
22
- Requires-Dist: pydantic-settings~=2.14.0
23
- Requires-Dist: SQLAlchemy[asyncio]~=2.0.49
24
- Requires-Dist: valkey[libvalkey]~=6.1.1
25
- Requires-Dist: zeep~=4.3.2
26
- Dynamic: license-file
27
-
28
- Re-usable Python3 code
29
- ======================
30
-
31
- Some description here
@@ -1,11 +0,0 @@
1
- aiobotocore~=3.5.0
2
- aiohttp[speedups]<3.15.0,>=3.13.5
3
- asyncpg~=0.31.0
4
- lxml~=6.1.0
5
- msgpack~=1.1.2
6
- msgspec==0.21.1
7
- object-storage-client==0.0.22
8
- pydantic-settings~=2.14.0
9
- SQLAlchemy[asyncio]~=2.0.49
10
- valkey[libvalkey]~=6.1.1
11
- zeep~=4.3.2