database-wrapper 0.1.44__tar.gz → 0.1.72__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.
- {database_wrapper-0.1.44 → database_wrapper-0.1.72}/PKG-INFO +7 -5
- {database_wrapper-0.1.44 → database_wrapper-0.1.72}/database_wrapper/__init__.py +12 -1
- database_wrapper-0.1.72/database_wrapper/abc.py +68 -0
- {database_wrapper-0.1.44 → database_wrapper-0.1.72}/database_wrapper/config.py +3 -3
- {database_wrapper-0.1.44 → database_wrapper-0.1.72}/database_wrapper/db_backend.py +131 -42
- {database_wrapper-0.1.44 → database_wrapper-0.1.72}/database_wrapper/db_data_model.py +109 -60
- {database_wrapper-0.1.44 → database_wrapper-0.1.72}/database_wrapper/db_wrapper.py +43 -123
- {database_wrapper-0.1.44 → database_wrapper-0.1.72}/database_wrapper/db_wrapper_async.py +54 -118
- {database_wrapper-0.1.44 → database_wrapper-0.1.72}/database_wrapper/db_wrapper_mixin.py +18 -44
- database_wrapper-0.1.72/database_wrapper/serialization.py +82 -0
- {database_wrapper-0.1.44 → database_wrapper-0.1.72}/database_wrapper/utils/__init__.py +0 -2
- {database_wrapper-0.1.44 → database_wrapper-0.1.72}/database_wrapper/utils/dataclass_addons.py +1 -1
- {database_wrapper-0.1.44 → database_wrapper-0.1.72}/database_wrapper.egg-info/PKG-INFO +7 -5
- {database_wrapper-0.1.44 → database_wrapper-0.1.72}/database_wrapper.egg-info/SOURCES.txt +3 -2
- {database_wrapper-0.1.44 → database_wrapper-0.1.72}/database_wrapper.egg-info/requires.txt +6 -4
- {database_wrapper-0.1.44 → database_wrapper-0.1.72}/pyproject.toml +7 -5
- database_wrapper-0.1.44/database_wrapper/utils/timer.py +0 -297
- {database_wrapper-0.1.44 → database_wrapper-0.1.72}/README.md +0 -0
- {database_wrapper-0.1.44 → database_wrapper-0.1.72}/database_wrapper/common.py +0 -0
- {database_wrapper-0.1.44 → database_wrapper-0.1.72}/database_wrapper/py.typed +0 -0
- {database_wrapper-0.1.44 → database_wrapper-0.1.72}/database_wrapper.egg-info/dependency_links.txt +0 -0
- {database_wrapper-0.1.44 → database_wrapper-0.1.72}/database_wrapper.egg-info/top_level.txt +0 -0
- {database_wrapper-0.1.44 → database_wrapper-0.1.72}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: database_wrapper
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.72
|
|
4
4
|
Summary: A Different Approach to Database Wrappers in Python
|
|
5
5
|
Author-email: Gints Murans <gm@gm.lv>
|
|
6
6
|
License: GNU General Public License v3.0 (GPL-3.0)
|
|
@@ -33,13 +33,13 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
33
33
|
Requires-Python: >=3.8
|
|
34
34
|
Description-Content-Type: text/markdown
|
|
35
35
|
Provides-Extra: pgsql
|
|
36
|
-
Requires-Dist: database_wrapper_pgsql==0.1.
|
|
36
|
+
Requires-Dist: database_wrapper_pgsql==0.1.72; extra == "pgsql"
|
|
37
37
|
Provides-Extra: mysql
|
|
38
|
-
Requires-Dist: database_wrapper_mysql==0.1.
|
|
38
|
+
Requires-Dist: database_wrapper_mysql==0.1.72; extra == "mysql"
|
|
39
39
|
Provides-Extra: mssql
|
|
40
|
-
Requires-Dist: database_wrapper_mssql==0.1.
|
|
40
|
+
Requires-Dist: database_wrapper_mssql==0.1.72; extra == "mssql"
|
|
41
41
|
Provides-Extra: sqlite
|
|
42
|
-
Requires-Dist: database_wrapper_sqlite==0.1.
|
|
42
|
+
Requires-Dist: database_wrapper_sqlite==0.1.72; extra == "sqlite"
|
|
43
43
|
Provides-Extra: all
|
|
44
44
|
Requires-Dist: database_wrapper[mssql,mysql,pgsql,sqlite]; extra == "all"
|
|
45
45
|
Provides-Extra: dev
|
|
@@ -47,8 +47,10 @@ Requires-Dist: ast-comments>=1.1.2; extra == "dev"
|
|
|
47
47
|
Requires-Dist: codespell>=2.2; extra == "dev"
|
|
48
48
|
Requires-Dist: build>=1.2.1; extra == "dev"
|
|
49
49
|
Requires-Dist: black>=24.1.0; extra == "dev"
|
|
50
|
+
Requires-Dist: mypy>=1.9.0; extra == "dev"
|
|
50
51
|
Requires-Dist: types-setuptools>=61.0.0; extra == "dev"
|
|
51
52
|
Requires-Dist: types-pymssql>=2.1.0; extra == "dev"
|
|
53
|
+
Requires-Dist: types-mysqlclient>=2.2.0; extra == "dev"
|
|
52
54
|
Requires-Dist: psycopg[binary]>=3.2.0; extra == "dev"
|
|
53
55
|
Requires-Dist: psycopg[pool]>=3.2.0; extra == "dev"
|
|
54
56
|
Requires-Dist: mysqlclient>=2.2.2; extra == "dev"
|
|
@@ -6,12 +6,15 @@ database_wrapper package - Base for database wrappers
|
|
|
6
6
|
|
|
7
7
|
import logging
|
|
8
8
|
|
|
9
|
+
from .abc import ConnectionABC, CursorABC, CursorAsyncABC, ConnectionAsyncABC
|
|
9
10
|
from . import utils
|
|
10
11
|
from .db_backend import DatabaseBackend
|
|
11
|
-
from .db_data_model import DBDataModel, DBDefaultsDataModel
|
|
12
|
+
from .db_data_model import MetadataDict, DBDataModel, DBDefaultsDataModel
|
|
12
13
|
from .common import OrderByItem, DataModelType, NoParam
|
|
13
14
|
from .db_wrapper import DBWrapper
|
|
14
15
|
from .db_wrapper_async import DBWrapperAsync
|
|
16
|
+
from .serialization import SerializeType
|
|
17
|
+
from .utils.dataclass_addons import ignore_unknown_kwargs
|
|
15
18
|
|
|
16
19
|
# Set the logger to a quiet default, can be enabled if needed
|
|
17
20
|
logger = logging.getLogger("database_wrapper")
|
|
@@ -30,8 +33,16 @@ __all__ = [
|
|
|
30
33
|
"DBWrapper",
|
|
31
34
|
"DBWrapperAsync",
|
|
32
35
|
# Helpers
|
|
36
|
+
"MetadataDict",
|
|
33
37
|
"DataModelType",
|
|
34
38
|
"OrderByItem",
|
|
35
39
|
"NoParam",
|
|
36
40
|
"utils",
|
|
41
|
+
"SerializeType",
|
|
42
|
+
"ignore_unknown_kwargs",
|
|
43
|
+
# Abstract classes
|
|
44
|
+
"ConnectionABC",
|
|
45
|
+
"CursorABC",
|
|
46
|
+
"CursorAsyncABC",
|
|
47
|
+
"ConnectionAsyncABC",
|
|
37
48
|
]
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from typing import Dict, Any, Literal
|
|
2
|
+
from typing import Any, Protocol, Sequence
|
|
3
|
+
|
|
4
|
+
from psycopg.sql import Composable
|
|
5
|
+
|
|
6
|
+
Row = Dict[str, Any]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CursorABC(Protocol):
|
|
10
|
+
def execute(
|
|
11
|
+
self,
|
|
12
|
+
operation: str | Composable,
|
|
13
|
+
parameters: Sequence[Any] | None = None,
|
|
14
|
+
) -> None: ...
|
|
15
|
+
def executemany(
|
|
16
|
+
self,
|
|
17
|
+
operation: str | Composable,
|
|
18
|
+
seq_of_parameters: Sequence[Sequence[Any]],
|
|
19
|
+
) -> None: ...
|
|
20
|
+
def fetchone(self) -> Row | None: ...
|
|
21
|
+
def fetchmany(self, size: int | None = None) -> list[Row]: ...
|
|
22
|
+
def fetchall(self) -> list[Row]: ...
|
|
23
|
+
def close(self) -> None: ...
|
|
24
|
+
def setinputsizes(self, sizes: Sequence[Any]) -> None: ...
|
|
25
|
+
def setoutputsize(self, size: Any, column: Any = None) -> None: ...
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def rowcount(self) -> int | Literal[-1]: ...
|
|
29
|
+
@property
|
|
30
|
+
def lastrowid(self) -> int | None: ...
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ConnectionABC(Protocol):
|
|
34
|
+
def cursor(self) -> CursorABC: ...
|
|
35
|
+
def commit(self) -> None: ...
|
|
36
|
+
def rollback(self) -> None: ...
|
|
37
|
+
def close(self) -> None: ...
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class CursorAsyncABC(Protocol):
|
|
41
|
+
async def execute(
|
|
42
|
+
self,
|
|
43
|
+
operation: str | Composable,
|
|
44
|
+
parameters: Sequence[Any] | None = None,
|
|
45
|
+
) -> None: ...
|
|
46
|
+
async def executemany(
|
|
47
|
+
self,
|
|
48
|
+
operation: str | Composable,
|
|
49
|
+
seq_of_parameters: Sequence[Sequence[Any]],
|
|
50
|
+
) -> None: ...
|
|
51
|
+
async def fetchone(self) -> Row | None: ...
|
|
52
|
+
async def fetchmany(self, size: int | None = None) -> list[Row]: ...
|
|
53
|
+
async def fetchall(self) -> list[Row]: ...
|
|
54
|
+
async def close(self) -> None: ...
|
|
55
|
+
def setinputsizes(self, sizes: Sequence[Any]) -> None: ...
|
|
56
|
+
def setoutputsize(self, size: Any, column: Any = None) -> None: ...
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def rowcount(self) -> int | Literal[-1]: ...
|
|
60
|
+
@property
|
|
61
|
+
def lastrowid(self) -> int | None: ...
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class ConnectionAsyncABC(Protocol):
|
|
65
|
+
async def cursor(self) -> CursorAsyncABC: ...
|
|
66
|
+
async def commit(self) -> None: ...
|
|
67
|
+
async def rollback(self) -> None: ...
|
|
68
|
+
async def close(self) -> None: ...
|
|
@@ -3,7 +3,7 @@ from typing import Any
|
|
|
3
3
|
CONFIG: dict[str, Any] = {
|
|
4
4
|
# These are supposed to be set automatically by a git pre-compile script
|
|
5
5
|
# They are one git commit hash behind, if used automatically
|
|
6
|
-
"git_commit_hash": "
|
|
7
|
-
"git_commit_date": "
|
|
8
|
-
"app_version": "0.1.
|
|
6
|
+
"git_commit_hash": "943e0234156c4a164061e5af9fb9dbd8c3041f6f",
|
|
7
|
+
"git_commit_date": "26.11.2024 22:09",
|
|
8
|
+
"app_version": "0.1.72",
|
|
9
9
|
}
|
|
@@ -5,63 +5,114 @@ from typing import Any
|
|
|
5
5
|
from threading import Event
|
|
6
6
|
from contextvars import ContextVar
|
|
7
7
|
|
|
8
|
-
from .utils.timer import Timer
|
|
9
|
-
|
|
10
8
|
|
|
11
9
|
class DatabaseBackend:
|
|
10
|
+
config: Any
|
|
11
|
+
""" Database configuration """
|
|
12
|
+
|
|
13
|
+
connectionTimeout: int
|
|
14
|
+
""" Connection timeout """
|
|
15
|
+
|
|
16
|
+
name: str
|
|
17
|
+
""" Instance name """
|
|
18
|
+
|
|
19
|
+
# TODO: This should be made to increase exponentially
|
|
20
|
+
slowDownTimeout: int
|
|
21
|
+
""" How long to wait before trying to reconnect """
|
|
22
|
+
|
|
23
|
+
pool: Any
|
|
24
|
+
""" Connection pool """
|
|
25
|
+
|
|
26
|
+
poolAsync: Any
|
|
27
|
+
""" Async connection pool """
|
|
28
|
+
|
|
12
29
|
connection: Any
|
|
30
|
+
""" Connection to database """
|
|
31
|
+
|
|
13
32
|
cursor: Any
|
|
33
|
+
""" Cursor to database """
|
|
34
|
+
|
|
14
35
|
contextConnection: ContextVar[Any | None]
|
|
15
|
-
|
|
36
|
+
""" Connection used in context manager """
|
|
16
37
|
|
|
17
|
-
|
|
38
|
+
contextConnectionAsync: ContextVar[Any | None]
|
|
39
|
+
""" Connection used in async context manager """
|
|
18
40
|
|
|
19
|
-
|
|
20
|
-
|
|
41
|
+
loggerName: str
|
|
42
|
+
""" Logger name """
|
|
21
43
|
|
|
22
|
-
name: str
|
|
23
44
|
logger: logging.Logger
|
|
24
|
-
|
|
45
|
+
""" Logger """
|
|
25
46
|
|
|
26
47
|
shutdownRequested: Event
|
|
48
|
+
"""
|
|
49
|
+
Event to signal shutdown
|
|
50
|
+
Used to stop database pool from creating new connections
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
########################
|
|
54
|
+
### Class Life Cycle ###
|
|
55
|
+
########################
|
|
27
56
|
|
|
28
57
|
def __init__(
|
|
29
58
|
self,
|
|
30
59
|
dbConfig: Any,
|
|
31
60
|
connectionTimeout: int = 5,
|
|
32
61
|
instanceName: str = "database_backend",
|
|
62
|
+
slowDownTimeout: int = 5,
|
|
33
63
|
) -> None:
|
|
34
64
|
"""
|
|
35
65
|
Main concept here is that in init we do not connect to database,
|
|
36
66
|
so that class instances can be safely made regardless of connection statuss.
|
|
37
67
|
|
|
38
|
-
Remember to call open() before using this class.
|
|
68
|
+
Remember to call open() or openPool() before using this class.
|
|
39
69
|
Close will be called automatically when class is destroyed.
|
|
40
|
-
|
|
70
|
+
|
|
71
|
+
Contexts are not implemented here, but in child classes should be used
|
|
72
|
+
by using connection pooling.
|
|
73
|
+
|
|
74
|
+
Async classes should be called manually and should override __del__ method,
|
|
75
|
+
if not upon destroying the class, an error will be raised that method was not awaited.
|
|
41
76
|
"""
|
|
42
77
|
|
|
43
78
|
self.config = dbConfig
|
|
44
79
|
self.connectionTimeout = connectionTimeout
|
|
45
80
|
self.name = instanceName
|
|
81
|
+
self.slowDownTimeout = slowDownTimeout
|
|
82
|
+
|
|
83
|
+
self.loggerName = f"{__name__}.{self.__class__.__name__}.{self.name}"
|
|
84
|
+
self.logger = logging.getLogger(self.loggerName)
|
|
46
85
|
|
|
47
|
-
|
|
48
|
-
self.
|
|
49
|
-
self.timer = ContextVar(f"db_timer", default=None)
|
|
86
|
+
self.pool = None
|
|
87
|
+
self.poolAsync = None
|
|
50
88
|
|
|
51
89
|
self.connection = None
|
|
52
90
|
self.cursor = None
|
|
53
91
|
self.shutdownRequested = Event()
|
|
54
92
|
self.contextConnection = ContextVar(f"db_connection_{self.name}", default=None)
|
|
55
|
-
self.
|
|
93
|
+
self.contextConnectionAsync = ContextVar(
|
|
56
94
|
f"db_connection_{self.name}_async", default=None
|
|
57
95
|
)
|
|
58
96
|
|
|
59
97
|
def __del__(self) -> None:
|
|
60
98
|
"""What to do when class is destroyed"""
|
|
61
99
|
self.logger.debug("Dealloc")
|
|
100
|
+
|
|
101
|
+
# Clean up connections
|
|
62
102
|
self.close()
|
|
103
|
+
self.closePool()
|
|
104
|
+
|
|
105
|
+
# Clean just in case
|
|
106
|
+
del self.connection
|
|
107
|
+
del self.cursor
|
|
108
|
+
|
|
109
|
+
del self.pool
|
|
110
|
+
del self.poolAsync
|
|
111
|
+
|
|
112
|
+
###############
|
|
113
|
+
### Context ###
|
|
114
|
+
###############
|
|
63
115
|
|
|
64
|
-
# Context
|
|
65
116
|
def __enter__(self) -> tuple[Any, Any]:
|
|
66
117
|
"""Context manager"""
|
|
67
118
|
raise Exception("Not implemented")
|
|
@@ -78,16 +129,23 @@ class DatabaseBackend:
|
|
|
78
129
|
"""Context manager"""
|
|
79
130
|
raise Exception("Not implemented")
|
|
80
131
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
132
|
+
##################
|
|
133
|
+
### Connection ###
|
|
134
|
+
##################
|
|
135
|
+
|
|
136
|
+
def openPool(self) -> Any:
|
|
137
|
+
"""Open connection pool"""
|
|
138
|
+
...
|
|
85
139
|
|
|
86
|
-
|
|
140
|
+
def closePool(self) -> Any:
|
|
141
|
+
"""Close connection pool"""
|
|
142
|
+
...
|
|
143
|
+
|
|
144
|
+
def open(self) -> Any:
|
|
87
145
|
"""Connect to database"""
|
|
88
|
-
|
|
146
|
+
...
|
|
89
147
|
|
|
90
|
-
def close(self) ->
|
|
148
|
+
def close(self) -> Any:
|
|
91
149
|
"""Close connections"""
|
|
92
150
|
if self.cursor:
|
|
93
151
|
self.logger.debug("Closing cursor")
|
|
@@ -99,7 +157,34 @@ class DatabaseBackend:
|
|
|
99
157
|
self.connection.close()
|
|
100
158
|
self.connection = None
|
|
101
159
|
|
|
102
|
-
def
|
|
160
|
+
def newConnection(self) -> Any:
|
|
161
|
+
"""
|
|
162
|
+
Create new connection
|
|
163
|
+
|
|
164
|
+
Used for async context manager and async connection creation
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
tuple[Any, Any] | None: Connection and cursor
|
|
168
|
+
"""
|
|
169
|
+
raise Exception("Not implemented")
|
|
170
|
+
|
|
171
|
+
def returnConnection(self, connection: Any) -> Any:
|
|
172
|
+
"""
|
|
173
|
+
Return connection to pool
|
|
174
|
+
|
|
175
|
+
Used for async context manager and async connections return.
|
|
176
|
+
For example to return connection to a pool.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
connection (Any): Connection to return to pool
|
|
180
|
+
"""
|
|
181
|
+
raise Exception("Not implemented")
|
|
182
|
+
|
|
183
|
+
###############
|
|
184
|
+
### Helpers ###
|
|
185
|
+
###############
|
|
186
|
+
|
|
187
|
+
def fixSocketTimeouts(self, fd: Any) -> None:
|
|
103
188
|
# Lets do some socket magic
|
|
104
189
|
s = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM)
|
|
105
190
|
# Enable sending of keep-alive messages
|
|
@@ -118,32 +203,36 @@ class DatabaseBackend:
|
|
|
118
203
|
socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, self.connectionTimeout * 1000
|
|
119
204
|
)
|
|
120
205
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
"""
|
|
125
|
-
Create new connection
|
|
206
|
+
####################
|
|
207
|
+
### Transactions ###
|
|
208
|
+
####################
|
|
126
209
|
|
|
127
|
-
|
|
210
|
+
def beginTransaction(self) -> Any:
|
|
211
|
+
"""Start transaction"""
|
|
212
|
+
raise Exception("Not implemented")
|
|
128
213
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
"""
|
|
214
|
+
def commitTransaction(self) -> Any:
|
|
215
|
+
"""Commit transaction"""
|
|
132
216
|
raise Exception("Not implemented")
|
|
133
217
|
|
|
134
|
-
|
|
135
|
-
"""
|
|
136
|
-
|
|
218
|
+
def rollbackTransaction(self) -> Any:
|
|
219
|
+
"""Rollback transaction"""
|
|
220
|
+
raise Exception("Not implemented")
|
|
137
221
|
|
|
138
|
-
|
|
139
|
-
|
|
222
|
+
# @contextmanager
|
|
223
|
+
def transaction(self, dbConn: Any = None) -> Any:
|
|
224
|
+
"""
|
|
225
|
+
Transaction context manager
|
|
140
226
|
|
|
141
|
-
|
|
142
|
-
|
|
227
|
+
! When overriding this method, remember to use context manager.
|
|
228
|
+
! Its not defined here, so that it can be used in both sync and async methods.
|
|
143
229
|
"""
|
|
144
230
|
raise Exception("Not implemented")
|
|
145
231
|
|
|
146
|
-
|
|
232
|
+
############
|
|
233
|
+
### Data ###
|
|
234
|
+
############
|
|
235
|
+
|
|
147
236
|
def lastInsertId(self) -> int:
|
|
148
237
|
"""Get last inserted row id generated by auto increment"""
|
|
149
238
|
raise Exception("Not implemented")
|
|
@@ -152,10 +241,10 @@ class DatabaseBackend:
|
|
|
152
241
|
"""Get affected rows count"""
|
|
153
242
|
raise Exception("Not implemented")
|
|
154
243
|
|
|
155
|
-
def commit(self) ->
|
|
244
|
+
def commit(self) -> Any:
|
|
156
245
|
"""Commit DB queries"""
|
|
157
246
|
raise Exception("Not implemented")
|
|
158
247
|
|
|
159
|
-
def rollback(self) ->
|
|
248
|
+
def rollback(self) -> Any:
|
|
160
249
|
"""Rollback DB queries"""
|
|
161
250
|
raise Exception("Not implemented")
|