kisa-utils 0.42.9__py3-none-any.whl → 0.42.11__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.
@@ -7,6 +7,8 @@ from kisa_utils import dates
7
7
  from kisa_utils.functionUtils import enforceRequirements
8
8
  from kisa_utils.structures.utils import Value
9
9
 
10
+ from typing import Callable
11
+
10
12
  class __PersistentQueueSingleton(type):
11
13
  _instances = {}
12
14
 
@@ -26,7 +28,6 @@ class __PersistentQueueSingleton(type):
26
28
 
27
29
  return cls._instances[idKey]
28
30
 
29
-
30
31
  class PersistentQueue(metaclass=__PersistentQueueSingleton):
31
32
  __openedQueues:dict = {} # name: PersistentQueue
32
33
 
@@ -81,7 +82,8 @@ class PersistentQueue(metaclass=__PersistentQueueSingleton):
81
82
  self.append = queueCallsInThreads(self.append, group=self.id)
82
83
  self.peek = queueCallsInThreads(self.peek, group=self.id)
83
84
  self.pop = queueCallsInThreads(self.pop, group=self.id)
84
-
85
+ self.process = queueCallsInThreads(self.process, group=self.id)
86
+
85
87
  self.__load__ = queueCallsInThreads(self.__load__, group = "pqueue__load__")
86
88
 
87
89
  if not (resp := self.__load__()):
@@ -162,6 +164,7 @@ class PersistentQueue(metaclass=__PersistentQueueSingleton):
162
164
  self.__length += 1
163
165
  return Ok(self.__length)
164
166
 
167
+ @enforceRequirements
165
168
  def peek(self, index:int, /) -> Response:
166
169
  '''
167
170
  get data at `index` without popping it from the queue
@@ -182,6 +185,7 @@ class PersistentQueue(metaclass=__PersistentQueueSingleton):
182
185
 
183
186
  return Ok(data)
184
187
 
188
+ @enforceRequirements
185
189
  def pop(self, index:int = 0, /) -> Response:
186
190
  '''
187
191
  get data at `index` and remove it from the queue
@@ -206,3 +210,93 @@ class PersistentQueue(metaclass=__PersistentQueueSingleton):
206
210
  self.__length -= 1
207
211
 
208
212
  return Ok(data)
213
+
214
+ @enforceRequirements
215
+ def process(self, index:int, handler:Callable[...,Response], /, *, popOnHandlerSuccess:bool=True, popOnHandlerError:bool=False) -> Response:
216
+ '''
217
+ process item at `index` by passing it as an argument to `handler`
218
+ Args:
219
+ index(int): the index to process
220
+ handler(Callable): a function/method that takes a single argument and returns a `Response` object
221
+ popOnHandlerSuccess(bool): a bool indicating if the item should be poppoed if `handler` returns as `Ok` object
222
+ popOnHandlerError(bool): a bool indicating if the item should be poppoed if `handler` returns as `Error` object
223
+
224
+ Returns:
225
+ `Ok` object with a KDict containing the processed item along with if or not it was popped if all goes well, `Error` object otherwise
226
+ '''
227
+
228
+ if not (resp := self.peek(index)):
229
+ return resp
230
+
231
+ item = resp.data
232
+ try:
233
+ handlerResp = handler(item)
234
+ except Exception as e:
235
+ return Error(str(e))
236
+
237
+ if not isinstance(handlerResp, Response):
238
+ return Error(f'{handler.__name__} did not return a {Response} object!')
239
+
240
+ popped:bool = False
241
+
242
+ if handlerResp and popOnHandlerSuccess:
243
+ if not (resp := self.pop(index)):
244
+ ...
245
+ popped = True
246
+
247
+ if not handlerResp and popOnHandlerError and not popped:
248
+ if not (resp := self.pop(index)):
249
+ ...
250
+ popped = True
251
+
252
+ if (popOnHandlerSuccess or popOnHandlerError) and not popped:
253
+ # what do we do in such a case?
254
+ ...
255
+
256
+ return Ok(KDict({
257
+ 'item': item,
258
+ 'popped': popped
259
+ }))
260
+
261
+ @enforceRequirements
262
+ def processAll(self, handler:Callable[...,Response], /, *, popOnHandlerSuccess:bool=True, popOnHandlerError:bool=False) -> Response:
263
+ '''
264
+ process item at `index` by passing it as an argument to `handler`
265
+ Args:
266
+ handler(Callable): a function/method that takes a single argument and returns a `Response` object
267
+ popOnHandlerSuccess(bool): a bool indicating if the item should be poppoed if `handler` returns as `Ok` object
268
+ popOnHandlerError(bool): a bool indicating if the item should be poppoed if `handler` returns as `Error` object
269
+
270
+ Returns:
271
+ `Ok` object with a KDict containing processedCount,PoppedCount,failedCount,failedLogs if all goes well, `Error` object otherwise
272
+ '''
273
+
274
+ index = 0
275
+
276
+ results = KDict({
277
+ 'processedCount': 0,
278
+ 'PoppedCount': 0,
279
+ 'failedCount': 0,
280
+ 'failedLogs': [],
281
+ })
282
+
283
+ while index < len(self):
284
+ if not (resp := self.process(index, handler, popOnHandlerSuccess=popOnHandlerSuccess, popOnHandlerError=popOnHandlerError)):
285
+ results.processedCount += 1
286
+ results.failedCount += 1
287
+ index += 1
288
+ results.failedLogs.append(KDict({
289
+ 'item': self.peek(index).data,
290
+ 'error': resp.log
291
+ }))
292
+ continue
293
+
294
+ results.processedCount += 1
295
+
296
+ popped = resp.data.popped
297
+ if popped:
298
+ results.PoppedCount += 1
299
+ else:
300
+ index += 1
301
+
302
+ return Ok(results)
@@ -3,7 +3,7 @@ this modules handle data structure validation to ensure that
3
3
  data is passed in expected formats/structures
