database-wrapper 0.1.44__py3-none-any.whl → 0.1.73__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.
@@ -10,48 +10,6 @@ class DBWrapper(DBWrapperMixin):
10
10
  Database wrapper class.
11
11
  """
12
12
 
13
- #######################
14
- ### Class lifecycle ###
15
- #######################
16
-
17
- def close(self) -> None:
18
- """
19
- Close resources. Usually you should not close connections here,
20
- just remove references.
21
- """
22
-
23
- raise NotImplementedError("Method not implemented")
24
-
25
- ######################
26
- ### Helper methods ###
27
- ######################
28
-
29
- @overload
30
- def createCursor(self) -> Any: ...
31
-
32
- @overload
33
- def createCursor(self, emptyDataClass: DBDataModel) -> Any: ...
34
-
35
- def createCursor(self, emptyDataClass: DBDataModel | None = None) -> Any:
36
- """
37
- Creates a new cursor object.
38
-
39
- Args:
40
- emptyDataClass (DataModelType | None, optional): The data model to use for the cursor. Defaults to None.
41
-
42
- Returns:
43
- The created cursor object.
44
- """
45
- if self.db is None and self.dbConn is None:
46
- raise ValueError(
47
- "Database object and connection is not properly initialized"
48
- )
49
-
50
- if self.dbConn is not None:
51
- return self.dbConn.cursor()
52
-
53
- return self.db.cursor
54
-
55
13
  #####################
56
14
  ### Query methods ###
57
15
  #####################
@@ -86,6 +44,8 @@ class DBWrapper(DBWrapperMixin):
86
44
  )
87
45
  for row in res:
88
46
  return row
47
+ else:
48
+ return None
89
49
 
90
50
  def getByKey(
91
51
  self,
@@ -112,6 +72,8 @@ class DBWrapper(DBWrapperMixin):
112
72
  )
113
73
  for row in res:
114
74
  return row
75
+ else:
76
+ return None
115
77
 
116
78
  def getAll(
117
79
  self,
@@ -145,7 +107,7 @@ class DBWrapper(DBWrapperMixin):
145
107
  or self.filterQuery(emptyDataClass.schemaName, emptyDataClass.tableName)
146
108
  )
147
109
  _params: tuple[Any, ...] = ()
148
- _filter = ""
110
+ _filter = None
149
111
 
150
112
  # TODO: Rewrite this so that filter method with loop is not used here
151
113
  if idKey and idValue:
@@ -158,28 +120,19 @@ class DBWrapper(DBWrapperMixin):
158
120
  # Create a SQL object for the query and format it
159
121
  querySql = self._formatFilterQuery(_query, _filter, _order, _limit)
160
122
 
161
- # Create a new cursor
162
- newCursor = self.createCursor(emptyDataClass)
163
-
164
123
  # Log
165
- self.logQuery(newCursor, querySql, _params)
166
-
167
- # Load data
168
- try:
169
- # Execute the query
170
- newCursor.execute(querySql, _params)
124
+ self.logQuery(self.dbCursor, querySql, _params)
171
125
 
172
- # Instead of fetchall(), we'll use a generator to yield results one by one
173
- while True:
174
- row = newCursor.fetchone()
175
- if row is None:
176
- break
126
+ # Execute the query
127
+ self.dbCursor.execute(querySql, _params)
177
128
 
178
- yield self.turnDataIntoModel(emptyDataClass, row)
129
+ # Instead of fetchall(), we'll use a generator to yield results one by one
130
+ while True:
131
+ row = self.dbCursor.fetchone()
132
+ if row is None:
133
+ break
179
134
 
180
- finally:
181
- # Ensure the cursor is closed after the generator is exhausted or an error occurs
182
- newCursor.close()
135
+ yield self.turnDataIntoModel(emptyDataClass.__class__, row)
183
136
 
184
137
  def getFiltered(
185
138
  self,
@@ -205,30 +158,21 @@ class DBWrapper(DBWrapperMixin):
205
158
  # Create SQL query
206
159
  querySql = self._formatFilterQuery(_query, _filter, _order, _limit)
207
160
 
208
- # Create a new cursor
209
- newCursor = self.createCursor(emptyDataClass)
210
-
211
161
  # Log
212
- self.logQuery(newCursor, querySql, _params)
213
-
214
- # Load data
215
- try:
216
- # Execute the query
217
- newCursor.execute(querySql, _params)
162
+ self.logQuery(self.dbCursor, querySql, _params)
218
163
 
219
- # Instead of fetchall(), we'll use a generator to yield results one by one
220
- while True:
221
- row = newCursor.fetchone()
222
- if row is None:
223
- break
164
+ # Execute the query
165
+ self.dbCursor.execute(querySql, _params)
224
166
 
225
- yield self.turnDataIntoModel(emptyDataClass, row)
167
+ # Instead of fetchall(), we'll use a generator to yield results one by one
168
+ while True:
169
+ row = self.dbCursor.fetchone()
170
+ if row is None:
171
+ break
226
172
 
227
- finally:
228
- # Ensure the cursor is closed after the generator is exhausted or an error occurs
229
- newCursor.close()
173
+ yield self.turnDataIntoModel(emptyDataClass.__class__, row)
230
174
 
231
- def _store(
175
+ def _insert(
232
176
  self,
233
177
  emptyDataClass: DBDataModel,
234
178
  schemaName: str | None,
@@ -254,35 +198,27 @@ class DBWrapper(DBWrapperMixin):
254
198
  returnKey = self.makeIdentifier(emptyDataClass.tableAlias, idKey)
255
199
  insertQuery = self._formatInsertQuery(tableIdentifier, storeData, returnKey)
256
200
 
257
- # Create a new cursor
258
- newCursor = self.createCursor(emptyDataClass)
259
-
260
201
  # Log
261
- self.logQuery(newCursor, insertQuery, tuple(values))
202
+ self.logQuery(self.dbCursor, insertQuery, tuple(values))
262
203
 
263
204
  # Insert
264
- try:
265
- newCursor.execute(insertQuery, tuple(values))
266
- affectedRows = newCursor.rowcount
267
- result = newCursor.fetchone()
268
-
269
- return (
270
- result.id if result and hasattr(result, "id") else 0,
271
- affectedRows,
272
- )
205
+ self.dbCursor.execute(insertQuery, tuple(values))
206
+ affectedRows = self.dbCursor.rowcount
207
+ result = self.dbCursor.fetchone()
273
208
 
274
- finally:
275
- # Close the cursor
276
- newCursor.close()
209
+ return (
210
+ result.id if result and hasattr(result, "id") else 0,
211
+ affectedRows,
212
+ )
277
213
 
278
214
  @overload
279
- def store(self, records: DataModelType) -> tuple[int, int]: # type: ignore
215
+ def insert(self, records: DataModelType) -> tuple[int, int]: # type: ignore
280
216
  ...
281
217
 
282
218
  @overload
283
- def store(self, records: list[DataModelType]) -> list[tuple[int, int]]: ...
219
+ def insert(self, records: list[DataModelType]) -> list[tuple[int, int]]: ...
284
220
 
285
- def store(
221
+ def insert(
286
222
  self,
287
223
  records: DataModelType | list[DataModelType],
288
224
  ) -> tuple[int, int] | list[tuple[int, int]]:
@@ -310,7 +246,7 @@ class DBWrapper(DBWrapperMixin):
310
246
  if not storeIdKey or not storeData:
311
247
  continue
312
248
 
313
- res = self._store(
249
+ res = self._insert(
314
250
  row,
315
251
  row.schemaName,
316
252
  row.tableName,
@@ -356,22 +292,14 @@ class DBWrapper(DBWrapperMixin):
356
292
  updateKey = self.makeIdentifier(emptyDataClass.tableAlias, idKey)
357
293
  updateQuery = self._formatUpdateQuery(tableIdentifier, updateKey, updateData)
358
294
 
359
- # Create a new cursor
360
- newCursor = self.createCursor(emptyDataClass)
361
-
362
295
  # Log
363
- self.logQuery(newCursor, updateQuery, tuple(values))
296
+ self.logQuery(self.dbCursor, updateQuery, tuple(values))
364
297
 
365
298
  # Update
366
- try:
367
- newCursor.execute(updateQuery, tuple(values))
368
- affectedRows = newCursor.rowcount
299
+ self.dbCursor.execute(updateQuery, tuple(values))
300
+ affectedRows = self.dbCursor.rowcount
369
301
 
370
- return affectedRows
371
-
372
- finally:
373
- # Close the cursor
374
- newCursor.close()
302
+ return affectedRows
375
303
 
376
304
  @overload
377
305
  def update(self, records: DataModelType) -> int: # type: ignore
@@ -470,22 +398,14 @@ class DBWrapper(DBWrapperMixin):
470
398
  deleteKey = self.makeIdentifier(emptyDataClass.tableAlias, idKey)
471
399
  delete_query = self._formatDeleteQuery(tableIdentifier, deleteKey)
472
400
 
473
- # Create a new cursor
474
- newCursor = self.createCursor(emptyDataClass)
475
-
476
401
  # Log
477
- self.logQuery(newCursor, delete_query, (idValue,))
402
+ self.logQuery(self.dbCursor, delete_query, (idValue,))
478
403
 
479
404
  # Delete
480
- try:
481
- newCursor.execute(delete_query, (idValue,))
482
- affected_rows = newCursor.rowcount
483
-
484
- return affected_rows
405
+ self.dbCursor.execute(delete_query, (idValue,))
406
+ affected_rows = self.dbCursor.rowcount
485
407
 
486
- finally:
487
- # Close the cursor
488
- newCursor.close()
408
+ return affected_rows
489
409
 
490
410
  @overload
491
411
  def delete(self, records: DataModelType) -> int: # type: ignore
@@ -9,43 +9,9 @@ class DBWrapperAsync(DBWrapperMixin):
9
9
  """
