python3-commons 0.7.6__py3-none-any.whl → 0.8.0__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/conf.py CHANGED
@@ -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):
@@ -1,60 +1,72 @@
1
- import asyncio
2
1
  import contextlib
3
2
  import logging
4
- from typing import AsyncGenerator
3
+ from typing import AsyncGenerator, Callable, Mapping
5
4
 
6
- from asyncpg import CannotConnectNowError
7
- from pydantic import PostgresDsn
8
5
  from sqlalchemy import MetaData
9
- from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
6
+ from sqlalchemy.ext.asyncio import AsyncSession, AsyncEngine, async_engine_from_config
10
7
  from sqlalchemy.ext.asyncio.session import async_sessionmaker
11
8
  from sqlalchemy.orm import declarative_base
12
9
 
13
- from python3_commons.conf import db_settings
10
+ from python3_commons.conf import DBSettings
14
11
 
15
12
  logger = logging.getLogger(__name__)
16
-
17
13
  metadata = MetaData()
18
14
  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
15
 
29
16
 
30
- async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
31
- async with async_session_maker() as session:
32
- yield session
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}')
33
28
 
29
+ raise
34
30
 
35
- get_async_session_context = contextlib.asynccontextmanager(get_async_session)
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
36
38
 
39
+ return engine
37
40
 
38
- async def is_healthy(pg) -> bool:
39
- return await pg.fetchval('SELECT 1 FROM alembic_version;') == 1
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
40
49
 
50
+ return session_maker
41
51
 
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
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
47
56
 
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)
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,12 +1,12 @@
1
1
  python3_commons/__init__.py,sha256=0KgaYU46H_IMKn-BuasoRN3C4Hi45KlkHHoPbU9cwiA,189
2
2
  python3_commons/api_client.py,sha256=zj6zTF-DhUMu4bjj9VWLi63mVODG_SB47Q5qlmG-Sxg,4539
3
3
  python3_commons/audit.py,sha256=DMQ-nrWSs0qilD7wkz_8PV4jXcee75O8FgAm2YIuOiY,6256
4
- python3_commons/conf.py,sha256=GAcjlFlloLriZXa9JmNGMtTPzvtHUActYDiE4C1DacU,749
4
+ python3_commons/conf.py,sha256=iikzlfmz7acoB-q30LDpDVn0uqN5x9F1TosnAQu00pc,940
5
5
  python3_commons/fs.py,sha256=wfLjybXndwLqNlOxTpm_HRJnuTcC4wbrHEOaEeCo9Wc,337
6
6
  python3_commons/helpers.py,sha256=OmuCF7UeJ6oe-rH1Y4ZYVW_uPQ973lCLsikj01wmNqs,3101
7
7
  python3_commons/object_storage.py,sha256=Bte49twIJ4l6VAzIqK6tLx7DFYwijErUmmhWkrZpSJE,4074
8
8
  python3_commons/permissions.py,sha256=V6kT7gBQ0fZRDQwreM9es90BvvpB3gUofuqnFvx2g-o,1553
9
- python3_commons/db/__init__.py,sha256=pZk0Fv-Le2sFC-KaVDdJjDDsHtmv-IJz_aFP5o2PGHA,1682
9
+ python3_commons/db/__init__.py,sha256=t9OsJXVbjsNZ0sW_96kzVMac_YKvKSlsrM5oP1uf9Vg,2338
10
10
  python3_commons/db/helpers.py,sha256=0z-pXAxQg0KqrqfoUcRooRHNrrKi-g2mSO6euVMscpE,1891
11
11
  python3_commons/db/models/__init__.py,sha256=AeeTLUqdqQrhCTi14ACWO0ccyVyWFppZVtCeWShSvR0,193
12
12
  python3_commons/db/models/auth.py,sha256=ctHiz_5x17TdXac7a3SjfuZ0-nEgnxK7RwKOY8qflZc,1195
@@ -19,9 +19,9 @@ python3_commons/serializers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
19
19
  python3_commons/serializers/json.py,sha256=P288wWz9ic38QWEMrpp_uwKPYkQiOgvE1cI4WZn6ZCg,808
20
20
  python3_commons/serializers/msgpack.py,sha256=tzIGGyDL3UpZnnouCtnxuYDx6InKM_C3PP1N4PN8wd4,1269
21
21
  python3_commons/serializers/msgspec.py,sha256=FuZVqOLJb0-lEKrs7dtjhwEbHIpfMUk5yu1hD64zRdc,2038
22
- python3_commons-0.7.6.dist-info/AUTHORS.rst,sha256=3R9JnfjfjH5RoPWOeqKFJgxVShSSfzQPIrEr1nxIo9Q,90
23
- python3_commons-0.7.6.dist-info/LICENSE,sha256=xxILuojHm4fKQOrMHPSslbyy6WuKAN2RiG74HbrYfzM,34575
24
- python3_commons-0.7.6.dist-info/METADATA,sha256=GtDliyLHNI1Y-zqZCrMXr7ggUb98BhvLffLIu3ROTIs,1127
25
- python3_commons-0.7.6.dist-info/WHEEL,sha256=beeZ86-EfXScwlR_HKu4SllMC9wUEj_8Z_4FJ3egI2w,91
26
- python3_commons-0.7.6.dist-info/top_level.txt,sha256=lJI6sCBf68eUHzupCnn2dzG10lH3jJKTWM_hrN1cQ7M,16
27
- python3_commons-0.7.6.dist-info/RECORD,,
22
+ python3_commons-0.8.0.dist-info/licenses/AUTHORS.rst,sha256=3R9JnfjfjH5RoPWOeqKFJgxVShSSfzQPIrEr1nxIo9Q,90
23
+ python3_commons-0.8.0.dist-info/licenses/LICENSE,sha256=xxILuojHm4fKQOrMHPSslbyy6WuKAN2RiG74HbrYfzM,34575
24
+ python3_commons-0.8.0.dist-info/METADATA,sha256=9JFDLJDERSBAArdTZSDt8TOX35dfQEAqR1Ri8-cXbHo,1149
25
+ python3_commons-0.8.0.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
26
+ python3_commons-0.8.0.dist-info/top_level.txt,sha256=lJI6sCBf68eUHzupCnn2dzG10lH3jJKTWM_hrN1cQ7M,16
27
+ python3_commons-0.8.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (76.1.0)
2
+ Generator: setuptools (77.0.3)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5