4
4
  '''
5
5
 
6
- from typing import Any, get_args, get_origin
6
+ from typing import Any, get_args, get_origin, Callable
7
7
  from types import UnionType
8
8
  from kisa_utils.structures.utils import Value
9
9
  from kisa_utils.response import Response, Ok, Error
@@ -54,6 +54,14 @@ def validate(instance:Any, structure:Any, path:str='$') -> dict:
54
54
  result['status'] = True
55
55
  return result
56
56
  else:
57
+ if isinstance(structure, Callable):
58
+ if not callable(instance):
59
+ result['log'] = f'E07: types not similar:: {path}, expected a Callable but got {str(type(instance))[7:-1]}'
60
+ else:
61
+ result['status'] = True
62
+
63
+ return result
64
+
57
65
  # checking in both directions as dict and KDict can fail when you check
58
66
  # `isinstance(dict, KDict)` but will pass when the arguments switch positions
59
67
  if (not isinstance(instance,type(structure))) and (not isinstance(structure, type(instance))):
kisa_utils/threads.py CHANGED
@@ -10,6 +10,7 @@ from concurrent import futures
10
10
  import time
11
11
  import datetime
12
12
  import threading
13
+ import inspect
13
14
  from . import codes
14
15
  from .response import Response, Ok, Error
15
16
  from typing import Callable
@@ -43,8 +44,8 @@ class Group:
43
44
  add a function to the group
44
45
  Args:
45
46
  function(Callable): the function to be run
46
- args(tuple): arguments and key-word arguments to be passsed to `function`
47
- kwargs(dict): arguments and key-word arguments to be passsed to `function`
47
+ args(tuple): arguments to be passsed to `function`
48
+ kwargs(dict): key-word arguments to be passsed to `function`
48
49
  '''
49
50
  if not callable(function):
50
51
  return Error(f'`{function}` is not callable')
@@ -120,8 +121,8 @@ def runOnce(function:Callable, *args:tuple, **kwargs:dict) -> Response:
120
121
  run a task in a separate thread
121
122
  Args:
122
123
  function(Callable): the function to be run
123
- args(tuple): arguments and key-word arguments to be passsed to `function`
124
- kwargs(dict): arguments and key-word arguments to be passsed to `function`
124
+ args(tuple): arguments to be passsed to `function`
125
+ kwargs(dict): key-word arguments to be passsed to `function`
125
126
  '''
126
127
  __initExecutor()
127
128
 
@@ -129,7 +130,7 @@ def runOnce(function:Callable, *args:tuple, **kwargs:dict) -> Response:
129
130
  return Error(f'`{function}` is not callable')
130
131
 
131
132
  try:
132
- return Ok(__executor.run(function, *args, **kwargs))
133
+ return Ok(__executor.run(function, *args, **kwargs).result())
133
134
  except Exception as e:
134
135
  return Error(f'failed to run task: {e}')
135
136
 
@@ -139,7 +140,11 @@ def __loop(function:Callable, duration:float, *args:tuple, **kwargs:dict) -> Non
139
140
  '''