10
10
  Async Database wrapper class.
11
11
 
12
- Note: In async environment we cannot call close method from __del__ method.
13
- It means you will need to call close method manually from async context.
12
+ This class is meant to be used in async environments.
14
13
  """
15
14
 
16
- #######################
17
- ### Class lifecycle ###
18
- #######################
19
-
20
- async def close(self) -> None:
21
- """
22
- Async method for closing async resources.
23
- """
24
- raise NotImplementedError("Method not implemented")
25
-
26
- ######################
27
- ### Helper methods ###
28
- ######################
29
-
30
- @overload
31
- async def createCursor(self) -> Any: ...
32
-
33
- @overload
34
- async def createCursor(self, emptyDataClass: DBDataModel) -> Any: ...
35
-
36
- async def createCursor(self, emptyDataClass: DBDataModel | None = None) -> Any:
37
- """
38
- Creates a new cursor object.
39
-
40
- Args:
41
- emptyDataClass (T | None, optional): The data model to use for the cursor. Defaults to None.
42
-
43
- Returns:
44
- The created cursor object.
45
- """
46
- assert self.db is not None, "Database connection is not set"
47
- return self.db.cursor
48
-
49
15
  #####################
50
16
  ### Query methods ###
51
17
  #####################
@@ -76,10 +42,16 @@ class DBWrapperAsync(DBWrapperMixin):
76
42
 
77
43
  # Get the record
78
44
  res = self.getAll(
79
- emptyDataClass, idKey, idValue, limit=1, customQuery=customQuery
45
+ emptyDataClass,
46
+ idKey,
47
+ idValue,
48
+ limit=1,
49
+ customQuery=customQuery,
80
50
  )
81
51
  async for row in res:
82
52
  return row
53
+ else:
54
+ return None
83
55
 
84
56
  async def getByKey(
85
57
  self,
@@ -102,10 +74,16 @@ class DBWrapperAsync(DBWrapperMixin):
102
74
  """
