python3-commons 0.7.6__tar.gz → 0.8.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 (54) hide show
  1. {python3_commons-0.7.6/src/python3_commons.egg-info → python3_commons-0.8.0}/PKG-INFO +3 -2
  2. {python3_commons-0.7.6 → python3_commons-0.8.0}/pyproject.toml +1 -1
  3. {python3_commons-0.7.6 → python3_commons-0.8.0}/src/python3_commons/conf.py +9 -1
  4. python3_commons-0.8.0/src/python3_commons/db/__init__.py +72 -0
  5. {python3_commons-0.7.6 → python3_commons-0.8.0/src/python3_commons.egg-info}/PKG-INFO +3 -2
  6. python3_commons-0.7.6/src/python3_commons/db/__init__.py +0 -60
  7. {python3_commons-0.7.6 → python3_commons-0.8.0}/.coveragerc +0 -0
  8. {python3_commons-0.7.6 → python3_commons-0.8.0}/.github/workflows/python-publish.yaml +0 -0
  9. {python3_commons-0.7.6 → python3_commons-0.8.0}/.gitignore +0 -0
  10. {python3_commons-0.7.6 → python3_commons-0.8.0}/AUTHORS.rst +0 -0
  11. {python3_commons-0.7.6 → python3_commons-0.8.0}/CHANGELOG.rst +0 -0
  12. {python3_commons-0.7.6 → python3_commons-0.8.0}/LICENSE +0 -0
  13. {python3_commons-0.7.6 → python3_commons-0.8.0}/README.md +0 -0
  14. {python3_commons-0.7.6 → python3_commons-0.8.0}/README.rst +0 -0
  15. {python3_commons-0.7.6 → python3_commons-0.8.0}/docs/Makefile +0 -0
  16. {python3_commons-0.7.6 → python3_commons-0.8.0}/docs/_static/.gitignore +0 -0
  17. {python3_commons-0.7.6 → python3_commons-0.8.0}/docs/authors.rst +0 -0
  18. {python3_commons-0.7.6 → python3_commons-0.8.0}/docs/changelog.rst +0 -0
  19. {python3_commons-0.7.6 → python3_commons-0.8.0}/docs/conf.py +0 -0
  20. {python3_commons-0.7.6 → python3_commons-0.8.0}/docs/index.rst +0 -0
  21. {python3_commons-0.7.6 → python3_commons-0.8.0}/docs/license.rst +0 -0
  22. {python3_commons-0.7.6 → python3_commons-0.8.0}/requirements.txt +0 -0
  23. {python3_commons-0.7.6 → python3_commons-0.8.0}/requirements_dev.txt +0 -0
  24. {python3_commons-0.7.6 → python3_commons-0.8.0}/requirements_test.txt +0 -0
  25. {python3_commons-0.7.6 → python3_commons-0.8.0}/setup.cfg +0 -0
  26. {python3_commons-0.7.6 → python3_commons-0.8.0}/setup.py +0 -0
  27. {python3_commons-0.7.6 → python3_commons-0.8.0}/src/python3_commons/__init__.py +0 -0
  28. {python3_commons-0.7.6 → python3_commons-0.8.0}/src/python3_commons/api_client.py +0 -0
  29. {python3_commons-0.7.6 → python3_commons-0.8.0}/src/python3_commons/audit.py +0 -0
  30. {python3_commons-0.7.6 → python3_commons-0.8.0}/src/python3_commons/db/helpers.py +0 -0
  31. {python3_commons-0.7.6 → python3_commons-0.8.0}/src/python3_commons/db/models/__init__.py +0 -0
  32. {python3_commons-0.7.6 → python3_commons-0.8.0}/src/python3_commons/db/models/auth.py +0 -0
  33. {python3_commons-0.7.6 → python3_commons-0.8.0}/src/python3_commons/db/models/common.py +0 -0
  34. {python3_commons-0.7.6 → python3_commons-0.8.0}/src/python3_commons/db/models/rbac.py +0 -0
  35. {python3_commons-0.7.6 → python3_commons-0.8.0}/src/python3_commons/fs.py +0 -0
  36. {python3_commons-0.7.6 → python3_commons-0.8.0}/src/python3_commons/helpers.py +0 -0
  37. {python3_commons-0.7.6 → python3_commons-0.8.0}/src/python3_commons/logging/__init__.py +0 -0
  38. {python3_commons-0.7.6 → python3_commons-0.8.0}/src/python3_commons/logging/filters.py +0 -0
  39. {python3_commons-0.7.6 → python3_commons-0.8.0}/src/python3_commons/logging/formatters.py +0 -0
  40. {python3_commons-0.7.6 → python3_commons-0.8.0}/src/python3_commons/object_storage.py +0 -0
  41. {python3_commons-0.7.6 → python3_commons-0.8.0}/src/python3_commons/permissions.py +0 -0
  42. {python3_commons-0.7.6 → python3_commons-0.8.0}/src/python3_commons/serializers/__init__.py +0 -0
  43. {python3_commons-0.7.6 → python3_commons-0.8.0}/src/python3_commons/serializers/json.py +0 -0
  44. {python3_commons-0.7.6 → python3_commons-0.8.0}/src/python3_commons/serializers/msgpack.py +0 -0
  45. {python3_commons-0.7.6 → python3_commons-0.8.0}/src/python3_commons/serializers/msgspec.py +0 -0
  46. {python3_commons-0.7.6 → python3_commons-0.8.0}/src/python3_commons.egg-info/SOURCES.txt +0 -0
  47. {python3_commons-0.7.6 → python3_commons-0.8.0}/src/python3_commons.egg-info/dependency_links.txt +0 -0
  48. {python3_commons-0.7.6 → python3_commons-0.8.0}/src/python3_commons.egg-info/requires.txt +0 -0
  49. {python3_commons-0.7.6 → python3_commons-0.8.0}/src/python3_commons.egg-info/top_level.txt +0 -0
  50. {python3_commons-0.7.6 → python3_commons-0.8.0}/tests/conftest.py +0 -0
  51. {python3_commons-0.7.6 → python3_commons-0.8.0}/tests/test_audit.py +0 -0
  52. {python3_commons-0.7.6 → python3_commons-0.8.0}/tests/test_helpers.py +0 -0
  53. {python3_commons-0.7.6 → python3_commons-0.8.0}/tests/test_msgpack.py +0 -0
  54. {python3_commons-0.7.6 → python3_commons-0.8.0}/tests/test_msgspec.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: python3-commons