140
141
  while 1:
141
142
  time.sleep(duration)
142
- function(*args, **kwargs)
143
+ try:
144
+ function(*args, **kwargs)
145
+ except Exception as e:
146
+ print(f'`[run-every ERR] `{inspect.getsourcefile(function)}::{function.__name__}` failed with exception: e`')
147
+
143
148
 
144
149
  def runEvery(function:Callable, duration:float, *args:tuple, **kwargs:dict) -> dict[str,str|bool]:
145
150
  '''
@@ -147,8 +152,8 @@ def runEvery(function:Callable, duration:float, *args:tuple, **kwargs:dict) -> d
147
152
  Args:
148
153
  function(Callable): the function to call every `duration`
149
154
  duration(float): the time(in SECONDS) after which to run `function` periodically
150
- args(tuple): arguments and key-word arguments to be passsed to `function`
151
- kwargs(dict): arguments and key-word arguments to be passsed to `function`
155
+ args(tuple): arguments to be passsed to `function`
156
+ kwargs(dict): key-word arguments to be passsed to `function`
152
157
 
153
158
  Returns:
154
159
  dict in form
@@ -178,6 +183,8 @@ def runEvery(function:Callable, duration:float, *args:tuple, **kwargs:dict) -> d
178
183
  reply['log'] = f'failed to run task: {e}'
179
184
  return reply
180
185
 
186
+ # future.result()
187
+
181
188
  reply['status'] = True
182
189
  return reply
183
190
 
@@ -213,9 +220,10 @@ def __loopRoutineThreads():
213
220
 
214
221
  try:
215
222
  future = __executor.run(data['function'],*data['args'], **data['kwargs'])
223
+ # future.result()
216
224
  currentMinuteData['functionsAlreadyRun'][functionName] = None
217
225
  except Exception as e:
218
- print(f'[routine-loop]: {e}')
226
+ print(f'[routine-loop ERR]: {e}')
219
227
  pass
220
228
 
221
229
  def runAt(function:Callable, timesOfDay:list[tuple[int,int]], weekDays:list[int], *args:tuple, tzHoursFromUTC:float = +3.00, **kwargs:dict) -> Response:
@@ -227,8 +235,8 @@ def runAt(function:Callable, timesOfDay:list[tuple[int,int]], weekDays:list[int]
227
235
  weekDays(list[int]): a list of `ints` each ranging `0-6, inclusive` indicating a day of the week where `0=sunday` and `6=saturday`
228
236
  tzHoursFromUTC(float): hours to add/subtract from UTC. this allows for `timesOfDay` to be seen in a particular timezone.
229
237
  the default value is `+3.00` ie `EAT` so times in `timesOfDay` are in EAT
230
- args(tuple): arguments and key-word arguments to be passsed to `function`
231
- kwargs(dict): arguments and key-word arguments to be passsed to `function`
238
+ args(tuple): arguments to be passsed to `function`
239
+ kwargs(dict): key-word arguments to be passsed to `function`
232
240
 
233
241
  '''
234
242
 
@@ -282,7 +290,8 @@ def __initExecutor():
282
290
  if __executor: return
283
291
 
284
292
  __executor = __Executor()
285
- __executor.run(__loopRoutineThreads)
293
+ future = __executor.run(__loopRoutineThreads)
294
+ # future.result()
286
295
 
287
296
  # futures.as_completed([__executor.run(__loopRoutineThreads)])
288
297
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kisa-utils
3
- Version: 0.42.9
3
+ Version: 0.42.11
4
4
  Summary: Utility functions and modules for KISA Developers
5
5
  Author: Tom Bukenya
6
6
  Author-email: glayn2bukman@gmail.com
@@ -13,12 +13,12 @@ kisa_utils/remote.py,sha256=0RDrfC4RUW4m6JLziC0_EXJYqzWp38Rw8NDroJ0MuqI,2149
13
13
  kisa_utils/response.py,sha256=asETUBkeF5OlSTwa-coa7lZDCKmQlHCmHf6eaZFl8CU,4560