103
75
  # Get the record
104
76
  res = self.getAll(
105
- emptyDataClass, idKey, idValue, limit=1, customQuery=customQuery
77
+ emptyDataClass,
78
+ idKey,
79
+ idValue,
80
+ limit=1,
81
+ customQuery=customQuery,
106
82
  )
107
83
  async for row in res:
108
84
  return row
85
+ else:
86
+ return None
109
87
 
110
88
  async def getAll(
111
89
  self,
@@ -152,28 +130,19 @@ class DBWrapperAsync(DBWrapperMixin):
152
130
  # Create a SQL object for the query and format it
153
131
  querySql = self._formatFilterQuery(_query, _filter, _order, _limit)
154
132
 
155
- # Create a new cursor
156
- newCursor = await self.createCursor(emptyDataClass)
157
-
158
133
  # Log
159
- self.logQuery(newCursor, querySql, _params)
160
-
161
- # Load data
162
- try:
163
- # Execute the query
164
- await newCursor.execute(querySql, _params)
134
+ self.logQuery(self.dbCursor, querySql, _params)
165
135
 
166
- # Instead of fetchall(), we'll use a generator to yield results one by one
167
- while True:
168
- row = await newCursor.fetchone()
169
- if row is None:
170
- break
136
+ # Execute the query
137
+ await self.dbCursor.execute(querySql, _params)
171
138
 
172
- yield self.turnDataIntoModel(emptyDataClass, row)
139
+ # Instead of fetchall(), we'll use a generator to yield results one by one
140
+ while True:
141
+ row = await self.dbCursor.fetchone()
142
+ if row is None:
143
+ break
173
144
 
174
- finally:
175
- # Ensure the cursor is closed after the generator is exhausted or an error occurs
176
- await newCursor.close()
145
+ yield self.turnDataIntoModel(emptyDataClass.__class__, row)
177
146
 
178
147
  async def getFiltered(
179
148
  self,
@@ -199,30 +168,21 @@ class DBWrapperAsync(DBWrapperMixin):
199
168
  # Create SQL query
200
169
  querySql = self._formatFilterQuery(_query, _filter, _order, _limit)
201
170
 
202
- # Create a new cursor
203
- newCursor = await self.createCursor(emptyDataClass)
204
-
205
171
  # Log
206
- self.logQuery(newCursor, querySql, _params)
207
-
208
- # Load data
209
- try:
210
- # Execute the query
211
- await newCursor.execute(querySql, _params)
172
+ self.logQuery(self.dbCursor, querySql, _params)
212
173
 
213
- # Instead of fetchall(), we'll use a generator to yield results one by one
214
- while True:
215
- row = await newCursor.fetchone()
216
- if row is None:
217
- break
174
+ # Execute the query
175
+ await self.dbCursor.execute(querySql, _params)
218
176
 
219
- yield self.turnDataIntoModel(emptyDataClass, row)
177
+ # Instead of fetchall(), we'll use a generator to yield results one by one
178
+ while True:
179
+ row = await self.dbCursor.fetchone()
180
+ if row is None:
181
+ break
220
182
 
221
- finally:
222
- # Ensure the cursor is closed after the generator is exhausted or an error occurs
223
- await newCursor.close()
183
+ yield self.turnDataIntoModel(emptyDataClass.__class__, row)
224
184
 
225
- async def _store(
185
+ async def _insert(
226
186
  self,
227
187
  emptyDataClass: DBDataModel,
228
188
  schemaName: str | None,
@@ -248,35 +208,27 @@ class DBWrapperAsync(DBWrapperMixin):
248
208
  returnKey = self.makeIdentifier(emptyDataClass.tableAlias, idKey)
249
209
  insertQuery = self._formatInsertQuery(tableIdentifier, storeData, returnKey)
250
210
 
251
- # Create a new cursor
252
- newCursor = await self.createCursor(emptyDataClass)
253
-
254
211
  # Log
255
- self.logQuery(newCursor, insertQuery, tuple(values))
212
+ self.logQuery(self.dbCursor, insertQuery, tuple(values))
256
213
 
257
214
  # Insert
258
- try:
259
- await newCursor.execute(insertQuery, tuple(values))
260
- affectedRows = newCursor.rowcount
261
- result = await newCursor.fetchone()
262
-
263
- return (
264
- result.id if result and hasattr(result, "id") else 0,
265
- affectedRows,
266
- )
215
+ await self.dbCursor.execute(insertQuery, tuple(values))
216
+ affectedRows = self.dbCursor.rowcount
217
+ result = await self.dbCursor.fetchone()
267
218
 
268
- finally:
269
- # Close the cursor
270
- await newCursor.close()
219
+ return (
220
+ result.id if result and hasattr(result, "id") else 0,
221
+ affectedRows,
222
+ )
271
223
 
272
224
  @overload
273
- async def store(self, records: DataModelType) -> tuple[int, int]: # type: ignore
225
+ async def insert(self, records: DataModelType) -> tuple[int, int]: # type: ignore
274
226
  ...
275
227
 
276
228
  @overload
277
- async def store(self, records: list[DataModelType]) -> list[tuple[int, int]]: ...
229
+ async def insert(self, records: list[DataModelType]) -> list[tuple[int, int]]: ...
278
230
 
279
- async def store(
231
+ async def insert(
280
232
  self,
281
233
  records: DataModelType | list[DataModelType],
282
234
  ) -> tuple[int, int] | list[tuple[int, int]]:
@@ -304,7 +256,7 @@ class DBWrapperAsync(DBWrapperMixin):
304
256
  if not storeIdKey or not storeData:
305
257
  continue
306
258
 
307
- res = await self._store(
259
+ res = await self._insert(
308
260
  row,
309
261
  row.schemaName,
310
262
  row.tableName,
@@ -350,22 +302,14 @@ class DBWrapperAsync(DBWrapperMixin):
350
302
  updateKey = self.makeIdentifier(emptyDataClass.tableAlias, idKey)
351
303
  updateQuery = self._formatUpdateQuery(tableIdentifier, updateKey, updateData)
352
304
 
353
- # Create a new cursor
354
- newCursor = await self.createCursor(emptyDataClass)
355
-
356
305
  # Log
357
- self.logQuery(newCursor, updateQuery, tuple(values))
306
+ self.logQuery(self.dbCursor, updateQuery, tuple(values))
358
307
 
359
308
  # Update
360
- try:
361
- await newCursor.execute(updateQuery, tuple(values))
362
- affectedRows = newCursor.rowcount
309
+ await self.dbCursor.execute(updateQuery, tuple(values))
310
+ affectedRows = self.dbCursor.rowcount
363
311
 
364
- return affectedRows
365
-
366
- finally:
367
- # Close the cursor
368
- await newCursor.close()
312
+ return affectedRows
369
313
 
370
314
  @overload
371
315
  async def update(self, records: DataModelType) -> int: # type: ignore
@@ -466,22 +410,14 @@ class DBWrapperAsync(DBWrapperMixin):
466
410
  deleteKey = self.makeIdentifier(emptyDataClass.tableAlias, idKey)
467
411
  delete_query = self._formatDeleteQuery(tableIdentifier, deleteKey)
468
412
 
469
- # Create a new cursor
470
- newCursor = await self.createCursor(emptyDataClass)
471
-
472
413
  # Log
473
- self.logQuery(newCursor, delete_query, (idValue,))
414
+ self.logQuery(self.dbCursor, delete_query, (idValue,))
474
415
 
475
416
  # Delete
476
- try:
477
- await newCursor.execute(delete_query, (idValue,))
478
- affected_rows = newCursor.rowcount
479
-
480
- return affected_rows
417
+ await self.dbCursor.execute(delete_query, (idValue,))
418
+ affected_rows = self.dbCursor.rowcount
481
419
 
482
- finally:
483
- # Close the cursor
484
- await newCursor.close()
420
+ return affected_rows
485
421
 
486
422
  @overload
487
423
  async def delete(self, records: DataModelType) -> int: # type: ignore
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
 
3
- from typing import cast, Any
3
+ from typing import Type, cast, Any
4
4
 
5
5
  from .common import OrderByItem, NoParam, DataModelType
6
6
 
@@ -10,8 +10,7 @@ class DBWrapperMixin:
10
10
  Mixin class for the DBWrapper class to provide methods that can be
11
11
  used by both sync and async versions of the DBWrapper class.
12
12
 
13
- :property db: Database backend object.
14
- :property dbConn: Database connection object.
13
+ :property dbCursor: Database cursor object.
15
14
  :property logger: Logger object
16
15
  """
17
16
 
@@ -19,16 +18,9 @@ class DBWrapperMixin:
19
18
  ### Instance properties ###
20
19
  ###########################
21
20
 
22
- # Db backend
23
- db: Any
24
- """Database backend object"""
25
-
26
- dbConn: Any
21
+ dbCursor: Any
27
22
  """
28
- Database connection object.
29
-
30
- Its not always set. Currently is used as a placeholder for async connections.
31
- For sync connections db - DatabaseBackend.connection is used.
23
+ Database cursor object.
32
24
  """
33
25
 
34
26
  # logger
@@ -42,10 +34,9 @@ class DBWrapperMixin:
42
34
  # Meta methods
43
35
  def __init__(
44
36
  self,
45
- db: Any = None,
46
- dbConn: Any = None,
37
+ dbCursor: Any = None,
47
38
  logger: logging.Logger | None = None,
48
- ):
39
+ ) -> None:
49
40
  """
50
41
  Initializes a new instance of the DBWrapper class.
51
42
 
@@ -53,8 +44,7 @@ class DBWrapperMixin:
53
44
  db (DatabaseBackend): The DatabaseBackend object.
54
45
  logger (logging.Logger, optional): The logger object. Defaults to None.
55
46
  """
56
- self.db = db
57
- self.dbConn = dbConn
47
+ self.dbCursor = dbCursor
58
48
 
59
49
  if logger is None:
60
50
  loggerName = f"{__name__}.{self.__class__.__name__}"
@@ -62,49 +52,33 @@ class DBWrapperMixin:
62
52
  else:
63
53
  self.logger = logger
64
54
 
65
- def __del__(self):
55
+ def __del__(self) -> None:
66
56
  """
67
57
  Deallocates the instance of the DBWrapper class.
68
58
  """
69
59
  self.logger.debug("Dealloc")
70
60
 
71
61
  # Force remove instances so that there are no circular references
72
- if hasattr(self, "db") and self.db:
73
- del self.db
74
-
75
- if hasattr(self, "dbConn") and self.dbConn:
76
- del self.dbConn
62
+ if hasattr(self, "dbCursor") and self.dbCursor:
63
+ del self.dbCursor
77
64
 
78
65
  ###############
79
66
  ### Setters ###
80
67
  ###############
81
68
 
82
- def setDb(self, db: Any) -> None:
83
- """
84
- Updates the database backend object.
85
-
86
- Args:
87
- db (Any): The new database backend object.
88
- """
89
- if db is None:
90
- del self.db
91
- return
92
-
93
- self.db = db
94
-
95
- def setDbConn(self, dbConn: Any) -> None:
69
+ def setDbCursor(self, dbCursor: Any) -> None:
96
70
  """
97
- Updates the database connection object.
71
+ Updates the database cursor object.
98
72
 
99
73
  Args:
100
- dbConn (Any): The new database connection object.
74
+ dbCursor (Any): The new database cursor object.
101
75
  """
102
76
 
103
- if dbConn is None:
104
- del self.dbConn
77
+ if dbCursor is None:
78
+ del self.dbCursor
105
79
  return
106
80
 
107
- self.dbConn = dbConn
81
+ self.dbCursor = dbCursor
108
82
 
109
83
  ######################
110
84
  ### Helper methods ###
@@ -139,7 +113,7 @@ class DBWrapperMixin:
139
113
 
140
114
  def turnDataIntoModel(
141
115
  self,
142
- emptyDataClass: DataModelType,
116
+ emptyDataClass: Type[DataModelType],
143
117
  dbData: dict[str, Any],
144
118
  ) -> DataModelType:
145
119
  """
@@ -155,7 +129,7 @@ class DBWrapperMixin:
155
129
  DataModelType: The data model filled with data.
156
130
  """
157
131
 
158
- result = emptyDataClass.__class__()
132
+ result = emptyDataClass()
159
133
  result.fillDataFromDict(dbData)
160
134
  result.raw_data = dbData
161
135