3
- Version: 0.7.6
3
+ Version: 0.8.0
4
4
  Summary: Re-usable Python3 code
5
5
  Author-email: Oleg Korsak <kamikaze.is.waiting.you@gmail.com>
6
6
  License: gpl-3
@@ -27,6 +27,7 @@ Requires-Dist: zeep~=4.3.1
27
27
  Provides-Extra: testing
28
28
  Requires-Dist: pytest; extra == "testing"
29
29
  Requires-Dist: pytest-cov; extra == "testing"
30
+ Dynamic: license-file
30
31
 
31
32
  Re-usable Python3 code
32
33
  ======================
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python3-commons"
7
- version = "0.7.6"
7
+ version = "0.8.0"
8
8
  description = "Re-usable Python3 code"
9
9
  authors = [
10
10
  {name = "Oleg Korsak", email = "kamikaze.is.waiting.you@gmail.com"}
@@ -9,7 +9,15 @@ class CommonSettings(BaseSettings):
9
9
 
10
10
 
11
11
  class DBSettings(BaseSettings):
12
- db_dsn: PostgresDsn | None = None
12
+ dsn: PostgresDsn | None = None
13
+ echo: bool = False,
14
+ pool_size: int = 20,
15
+ max_overflow: int = 0,
16
+ pool_timeout: int = 30,
17
+ pool_recycle: int = 1800, # 30 minutes
18
+
19
+ class Config:
20
+ env_prefix = 'DB_'
13
21
 
14
22
 
15
23
  class S3Settings(BaseSettings):
@@ -0,0 +1,72 @@
1
+ import contextlib
2
+ import logging
3
+ from typing import AsyncGenerator, Callable, Mapping
4
+
5
+ from sqlalchemy import MetaData
6
+ from sqlalchemy.ext.asyncio import AsyncSession, AsyncEngine, async_engine_from_config
7
+ from sqlalchemy.ext.asyncio.session import async_sessionmaker
8
+ from sqlalchemy.orm import declarative_base
9
+
10
+ from python3_commons.conf import DBSettings
11
+
12
+ logger = logging.getLogger(__name__)
13
+ metadata = MetaData()
14
+ Base = declarative_base(metadata=metadata)
15
+
16
+
17
+ class AsyncSessionManager:
18
+ def __init__(self, config: Mapping[str: DBSettings]):
19
+ self.config: Mapping[str: DBSettings] = config
20
+ self.engines: dict[str, AsyncEngine] = {}
21
+ self.session_makers: dict = {}
22
+
23
+ def get_config(self, name: str) -> Mapping[str, str | int]:
24
+ try:
25
+ return self.config[name]
26
+ except KeyError:
27
+ logger.error(f'Missing database config: {name}')
28
+
29
+ raise
30
+
31
+ def get_engine(self, name: str) -> AsyncEngine:
32
+ try:
33
+ engine = self.engines[name]
34
+ except KeyError:
35
+ logger.debug(f'Creating engine: {name}')
36
+ engine = async_engine_from_config(self.config[name])
37
+ self.engines[name] = engine
38
+
39
+ return engine
40
+
41
+ def get_session_maker(self, name: str):
42
+ try:
43
+ session_maker = self.session_makers[name]
44
+ except KeyError:
45
+ logger.debug(f'Creating session maker: {name}')
46
+ engine = self.get_engine(name)
47
+ session_maker = async_sessionmaker(engine)
48
+ self.session_makers[name] = session_maker
49
+
50
+ return session_maker
51
+
52
+ def get_async_session(self, name: str) -> Callable[[], AsyncGenerator[AsyncSession, None]]:
53
+ async def get_session() -> AsyncGenerator[AsyncSession, None]:
54
+ async with self.get_session_maker(name) as session:
55
+ yield session
56
+
57
+ return get_session
58
+
59
+ def get_session_context(self, name: str):
60
+ return contextlib.asynccontextmanager(lambda: self.get_async_session(name)())
61
+
62
+
63
+ async def is_healthy(engine: AsyncEngine) -> bool:
64
+ try:
65
+ async with engine.begin() as conn:
66
+ result = await conn.execute('SELECT 1;')
67
+
68
+ return result.scalar() == 1
69
+ except Exception as e:
70
+ logger.error(f'Database connection is not healthy: {e}')
71
+
72
+ return False
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: python3-commons
3
- Version: 0.7.6
3
+ Version: 0.8.0
4
4
  Summary: Re-usable Python3 code
5
5
  Author-email: Oleg Korsak <kamikaze.is.waiting.you@gmail.com>
6
6
  License: gpl-3
@@ -27,6 +27,7 @@ Requires-Dist: zeep~=4.3.1
27
27
  Provides-Extra: testing
28
28
  Requires-Dist: pytest; extra == "testing"
29
29
  Requires-Dist: pytest-cov; extra == "testing"
30
+ Dynamic: license-file
30
31
 
31
32
  Re-usable Python3 code
32
33
  ======================
@@ -1,60 +0,0 @@
1
- import asyncio
2
- import contextlib
3
- import logging
4
- from typing import AsyncGenerator
5
-
6
- from asyncpg import CannotConnectNowError
7
- from pydantic import PostgresDsn
8
- from sqlalchemy import MetaData
9
- from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
10
- from sqlalchemy.ext.asyncio.session import async_sessionmaker
11
- from sqlalchemy.orm import declarative_base
12
-
13
- from python3_commons.conf import db_settings
14
-
15
- logger = logging.getLogger(__name__)
16
-
17
- metadata = MetaData()
18
- Base = declarative_base(metadata=metadata)
19
- engine = create_async_engine(
20
- str(db_settings.db_dsn),
21
- # echo=True,
22
- pool_size=20,
23
- max_overflow=0,
24
- pool_timeout=30,
25
- pool_recycle=1800, # 30 minutes
26
- )
27
- async_session_maker = async_sessionmaker(engine, expire_on_commit=False)
28
-
29
-
30
- async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
31
- async with async_session_maker() as session:
32
- yield session
33
-
34
-
35
- get_async_session_context = contextlib.asynccontextmanager(get_async_session)
36
-
37
-
38
- async def is_healthy(pg) -> bool:
39
- return await pg.fetchval('SELECT 1 FROM alembic_version;') == 1
40
-
41
-
42
- async def connect_to_db(database, dsn: PostgresDsn):
43
- logger.info('Waiting for services')
44
- logger.debug(f'DB_DSN: {dsn}')
45
- timeout = 0.001
46
- total_timeout = 0
47
-
48
- for i in range(15):
49
- try:
50
- await database.connect()
51
- except (ConnectionRefusedError, CannotConnectNowError):
52
- timeout *= 2
53
- await asyncio.sleep(timeout)
54
- total_timeout += timeout
55
- else:
56
- break
57
- else:
58
- msg = f'Unable to connect database for {int(total_timeout)}s'
59
- logger.error(msg)
60
- raise ConnectionRefusedError(msg)
File without changes