maleo-database 0.0.1__py3-none-any.whl → 0.0.3__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.
- maleo/database/managers/session.py +180 -47
- {maleo_database-0.0.1.dist-info → maleo_database-0.0.3.dist-info}/METADATA +19 -2
- {maleo_database-0.0.1.dist-info → maleo_database-0.0.3.dist-info}/RECORD +6 -6
- {maleo_database-0.0.1.dist-info → maleo_database-0.0.3.dist-info}/WHEEL +0 -0
- {maleo_database-0.0.1.dist-info → maleo_database-0.0.3.dist-info}/licenses/LICENSE +0 -0
- {maleo_database-0.0.1.dist-info → maleo_database-0.0.3.dist-info}/top_level.txt +0 -0
@@ -4,18 +4,41 @@ from contextlib import (
|
|
4
4
|
asynccontextmanager,
|
5
5
|
contextmanager,
|
6
6
|
)
|
7
|
+
from datetime import datetime, timezone
|
7
8
|
from pydantic import ValidationError
|
8
9
|
from sqlalchemy.engine import Engine
|
9
10
|
from sqlalchemy.exc import SQLAlchemyError
|
10
11
|
from sqlalchemy.ext.asyncio import AsyncEngine, async_sessionmaker, AsyncSession
|
11
12
|
from sqlalchemy.orm import sessionmaker, Session
|
12
13
|
from typing import AsyncGenerator, Generator, Literal, Tuple, Union, overload
|
14
|
+
from uuid import UUID, uuid4
|
15
|
+
from maleo.mixins.timestamp import OperationTimestamp
|
16
|
+
from maleo.types.base.uuid import OptionalUUID
|
17
|
+
from maleo.logging.logger import Database
|
13
18
|
from ..enums import Connection
|
19
|
+
from ..config import (
|
20
|
+
PostgreSQLDatabaseConfig,
|
21
|
+
MySQLDatabaseConfig,
|
22
|
+
SQLiteDatabaseConfig,
|
23
|
+
SQLServerDatabaseConfig,
|
24
|
+
)
|
14
25
|
|
15
26
|
|
16
27
|
class SessionManager:
|
17
|
-
def __init__(
|
28
|
+
def __init__(
|
29
|
+
self,
|
30
|
+
config: Union[
|
31
|
+
PostgreSQLDatabaseConfig,
|
32
|
+
MySQLDatabaseConfig,
|
33
|
+
SQLiteDatabaseConfig,
|
34
|
+
SQLServerDatabaseConfig,
|
35
|
+
],
|
36
|
+
engines: Tuple[AsyncEngine, Engine],
|
37
|
+
logger: Database,
|
38
|
+
) -> None:
|
39
|
+
self._config = config
|
18
40
|
self._async_engine, self._sync_engine = engines
|
41
|
+
self._logger = logger
|
19
42
|
self._async_sessionmaker: async_sessionmaker[AsyncSession] = async_sessionmaker[
|
20
43
|
AsyncSession
|
21
44
|
](bind=self._async_engine, expire_on_commit=True)
|
@@ -23,101 +46,211 @@ class SessionManager:
|
|
23
46
|
bind=self._sync_engine, expire_on_commit=True
|
24
47
|
)
|
25
48
|
|
26
|
-
async def _async_session_handler(
|
49
|
+
async def _async_session_handler(
|
50
|
+
self, operation_id: UUID
|
51
|
+
) -> AsyncGenerator[AsyncSession, None]:
|
27
52
|
"""Async session handler with proper error handling."""
|
53
|
+
executed_at = datetime.now(tz=timezone.utc)
|
28
54
|
session = self._async_sessionmaker()
|
29
55
|
try:
|
30
56
|
yield session
|
31
57
|
await session.commit()
|
58
|
+
completed_at = datetime.now(tz=timezone.utc)
|
59
|
+
self._logger.info(
|
60
|
+
f"Operation {operation_id} - success - Committed async database transaction",
|
61
|
+
extra={
|
62
|
+
"json_fields": {
|
63
|
+
"config": self._config.model_dump(mode="json"),
|
64
|
+
"operation_id": operation_id,
|
65
|
+
"success": True,
|
66
|
+
"timestamp": OperationTimestamp(
|
67
|
+
executed_at=executed_at,
|
68
|
+
completed_at=completed_at,
|
69
|
+
duration=(completed_at - executed_at).total_seconds(),
|
70
|
+
).model_dump(mode="json"),
|
71
|
+
},
|
72
|
+
},
|
73
|
+
)
|
32
74
|
except (SQLAlchemyError, ValidationError, Exception):
|
33
75
|
await session.rollback()
|
76
|
+
completed_at = datetime.now(tz=timezone.utc)
|
77
|
+
self._logger.error(
|
78
|
+
f"Operation {operation_id} - failed - Error handling async database session",
|
79
|
+
exc_info=True,
|
80
|
+
extra={
|
81
|
+
"json_fields": {
|
82
|
+
"config": self._config.model_dump(mode="json"),
|
83
|
+
"operation_id": operation_id,
|
84
|
+
"success": False,
|
85
|
+
"timestamp": OperationTimestamp(
|
86
|
+
executed_at=executed_at,
|
87
|
+
completed_at=completed_at,
|
88
|
+
duration=(completed_at - executed_at).total_seconds(),
|
89
|
+
).model_dump(mode="json"),
|
90
|
+
},
|
91
|
+
},
|
92
|
+
)
|
34
93
|
raise
|
35
94
|
finally:
|
36
95
|
await session.close()
|
96
|
+
completed_at = datetime.now(tz=timezone.utc)
|
97
|
+
self._logger.info(
|
98
|
+
f"Operation {operation_id} - success - Closed async database session",
|
99
|
+
extra={
|
100
|
+
"json_fields": {
|
101
|
+
"config": self._config.model_dump(mode="json"),
|
102
|
+
"operation_id": operation_id,
|
103
|
+
"success": True,
|
104
|
+
"timestamp": OperationTimestamp(
|
105
|
+
executed_at=executed_at,
|
106
|
+
completed_at=completed_at,
|
107
|
+
duration=(completed_at - executed_at).total_seconds(),
|
108
|
+
).model_dump(mode="json"),
|
109
|
+
},
|
110
|
+
},
|
111
|
+
)
|
37
112
|
|
38
|
-
def _sync_session_handler(
|
113
|
+
def _sync_session_handler(
|
114
|
+
self, operation_id: UUID
|
115
|
+
) -> Generator[Session, None, None]:
|
39
116
|
"""Sync session handler with proper error handling."""
|
117
|
+
executed_at = datetime.now(tz=timezone.utc)
|
40
118
|
session = self._sync_sessionmaker()
|
41
119
|
try:
|
42
120
|
yield session
|
43
121
|
session.commit()
|
122
|
+
completed_at = datetime.now(tz=timezone.utc)
|
123
|
+
self._logger.info(
|
124
|
+
f"Operation {operation_id} - success - Committed sync database transaction",
|
125
|
+
extra={
|
126
|
+
"json_fields": {
|
127
|
+
"config": self._config.model_dump(mode="json"),
|
128
|
+
"operation_id": operation_id,
|
129
|
+
"success": True,
|
130
|
+
"timestamp": OperationTimestamp(
|
131
|
+
executed_at=executed_at,
|
132
|
+
completed_at=completed_at,
|
133
|
+
duration=(completed_at - executed_at).total_seconds(),
|
134
|
+
).model_dump(mode="json"),
|
135
|
+
},
|
136
|
+
},
|
137
|
+
)
|
44
138
|
except (SQLAlchemyError, ValidationError, Exception):
|
45
139
|
session.rollback()
|
140
|
+
completed_at = datetime.now(tz=timezone.utc)
|
141
|
+
self._logger.error(
|
142
|
+
f"Operation {operation_id} - failed - Error handling sync database session",
|
143
|
+
exc_info=True,
|
144
|
+
extra={
|
145
|
+
"json_fields": {
|
146
|
+
"config": self._config.model_dump(mode="json"),
|
147
|
+
"operation_id": operation_id,
|
148
|
+
"success": False,
|
149
|
+
"timestamp": OperationTimestamp(
|
150
|
+
executed_at=executed_at,
|
151
|
+
completed_at=completed_at,
|
152
|
+
duration=(completed_at - executed_at).total_seconds(),
|
153
|
+
).model_dump(mode="json"),
|
154
|
+
},
|
155
|
+
},
|
156
|
+
)
|
46
157
|
raise
|
47
158
|
finally:
|
48
159
|
session.close()
|
160
|
+
completed_at = datetime.now(tz=timezone.utc)
|
161
|
+
self._logger.info(
|
162
|
+
f"Operation {operation_id} - success - Closed sync database session",
|
163
|
+
extra={
|
164
|
+
"json_fields": {
|
165
|
+
"config": self._config.model_dump(mode="json"),
|
166
|
+
"operation_id": operation_id,
|
167
|
+
"success": True,
|
168
|
+
"timestamp": OperationTimestamp(
|
169
|
+
executed_at=executed_at,
|
170
|
+
completed_at=completed_at,
|
171
|
+
duration=(completed_at - executed_at).total_seconds(),
|
172
|
+
).model_dump(mode="json"),
|
173
|
+
},
|
174
|
+
},
|
175
|
+
)
|
49
176
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
177
|
+
@asynccontextmanager
|
178
|
+
async def _async_context_manager(
|
179
|
+
self, operation_id: UUID
|
180
|
+
) -> AsyncGenerator[AsyncSession, None]:
|
181
|
+
"""Async context manager implementation."""
|
182
|
+
async for session in self._async_session_handler(operation_id):
|
183
|
+
yield session
|
55
184
|
|
56
|
-
@
|
57
|
-
def
|
58
|
-
self,
|
59
|
-
) -> Generator[Session, None, None]:
|
60
|
-
|
61
|
-
|
62
|
-
self, connection: Connection = Connection.ASYNC
|
63
|
-
) -> Union[AsyncGenerator[AsyncSession, None], Generator[Session, None, None]]:
|
64
|
-
"""Returns a generator for dependency injection."""
|
65
|
-
if connection is Connection.ASYNC:
|
66
|
-
return self._async_session_handler()
|
67
|
-
else:
|
68
|
-
return self._sync_session_handler()
|
185
|
+
@contextmanager
|
186
|
+
def _sync_context_manager(
|
187
|
+
self, operation_id: UUID
|
188
|
+
) -> Generator[Session, None, None]:
|
189
|
+
"""Sync context manager implementation."""
|
190
|
+
yield from self._sync_session_handler(operation_id)
|
69
191
|
|
70
192
|
# Overloaded context manager methods
|
71
193
|
@overload
|
72
194
|
def get(
|
73
|
-
self, connection: Literal[Connection.ASYNC]
|
195
|
+
self, connection: Literal[Connection.ASYNC], operation_id: OptionalUUID = None
|
74
196
|
) -> AbstractAsyncContextManager[AsyncSession]: ...
|
75
197
|
|
76
198
|
@overload
|
77
199
|
def get(
|
78
|
-
self, connection: Literal[Connection.SYNC]
|
200
|
+
self, connection: Literal[Connection.SYNC], operation_id: OptionalUUID = None
|
79
201
|
) -> AbstractContextManager[Session]: ...
|
80
202
|
|
81
203
|
def get(
|
82
|
-
self,
|
204
|
+
self,
|
205
|
+
connection: Connection = Connection.ASYNC,
|
206
|
+
operation_id: OptionalUUID = None,
|
83
207
|
) -> Union[
|
84
208
|
AbstractAsyncContextManager[AsyncSession], AbstractContextManager[Session]
|
85
209
|
]:
|
86
210
|
"""Context manager for manual session handling."""
|
211
|
+
if operation_id is None:
|
212
|
+
operation_id = uuid4()
|
87
213
|
if connection is Connection.ASYNC:
|
88
|
-
return self._async_context_manager()
|
214
|
+
return self._async_context_manager(operation_id)
|
89
215
|
else:
|
90
|
-
return self._sync_context_manager()
|
91
|
-
|
92
|
-
@asynccontextmanager
|
93
|
-
async def _async_context_manager(self) -> AsyncGenerator[AsyncSession, None]:
|
94
|
-
"""Async context manager implementation."""
|
95
|
-
async for session in self._async_session_handler():
|
96
|
-
yield session
|
97
|
-
|
98
|
-
@contextmanager
|
99
|
-
def _sync_context_manager(self) -> Generator[Session, None, None]:
|
100
|
-
"""Sync context manager implementation."""
|
101
|
-
yield from self._sync_session_handler()
|
216
|
+
return self._sync_context_manager(operation_id)
|
102
217
|
|
103
218
|
# Alternative: More explicit methods
|
104
219
|
@asynccontextmanager
|
105
|
-
async def get_async(
|
220
|
+
async def get_async(
|
221
|
+
self, operation_id: OptionalUUID = None
|
222
|
+
) -> AsyncGenerator[AsyncSession, None]:
|
106
223
|
"""Explicit async context manager."""
|
107
|
-
|
224
|
+
if operation_id is None:
|
225
|
+
operation_id = uuid4()
|
226
|
+
async for session in self._async_session_handler(operation_id):
|
108
227
|
yield session
|
109
228
|
|
110
229
|
@contextmanager
|
111
|
-
def get_sync(
|
230
|
+
def get_sync(
|
231
|
+
self, operation_id: OptionalUUID = None
|
232
|
+
) -> Generator[Session, None, None]:
|
112
233
|
"""Explicit sync context manager."""
|
113
|
-
|
114
|
-
|
115
|
-
|
234
|
+
if operation_id is None:
|
235
|
+
operation_id = uuid4()
|
236
|
+
yield from self._sync_session_handler(operation_id)
|
116
237
|
|
117
|
-
def
|
238
|
+
def as_async_dependency(self, operation_id: OptionalUUID = None):
|
118
239
|
"""Explicit async dependency injection."""
|
119
|
-
|
240
|
+
if operation_id is None:
|
241
|
+
operation_id = uuid4()
|
242
|
+
|
243
|
+
def dependency() -> AsyncGenerator[AsyncSession, None]:
|
244
|
+
return self._async_session_handler(operation_id)
|
245
|
+
|
246
|
+
return dependency
|
120
247
|
|
121
|
-
def
|
248
|
+
def as_sync_dependency(self, operation_id: OptionalUUID = None):
|
122
249
|
"""Explicit sync dependency injection."""
|
123
|
-
|
250
|
+
if operation_id is None:
|
251
|
+
operation_id = uuid4()
|
252
|
+
|
253
|
+
def dependency() -> AsyncGenerator[AsyncSession, None]:
|
254
|
+
return self._async_session_handler(operation_id)
|
255
|
+
|
256
|
+
return dependency
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: maleo-database
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.3
|
4
4
|
Summary: Database package for MaleoSuite
|
5
5
|
Author-email: Agra Bima Yuda <agra@nexmedis.com>
|
6
6
|
License: Proprietary
|
@@ -16,6 +16,7 @@ Requires-Dist: cachetools>=5.5.2
|
|
16
16
|
Requires-Dist: certifi>=2025.8.3
|
17
17
|
Requires-Dist: cffi>=1.17.1
|
18
18
|
Requires-Dist: cfgv>=3.4.0
|
19
|
+
Requires-Dist: charset-normalizer>=3.4.3
|
19
20
|
Requires-Dist: click>=8.2.1
|
20
21
|
Requires-Dist: cryptography>=45.0.6
|
21
22
|
Requires-Dist: distlib>=0.4.0
|
@@ -24,23 +25,37 @@ Requires-Dist: elastic-transport>=9.1.0
|
|
24
25
|
Requires-Dist: elasticsearch>=9.1.0
|
25
26
|
Requires-Dist: fastapi>=0.116.1
|
26
27
|
Requires-Dist: filelock>=3.19.1
|
28
|
+
Requires-Dist: google-api-core>=2.25.1
|
27
29
|
Requires-Dist: google-auth>=2.40.3
|
30
|
+
Requires-Dist: google-cloud-appengine-logging>=1.6.2
|
31
|
+
Requires-Dist: google-cloud-audit-log>=0.3.2
|
32
|
+
Requires-Dist: google-cloud-core>=2.4.3
|
33
|
+
Requires-Dist: google-cloud-logging>=3.12.1
|
34
|
+
Requires-Dist: googleapis-common-protos>=1.70.0
|
28
35
|
Requires-Dist: greenlet>=3.2.4
|
36
|
+
Requires-Dist: grpc-google-iam-v1>=0.14.2
|
37
|
+
Requires-Dist: grpcio>=1.74.0
|
38
|
+
Requires-Dist: grpcio-status>=1.74.0
|
29
39
|
Requires-Dist: identify>=2.6.13
|
30
40
|
Requires-Dist: idna>=3.10
|
41
|
+
Requires-Dist: importlib_metadata>=8.7.0
|
31
42
|
Requires-Dist: maleo-constants>=0.0.2
|
32
43
|
Requires-Dist: maleo-enums>=0.0.2
|
33
|
-
Requires-Dist: maleo-
|
44
|
+
Requires-Dist: maleo-logging>=0.0.1
|
45
|
+
Requires-Dist: maleo-mixins>=0.0.5
|
34
46
|
Requires-Dist: maleo-types-base>=0.0.2
|
35
47
|
Requires-Dist: maleo-types-enums>=0.0.2
|
36
48
|
Requires-Dist: maleo-utils>=0.0.3
|
37
49
|
Requires-Dist: motor>=3.7.1
|
38
50
|
Requires-Dist: mypy_extensions>=1.1.0
|
39
51
|
Requires-Dist: nodeenv>=1.9.1
|
52
|
+
Requires-Dist: opentelemetry-api>=1.36.0
|
40
53
|
Requires-Dist: packaging>=25.0
|
41
54
|
Requires-Dist: pathspec>=0.12.1
|
42
55
|
Requires-Dist: platformdirs>=4.4.0
|
43
56
|
Requires-Dist: pre_commit>=4.3.0
|
57
|
+
Requires-Dist: proto-plus>=1.26.1
|
58
|
+
Requires-Dist: protobuf>=6.32.0
|
44
59
|
Requires-Dist: pyasn1>=0.6.1
|
45
60
|
Requires-Dist: pyasn1_modules>=0.4.2
|
46
61
|
Requires-Dist: pycparser>=2.22
|
@@ -51,6 +66,7 @@ Requires-Dist: pymongo>=4.14.1
|
|
51
66
|
Requires-Dist: python-dateutil>=2.9.0.post0
|
52
67
|
Requires-Dist: PyYAML>=6.0.2
|
53
68
|
Requires-Dist: redis>=6.4.0
|
69
|
+
Requires-Dist: requests>=2.32.5
|
54
70
|
Requires-Dist: rsa>=4.9.1
|
55
71
|
Requires-Dist: six>=1.17.0
|
56
72
|
Requires-Dist: sniffio>=1.3.1
|
@@ -60,6 +76,7 @@ Requires-Dist: typing-inspection>=0.4.1
|
|
60
76
|
Requires-Dist: typing_extensions>=4.15.0
|
61
77
|
Requires-Dist: urllib3>=2.5.0
|
62
78
|
Requires-Dist: virtualenv>=20.34.0
|
79
|
+
Requires-Dist: zipp>=3.23.0
|
63
80
|
Dynamic: license-file
|
64
81
|
|
65
82
|
# README #
|
@@ -6,7 +6,7 @@ maleo/database/config/connection.py,sha256=lfnEE69355CNtPVlfoXaIu-IWiw4VPEkiY9tf
|
|
6
6
|
maleo/database/config/identifier.py,sha256=b1MjhoKl3h7xJe1eVIj1wjvYH9BrjHzKnjzdjaEaTeQ,626
|
7
7
|
maleo/database/config/pooling.py,sha256=tVJTxFV-fSEp4xJEVVLkTHFhhKEClSTGDQw5_pqLZgo,9246
|
8
8
|
maleo/database/managers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
|
-
maleo/database/managers/session.py,sha256=
|
9
|
+
maleo/database/managers/session.py,sha256=2kfHOziwnjyQVNAskQ2LLx2xbrCcnxb760T5yP1xXM0,10054
|
10
10
|
maleo/database/managers/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
11
|
maleo/database/managers/clients/elasticsearch.py,sha256=2US-S26N2o61iGwZZ8L0baYU7cXzJ8yFKmYFCHZ9sg0,2471
|
12
12
|
maleo/database/managers/clients/mongodb.py,sha256=h20IW1BDvZWBH-XH1U_8quoNMBDV44uyg3AAS2vIXoA,2012
|
@@ -25,8 +25,8 @@ maleo/database/orm/models/mixins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRk
|
|
25
25
|
maleo/database/orm/models/mixins/identifier.py,sha256=jICVsLUFw_f7TV_aX4KTTs_ycH35MRtRj5boTqctmZo,425
|
26
26
|
maleo/database/orm/models/mixins/status.py,sha256=Jzithn-Hl2ct1AqboGZSXrA35aaQEDvqtaB8oL7KIyo,376
|
27
27
|
maleo/database/orm/models/mixins/timestamp.py,sha256=TWqBTBvgSxcPsK0m7ggVrkHqzlqCM-2Zox7TAwCxd0I,1500
|
28
|
-
maleo_database-0.0.
|
29
|
-
maleo_database-0.0.
|
30
|
-
maleo_database-0.0.
|
31
|
-
maleo_database-0.0.
|
32
|
-
maleo_database-0.0.
|
28
|
+
maleo_database-0.0.3.dist-info/licenses/LICENSE,sha256=aftGsecnk7TWVX-7KW94FqK4Syy6YSZ8PZEF7EcIp3M,2621
|
29
|
+
maleo_database-0.0.3.dist-info/METADATA,sha256=aYpB8hcTx8HQXyBT0DKnOtZzxGCf365WAGAoqXpXYUE,3258
|
30
|
+
maleo_database-0.0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
31
|
+
maleo_database-0.0.3.dist-info/top_level.txt,sha256=3Tpd1siVsfYoeI9FEOJNYnffx_shzZ3wsPpTvz5bljc,6
|
32
|
+
maleo_database-0.0.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|