14
14
  kisa_utils/standardize.py,sha256=nt-uzHQFoKxGscD_MpDYXw65Teg3724whAqa6Kh_zhE,2231
15
15
  kisa_utils/storage.py,sha256=6NdEVrHMS7WB_vmCwiGigIinu-EjxalFJhk1kj-_vWs,5990
16
- kisa_utils/threads.py,sha256=qQqsf64YHMyLpboq5AEXKxYqf3iXUhxiJe6Ymg-vlxI,12840
16
+ kisa_utils/threads.py,sha256=U1hVCmDoCrWsi8fOJptewJ0Q0hXtGn8UZVT52pbC36E,12979
17
17
  kisa_utils/token.py,sha256=Y2qglWYWpmHxoXBh-TH0r1as0uPV5LLqMNcunLvM4vM,7850
18
18
  kisa_utils/permissions/__config__.py,sha256=i3ELkOydDnjKx2ozQTxLZdZ8DXSeUncnl2kRxANjFmM,613
19
19
  kisa_utils/permissions/__init__.py,sha256=iAsGEf5Ktw3gPJ5ZKL8BnuqX8e_S4QgsCVgfaRYi4Qg,48068
20
20
  kisa_utils/queues/__init__.py,sha256=VvhceyN5qeiMel1JFQwLRuVk48oBXaWvDtriCubDOms,48
21
- kisa_utils/queues/persistent.py,sha256=KueLVqkVcTqvVe5yt9DEM_PvtUEannukixRS8icONTI,6872
21
+ kisa_utils/queues/persistent.py,sha256=b731r1NJkOxlJdkYoxDx2Ojf8loTCY87sd-1_xWn_L4,10431
22
22
  kisa_utils/queues/callables/__init__.py,sha256=OJL3AQnaAS1Eek4H6WBH3WefA2wf-x03cwFmRSK8hoU,141
23
23
  kisa_utils/queues/callables/enqueueFunctionCalls.py,sha256=VIliaMvw4MUdOqts0dXdZCYNxs-QrOVjIRAR3scGrRM,11786
24
24
  kisa_utils/queues/callables/executorQueues.py,sha256=x6bAqxBSZRZ_kL8CK1lSN6JYAYFLxzM84LC1RmwaOLw,6626
@@ -26,8 +26,8 @@ kisa_utils/servers/__init__.py,sha256=lPqDyGTrFo0qwPZ2WA9Xtcpc5D8AIU4huqgFx1iZf6
26
26
  kisa_utils/servers/flask.py,sha256=XZYY1pWnP1mSvaS5Uv8G3EFJV5BJBQtU2gDbO8suvLc,40422
27
27
  kisa_utils/structures/__init__.py,sha256=JBU1j3A42jQ62ALKnsS1Hav9YXcYwjDw1wQJtohXPbU,83
28
28
  kisa_utils/structures/utils.py,sha256=665rXIapGwFqejizeJwy3DryeskCQOdgP25BCdLkGvk,2898
29
- kisa_utils/structures/validator.py,sha256=JhD9jcfbjTwBr_7OfuNaJd_cYr7wR2emFhsCEo5MCHQ,4323
30
- kisa_utils-0.42.9.dist-info/METADATA,sha256=ggmPfO1oysfdUkAExab5IOmqtA6pgvrD5O_orx1WzaU,477
31
- kisa_utils-0.42.9.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
32
- kisa_utils-0.42.9.dist-info/top_level.txt,sha256=GFOLXZYqpBG9xtscGa2uGJAEiZ5NwsqHBH9NylnB29M,11
33
- kisa_utils-0.42.9.dist-info/RECORD,,
29
+ kisa_utils/structures/validator.py,sha256=oCSgY_itst6bZdC5g8yVU4-lSH2xnUsOx3X-oPyV7nA,4626
30
+ kisa_utils-0.42.11.dist-info/METADATA,sha256=RRVFT0IudGdenO9WQWlCG9AXy2RBec1YTC7pB-VhAlw,478
31
+ kisa_utils-0.42.11.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
32
+ kisa_utils-0.42.11.dist-info/top_level.txt,sha256=GFOLXZYqpBG9xtscGa2uGJAEiZ5NwsqHBH9NylnB29M,11
33
+ kisa_utils-0.42.11.dist-info/RECORD,,