kisa-utils 0.37.12__py3-none-any.whl → 0.37.13__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.
kisa_utils/db.py CHANGED
@@ -6,6 +6,7 @@ from typing import Generator, Any, Callable
6
6
  import inspect
7
7
  import traceback
8
8
  import re
9
+ from threading import get_native_id as getCurrentThreadId, Lock
9
10
 
10
11
  from . import storage
11
12
  from . import codes
@@ -52,6 +53,8 @@ TRIGGERS:dict = {
52
53
 
53
54
  JSON_SEPARATOR = '::'
54
55
 
56
+ RAM_DB_PATHS:list[str] = [':memory:', ':ram:',':RAM:']
57
+
55
58
  # *********************************************************************
56
59
  def _getNumberOfArgsAndKwargs(function:Callable) -> tuple[int]:
57
60
  '''
@@ -69,6 +72,39 @@ def _getNumberOfArgsAndKwargs(function:Callable) -> tuple[int]:
69
72
  return nArgs, nKwargs
70
73
 
71
74
  class Api:
75
+ __lock = Lock()
76
+ __openDatabases:dict = {
77
+ # path:str -> {
78
+ # threadId:int -> instance:Api,
79
+ # ...
80
+ # }
81
+ }
82
+
83
+ # make this class a singleton
84
+ def __new__(cls, *args, **kwargs) -> 'Api':
85
+ path = ''
86
+ if args: path=args[0]
87
+ else: path = kwargs.get(path, RAM_DB_PATHS[0])
88
+
89
+ if path not in RAM_DB_PATHS:
90
+ threadId = getCurrentThreadId()
91
+ with Api.__lock:
92
+ if path not in Api.__openDatabases:
93
+ Api.__openDatabases[path] = {}
94
+
95
+ if threadId in Api.__openDatabases[path]:
96
+ instance = Api.__openDatabases[path][threadId]
97
+ # print(f'using existing db handle: {threadId} {path}')
98
+ else:
99
+ instance = super().__new__(cls)
100
+ # print(f'new db handle: {threadId} {path}')
101
+ Api.__openDatabases[path][threadId] = instance
102
+ else:
103
+ instance = super().__new__(cls)
104
+
105
+ return instance
106
+
107
+
72
108
  def __checkContext(method):
73
109
  '''
74
110
  this decorator will be used to enforce the calling of Api class instances
@@ -162,6 +198,9 @@ class Api:
162
198
  useWALMode(bool): if set to `True`, WAL mode will be used for the underlying sqlite3 database
163
199
  returnKISAResponse(bool|None): if not set, `kisa_utils.db.RETURN_KISA_RESPONSES` will be used. if set to `True`, all public methods that dont return `None` will return KISA Response objects
164
200
  '''
201
+ if getattr(self, "_dbInitializedInThread", False):
202
+ return
203
+ self._dbInitializedInThread = True
165
204
 
166
205
  self._in_with_statement = False
167
206
  self.isOpen = False
@@ -172,7 +211,9 @@ class Api:
172
211
  self.__withContextCount = 0
173
212
  self.__returnKISAResponse = RETURN_KISA_RESPONSES if None==returnKISAResponse else returnKISAResponse
174
213
 
175
- if path not in [':memory:', ':ram:',':RAM:']:
214
+ self._path = path
215
+
216
+ if path not in RAM_DB_PATHS:
176
217
  while path.endswith('/'): path = path[:-1]
177
218
  if not path.endswith(f'.{__EXT__}') and not os.path.isfile(path):
178
219
  path += f'.{__EXT__}'
@@ -700,13 +741,19 @@ class Api:
700
741
  + 1d = insert single row
701
742
  + 2d = insert multple row
702
743
  '''
703
- self.cursor.execute(f'select * from {table}')
704
- column_value_placeholders = ['?' for description in self.cursor.description]
744
+ reply = {'status':False, 'log':'', 'affectedRows':0}
745
+
746
+ try:
747
+ self.cursor.execute(f'select * from {table}')
748
+ column_value_placeholders = ['?' for description in self.cursor.description]
749
+ except Exception as e:
750
+ if self.__transactionMode: self.__transactionData['insert']['failed'] += 1
751
+ reply['log'] = str(e)
752
+ return reply if not self.__returnKISAResponse else Error(reply['log'])
705
753
 
706
754
  if not isinstance(data, (list, tuple)):
707
755
  data = [data]
708
756
 
709
- reply = {'status':False, 'log':'', 'affectedRows':0}
710
757
  try:
711
758
  data = self.__encodeIncomingData(data)
712
759
  if isinstance(data[0],(list,tuple)):
@@ -979,10 +1026,15 @@ class Api:
979
1026
  return reply if not self.__returnKISAResponse else Ok()
980
1027
 
981
1028
  # @__checkContext
982
- def close(self) -> None:
1029
+ def close(self) -> bool:
983
1030
  '''
984
1031
  close the sqlite connection
985
1032
  '''
1033
+
1034
+ if self.__withContextCount > 0:
1035
+ return False
1036
+
1037
+ # print('>>>closed DB<<<<', self._path)
986
1038
  if self.isOpen:
987
1039
  # self.db.execute('pragma optimize;')
988
1040
  if self.__transactionMode:
@@ -995,12 +1047,24 @@ class Api:
995
1047
  # self.db, self.cursor = None,None
996
1048
  self.isOpen = False
997
1049
 
1050
+ return True
1051
+
998
1052
  # @__checkContext
999
1053
  def release(self) -> None:
1000
1054
  '''
1001
1055
  close the sqlite3 connection
1002
1056
  '''
1003
- return self.close()
1057
+
1058
+ path = self._path
1059
+
1060
+ if not self.close():
1061
+ return
1062
+
1063
+ if path in RAM_DB_PATHS: return
1064
+
1065
+ threadId = getCurrentThreadId()
1066
+ with Api.__lock:
1067
+ del Api.__openDatabases[path][threadId]
1004
1068
 
1005
1069
  # methods to add context to the handle
1006
1070
  def __enter__(self) -> 'Api':
@@ -1059,7 +1123,7 @@ def transaction(*dbArgs, **dbKwargs) -> dict|Response:
1059
1123
 
1060
1124
  '''
1061
1125
 
1062
- returnKISAResponse = dbKwargs.get('returnKISAResponse',False)
1126
+ returnKISAResponse = dbKwargs.get('returnKISAResponse',RETURN_KISA_RESPONSES)
1063
1127
 
1064
1128
  def handler(func) -> dict:
1065
1129
  def wrapper(*args, **kwargs) -> dict:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kisa-utils
3
- Version: 0.37.12
3
+ Version: 0.37.13
4
4
  Summary: Utility functions and modules for KISA Developers
5
5
  Author: Tom Bukenya
6
6
  Author-email: glayn2bukman@gmail.com
@@ -3,7 +3,7 @@ kisa_utils/cache.py,sha256=4Ue5G3QhHSQAmIfQKYgWKWjNL4rA4wLLd_RdBLb2ABY,7345
3
3
  kisa_utils/codes.py,sha256=PV_S53Skggf4XetOdYoIKtEmM8cpN5wZwUlxje70WZY,904
4
4
  kisa_utils/config.py,sha256=NfluzGKTh66qfNtC-Ae0zNb1XzMTgU2Me9Vi82R9c1E,2285
5
5
  kisa_utils/dates.py,sha256=kcNqoY_iguG9hSzABMIBqeL3MGz3n4MRIXuW4OxRHLs,12662
6
- kisa_utils/db.py,sha256=RtO3vh6K6HYYvJcz4fbe1pYT--9j_n1Eqg7z0Zr3H3E,44461
6
+ kisa_utils/db.py,sha256=8WwJs2a1h2hdPc2U65effJ5MYLVadaeGNLR2iPqd6FA,46391
7
7
  kisa_utils/encryption.py,sha256=nFzNpzWV_D9uSEq4FsgCnlS7FQtqWP9fvM_81rsfcLo,4218
8
8
  kisa_utils/enqueue.py,sha256=VIliaMvw4MUdOqts0dXdZCYNxs-QrOVjIRAR3scGrRM,11786
9
9
  kisa_utils/figures.py,sha256=pYIpQzu1OXRSsY1d98GhgPifnIRmgl-r7S32ai-Ms0c,3731
@@ -23,7 +23,7 @@ kisa_utils/servers/flask.py,sha256=o76cJKlQ3L8EOVdHUF092qwoAZMzgttuLt0mMhtCsGI,4
23
23
  kisa_utils/structures/__init__.py,sha256=JBU1j3A42jQ62ALKnsS1Hav9YXcYwjDw1wQJtohXPbU,83
24
24
  kisa_utils/structures/utils.py,sha256=l56NQiPVcFijpuLqt2n9ZwnVKT4XzK6oknRVallRzwQ,2573
25
25
  kisa_utils/structures/validator.py,sha256=Y4UmB4TH7N-GkK22EV1WOsPWjTeqxVWLTentl1keZD4,4053
26
- kisa_utils-0.37.12.dist-info/METADATA,sha256=dJyypLrTFm2KcUP4HLzISVtQ0EwUoDc1OxwHmY1HXNU,478
27
- kisa_utils-0.37.12.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
28
- kisa_utils-0.37.12.dist-info/top_level.txt,sha256=URxY4sRuqmirOxWtztpVmPoGQdksEMYO6hmYsEDGz2Y,75
29
- kisa_utils-0.37.12.dist-info/RECORD,,
26
+ kisa_utils-0.37.13.dist-info/METADATA,sha256=CVP00b5I9CF2mKMRXXv0XaGaHPqp741ggky_7256efo,478
27
+ kisa_utils-0.37.13.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
28
+ kisa_utils-0.37.13.dist-info/top_level.txt,sha256=URxY4sRuqmirOxWtztpVmPoGQdksEMYO6hmYsEDGz2Y,75
29
+ kisa_utils-0.37.13.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.42.0)
2
+ Generator: bdist_wheel (0.41.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5