kisa-utils 0.41.0__py3-none-any.whl → 0.42.1__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 +125 -17
- kisa_utils/queues/__init__.py +2 -0
- kisa_utils/queues/persistent.py +206 -0
- kisa_utils/storage.py +9 -1
- {kisa_utils-0.41.0.dist-info → kisa_utils-0.42.1.dist-info}/METADATA +1 -1
- {kisa_utils-0.41.0.dist-info → kisa_utils-0.42.1.dist-info}/RECORD +8 -7
- {kisa_utils-0.41.0.dist-info → kisa_utils-0.42.1.dist-info}/WHEEL +0 -0
- {kisa_utils-0.41.0.dist-info → kisa_utils-0.42.1.dist-info}/top_level.txt +0 -0
kisa_utils/db.py
CHANGED
|
@@ -20,7 +20,8 @@ if sqlite3.sqlite_version_info[1]<38:
|
|
|
20
20
|
sys.exit(f'we need sqlite3 v3.38.0+ to run this program. current version::{sqlite3.sqlite_version}')
|
|
21
21
|
|
|
22
22
|
MAX_FETCH_ITEMS = 16*1024
|
|
23
|
-
RETURN_KISA_RESPONSES = False # ensure all Handles return KISA-Responses globally
|
|
23
|
+
RETURN_KISA_RESPONSES:bool = False # ensure all Handles return KISA-Responses globally
|
|
24
|
+
PARSE_DICTS_TO_KDICTS:bool = True # ensure all dicts parsed on return data are KDicts
|
|
24
25
|
|
|
25
26
|
__EXT__ = 'sqlite3'
|
|
26
27
|
|
|
@@ -56,6 +57,7 @@ TRIGGERS:dict = {
|
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
JSON_SEPARATOR = '::'
|
|
60
|
+
JSON_ARRAY_APPEND_SYMBOL = '+' # arr[+]=N => arr.append(N)
|
|
59
61
|
|
|
60
62
|
RAM_DB_PATHS:list[str] = [':memory:', ':ram:',':RAM:']
|
|
61
63
|
|
|
@@ -508,24 +510,116 @@ class Api:
|
|
|
508
510
|
|
|
509
511
|
return tables
|
|
510
512
|
|
|
511
|
-
def
|
|
513
|
+
def __getJSONListIndices(self, jsonPathSection:str) -> Response:
|
|
514
|
+
'''
|
|
515
|
+
get key and indices of a json path
|
|
516
|
+
Returns:
|
|
517
|
+
Response.data ->
|
|
518
|
+
```
|
|
519
|
+
{
|
|
520
|
+
'key': str, # if array is a key in a dict, then this is set, otherwise its an empty string
|
|
521
|
+
'indices': list[int] # contains all indices
|
|
522
|
+
}
|
|
523
|
+
```
|
|
524
|
+
'''
|
|
525
|
+
data = KDict({
|
|
526
|
+
'key': '',
|
|
527
|
+
'indices': [],
|
|
528
|
+
})
|
|
529
|
+
|
|
530
|
+
if '[' not in jsonPathSection:
|
|
531
|
+
return Ok(data)
|
|
532
|
+
|
|
533
|
+
if jsonPathSection.count('[') != jsonPathSection.count(']') or jsonPathSection.index('[') > jsonPathSection.index(']'):
|
|
534
|
+
return Error(f'malformed array indexing found: `{jsonPathSection}`')
|
|
535
|
+
|
|
536
|
+
targetIndex = jsonPathSection.index('[')
|
|
537
|
+
data.key = jsonPathSection[:targetIndex].strip()
|
|
538
|
+
|
|
539
|
+
section = jsonPathSection[targetIndex:]
|
|
540
|
+
while '[' in section:
|
|
541
|
+
if section.count('[') != section.count(']') or section.index('[') > section.index(']'):
|
|
542
|
+
return Error(f'malformed array indexing found: `{jsonPathSection}`')
|
|
543
|
+
|
|
544
|
+
startIndex, endIndex = section.index('['), section.index(']')
|
|
545
|
+
|
|
546
|
+
try:
|
|
547
|
+
index = section[startIndex+1:endIndex]
|
|
548
|
+
index = int(index)
|
|
549
|
+
except:
|
|
550
|
+
index = index.strip()
|
|
551
|
+
if index != JSON_ARRAY_APPEND_SYMBOL:
|
|
552
|
+
return Error(f'malformed array index found: `{jsonPathSection}`->`{section[startIndex+1:endIndex]}`')
|
|
553
|
+
|
|
554
|
+
if JSON_ARRAY_APPEND_SYMBOL in data.indices:
|
|
555
|
+
return Error(f'index `{JSON_ARRAY_APPEND_SYMBOL}` should be the last index: `{jsonPathSection}`->`{section[startIndex+1:endIndex]}`')
|
|
556
|
+
|
|
557
|
+
data.indices.append(index)
|
|
558
|
+
|
|
559
|
+
section = section[endIndex+1:]
|
|
560
|
+
|
|
561
|
+
return Ok(data)
|
|
562
|
+
|
|
563
|
+
def __formRootPathFields(self, root:dict|list, keys:list, value:Any) -> Response:
|
|
512
564
|
for key in keys[:-1]:
|
|
565
|
+
if not (resp := self.__getJSONListIndices(key)):
|
|
566
|
+
return resp
|
|
513
567
|
|
|
514
|
-
if
|
|
515
|
-
key = key[:-4]
|
|
516
|
-
root = root.setdefault(key, [])
|
|
517
|
-
else:
|
|
568
|
+
if not resp.data.indices: # dict object
|
|
518
569
|
root = root.setdefault(key, {})
|
|
570
|
+
else: # list object
|
|
571
|
+
if resp.data.key and isinstance(root, dict):
|
|
572
|
+
root = root.setdefault(resp.data.key, [])
|
|
573
|
+
|
|
574
|
+
if not isinstance(root, list):
|
|
575
|
+
return Error(f'indexing non-array: `{key}`')
|
|
576
|
+
|
|
577
|
+
while resp.data.indices:
|
|
578
|
+
_index = index = resp.data.indices.pop(0)
|
|
579
|
+
if _index == JSON_ARRAY_APPEND_SYMBOL:
|
|
580
|
+
return Error(f'append index `{_index}` should be at the end: `{key}`, index `{_index}({index})`')
|
|
581
|
+
if index < 0:
|
|
582
|
+
index = len(root) + index
|
|
583
|
+
|
|
584
|
+
if not (0<= index < len(root)):
|
|
585
|
+
return Error(f'array-index out of bounds: `{key}`, index `{_index}({index})`')
|
|
519
586
|
|
|
520
|
-
|
|
587
|
+
root = root[index]
|
|
521
588
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
root[
|
|
589
|
+
key = keys[-1]
|
|
590
|
+
|
|
591
|
+
if not (resp := self.__getJSONListIndices(key)):
|
|
592
|
+
return resp
|
|
593
|
+
|
|
594
|
+
if not resp.data.indices: # dict object
|
|
595
|
+
root[key] = value
|
|
596
|
+
else: # list object
|
|
597
|
+
if resp.data.key and isinstance(root, dict):
|
|
598
|
+
root = root.setdefault(resp.data.key, [])
|
|
599
|
+
|
|
600
|
+
if not isinstance(root, list):
|
|
601
|
+
return Error(f'indexing non-array: `{key}`')
|
|
602
|
+
|
|
603
|
+
while len(resp.data.indices):
|
|
604
|
+
_index = index = resp.data.indices.pop(0)
|
|
605
|
+
if _index != JSON_ARRAY_APPEND_SYMBOL:
|
|
606
|
+
if index < 0:
|
|
607
|
+
index = len(root) + index
|
|
608
|
+
|
|
609
|
+
if not (0<= index < len(root)):
|
|
610
|
+
return Error(f'array-index out of bounds: `{key}`, index `{_index}({index})`')
|
|
611
|
+
elif resp.data.indices:
|
|
612
|
+
return Error(f'append index `{_index}` should be at the end: `{key}`, index `{_index}({index})`')
|
|
613
|
+
|
|
614
|
+
if resp.data.indices:
|
|
615
|
+
root = root[index]
|
|
616
|
+
else:
|
|
617
|
+
if _index != JSON_ARRAY_APPEND_SYMBOL:
|
|
618
|
+
root[index] = value
|
|
619
|
+
else:
|
|
620
|
+
root.append(value)
|
|
621
|
+
|
|
622
|
+
return Ok()
|
|
529
623
|
|
|
530
624
|
def __getRootAndPath(self, string, separator:str=JSON_SEPARATOR):
|
|
531
625
|
index = string.index(separator)
|
|
@@ -617,7 +711,8 @@ class Api:
|
|
|
617
711
|
limit:int=MAX_FETCH_ITEMS, returnDicts:bool=False,
|
|
618
712
|
returnNamespaces:bool=False, parseJson:bool=False,
|
|
619
713
|
returnGenerator:bool=False,
|
|
620
|
-
useKDicts:bool=
|
|
714
|
+
useKDicts:bool|None=None,
|
|
715
|
+
offset:int = 0,
|
|
621
716
|
) -> list|Response:
|
|
622
717
|
'''
|
|
623
718
|
attempt to fetch from the database
|
|
@@ -634,6 +729,7 @@ class Api:
|
|
|
634
729
|
parseJson(bool):if `True`, we shall parse json objects to python lists and dictionaries where possible
|
|
635
730
|
returnGenerator(bool): if True, a generator will be returned instead of the list of tuple|dict|SimpleNamespace. this is especially recommended for large data
|
|
636
731
|
useKDicts(bool): if True, all dicts returned are `KDicts` ie dicts that support the dot notation, just like JS objects
|
|
732
|
+
offset(int): how many rows to skip. along with `limit`, this allows pagination of results
|
|
637
733
|
'''
|
|
638
734
|
if not (limit>0 and limit<=MAX_FETCH_ITEMS):
|
|
639
735
|
err = f'please set a limit on the returned rows. maximum should be {MAX_FETCH_ITEMS}'
|
|
@@ -641,6 +737,14 @@ class Api:
|
|
|
641
737
|
raise ValueError(err)
|
|
642
738
|
return Error(err)
|
|
643
739
|
|
|
740
|
+
if not isinstance(offset, int) or offset < 0:
|
|
741
|
+
err = f'invalid offset given. expected int >= 0'
|
|
742
|
+
if not self.__returnKISAResponse:
|
|
743
|
+
raise ValueError(err)
|
|
744
|
+
return Error(err)
|
|
745
|
+
|
|
746
|
+
useKDicts = PARSE_DICTS_TO_KDICTS if None==useKDicts else useKDicts
|
|
747
|
+
|
|
644
748
|
condition = condition.strip() or '1'
|
|
645
749
|
if not len(condition.strip()):
|
|
646
750
|
err = 'no condition provided'
|
|
@@ -660,7 +764,7 @@ class Api:
|
|
|
660
764
|
# columns = [self.__formatDBJson(_) for _ in columns]
|
|
661
765
|
columns = [self.__formatJSONCondition(_) for _ in columns]
|
|
662
766
|
|
|
663
|
-
__SQL_stmt = f"select {','.join(columns)} from {table} where {condition} limit {limit}"
|
|
767
|
+
__SQL_stmt = f"select {','.join(columns)} from {table} where {condition} limit {limit} offset {offset}"
|
|
664
768
|
# print(f'<{__SQL_stmt}>')
|
|
665
769
|
|
|
666
770
|
if not self.__returnKISAResponse:
|
|
@@ -857,6 +961,7 @@ class Api:
|
|
|
857
961
|
|
|
858
962
|
rootResult = rootResult[0]
|
|
859
963
|
rootResult = storage.decodeJSON(rootResult)
|
|
964
|
+
# print('>>> ',rootResult,'<<<')
|
|
860
965
|
|
|
861
966
|
if isinstance(rootResult, list):
|
|
862
967
|
if self.__transactionMode: self.__transactionData['update']['failed'] += 1
|
|
@@ -868,7 +973,9 @@ class Api:
|
|
|
868
973
|
|
|
869
974
|
json_roots[root] = rootResult
|
|
870
975
|
|
|
871
|
-
self.__formRootPathFields(json_roots[root], path.split(JSON_SEPARATOR), columnData[index])
|
|
976
|
+
if not (resp := self.__formRootPathFields(json_roots[root], path.split(JSON_SEPARATOR), columnData[index])):
|
|
977
|
+
reply['log'] = resp.log
|
|
978
|
+
return reply if not self.__returnKISAResponse else resp
|
|
872
979
|
|
|
873
980
|
_columns.append(f'{root}=?')
|
|
874
981
|
|
|
@@ -876,6 +983,7 @@ class Api:
|
|
|
876
983
|
columns = _columns
|
|
877
984
|
values += conditionData
|
|
878
985
|
|
|
986
|
+
|
|
879
987
|
if not condition:
|
|
880
988
|
if self.__transactionMode: self.__transactionData['update']['failed'] += 1
|
|
881
989
|
reply['log'] = 'please provide an update condition. use `1` if you want all data updated'
|
kisa_utils/queues/__init__.py
CHANGED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
from kisa_utils.db import Handle
|
|
2
|
+
from kisa_utils import db
|
|
3
|
+
from kisa_utils.queues.callables import queueCallsInThreads
|
|
4
|
+
from kisa_utils.response import Response, Error, Ok
|
|
5
|
+
from kisa_utils.dataStructures import KDict
|
|
6
|
+
from kisa_utils.storage import Path
|
|
7
|
+
from kisa_utils import dates
|
|
8
|
+
from kisa_utils.functionUtils import enforceRequirements
|
|
9
|
+
from kisa_utils.structures.utils import Value
|
|
10
|
+
|
|
11
|
+
db.RETURN_KISA_RESPONSES = True
|
|
12
|
+
|
|
13
|
+
class __PersistentQueueSingleton(type):
|
|
14
|
+
_instances = {}
|
|
15
|
+
|
|
16
|
+
def __call__(cls, *args, **kwargs):
|
|
17
|
+
if not args:
|
|
18
|
+
raise Exception(f'invalid instantiation of {cls}')
|
|
19
|
+
id = args[0]
|
|
20
|
+
storageLocation = kwargs.get('storageLocation', '')
|
|
21
|
+
|
|
22
|
+
if not id.isalnum():
|
|
23
|
+
raise ValueError('persistent queue ID should be an alphanumeric value ie a-zA-Z0-9 with no special characters or spaces')
|
|
24
|
+
|
|
25
|
+
idKey = f'{storageLocation}:{id}'
|
|
26
|
+
|
|
27
|
+
if idKey not in cls._instances:
|
|
28
|
+
cls._instances[idKey] = super().__call__(*args, **kwargs)
|
|
29
|
+
|
|
30
|
+
return cls._instances[idKey]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class PersistentQueue(metaclass=__PersistentQueueSingleton):
|
|
34
|
+
__openedQueues:dict = {} # name: PersistentQueue
|
|
35
|
+
|
|
36
|
+
__allowedDataTypes = str|int|float|dict|KDict|list|tuple|set
|
|
37
|
+
|
|
38
|
+
__dataTypeCasts = {
|
|
39
|
+
'str': str,
|
|
40
|
+
'int': int,
|
|
41
|
+
'float': float,
|
|
42
|
+
'dict': dict,
|
|
43
|
+
'KDict': KDict,
|
|
44
|
+
'list': list,
|
|
45
|
+
'tuple': tuple,
|
|
46
|
+
'set': set
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
__defaultDirectoryName:str = '.kisaPersistentQueues'
|
|
50
|
+
__defaultStorageLocation:str = Path.join(Path.HOME, __defaultDirectoryName)
|
|
51
|
+
|
|
52
|
+
__schema = {
|
|
53
|
+
'data': '''
|
|
54
|
+
tstamp varchar(30) not null,
|
|
55
|
+
dataType varcahr(30) not null,
|
|
56
|
+
data json not null
|
|
57
|
+
''',
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def __init__(self, id:str, /, *, storageLocation:str=''):
|
|
62
|
+
'''
|
|
63
|
+
create a new thread-safe PersistentQueue
|
|
64
|
+
Args:
|
|
65
|
+
id(str): the Queue ID. queue sharing an ID will all reference the same underwlying queue
|
|
66
|
+
storageLocation(str): if provided, the location to store the queue. if not provided, data is stored in the default location. use `obj.defaultStorageLocation` to get the default storage directory
|
|
67
|
+
'''
|
|
68
|
+
|
|
69
|
+
if storageLocation:
|
|
70
|
+
if not Path.exists(storageLocation):
|
|
71
|
+
raise Exception(f'could not find storage directory: `{storageLocation}`')
|
|
72
|
+
else:
|
|
73
|
+
storageLocation = self.__defaultStorageLocation
|
|
74
|
+
if not Path.exists(storageLocation) and not Path.createDirectory(storageLocation):
|
|
75
|
+
raise Exception(f'failed to create storage directory: `{storageLocation}`')
|
|
76
|
+
|
|
77
|
+
self.__length:int = 0
|
|
78
|
+
self.id = id
|
|
79
|
+
|
|
80
|
+
self.dbPath = Path.join(storageLocation, self.id)
|
|
81
|
+
|
|
82
|
+
# ensure thread safety throughout the running program
|
|
83
|
+
self.append = queueCallsInThreads(self.append, group=self.id)
|
|
84
|
+
self.peek = queueCallsInThreads(self.peek, group=self.id)
|
|
85
|
+
self.pop = queueCallsInThreads(self.pop, group=self.id)
|
|
86
|
+
|
|
87
|
+
self.__load__ = queueCallsInThreads(self.__load__, group = "pqueue__load__")
|
|
88
|
+
|
|
89
|
+
if not (resp := self.__load__()):
|
|
90
|
+
raise Exception(f'P-QUEUE DB LOAD ERROR: {resp.log}')
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def defaultStorageLocation(self):
|
|
94
|
+
'''
|
|
95
|
+
get the default storage location
|
|
96
|
+
'''
|
|
97
|
+
return self.__defaultStorageLocation
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def allowedDataTypes(self):
|
|
101
|
+
'''
|
|
102
|
+
get data types allowed in the queue
|
|
103
|
+
'''
|
|
104
|
+
return self.__allowedDataTypes
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def length(self) -> int:
|
|
108
|
+
'''
|
|
109
|
+
get number of items in the queue
|
|
110
|
+
'''
|
|
111
|
+
return self.__length
|
|
112
|
+
|
|
113
|
+
def __load__(self) -> Response:
|
|
114
|
+
'''
|
|
115
|
+
load underlying queue database
|
|
116
|
+
'''
|
|
117
|
+
|
|
118
|
+
with Handle(self.dbPath, tables=self.__schema) as handle:
|
|
119
|
+
if not (resp := handle.fetch('data', ['count(*)'],'',[])):
|
|
120
|
+
return resp
|
|
121
|
+
|
|
122
|
+
self.__length = resp.data[0][0]
|
|
123
|
+
|
|
124
|
+
return Ok()
|
|
125
|
+
|
|
126
|
+
def __resolveIndex(self, index:int) -> Response:
|
|
127
|
+
'''
|
|
128
|
+
resolve index and determine if its legitimate
|
|
129
|
+
'''
|
|
130
|
+
if index<0:
|
|
131
|
+
index = self.__length + index
|
|
132
|
+
|
|
133
|
+
if not (0 <= index < self.__length):
|
|
134
|
+
return Error(f'invalid index given for persistent queue of length {self.__length}')
|
|
135
|
+
|
|
136
|
+
return Ok(index)
|
|
137
|
+
|
|
138
|
+
@enforceRequirements
|
|
139
|
+
def append(self, data:__allowedDataTypes, /) -> Response:
|
|
140
|
+
'''
|
|
141
|
+
append to the queue
|
|
142
|
+
Args:
|
|
143
|
+
data(__allowedDataTypes): the data to insert, use `obj.allowedDataTypes` to get the list of allowed data types
|
|
144
|
+
'''
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
dataType = type(data).__name__
|
|
148
|
+
except:
|
|
149
|
+
return Error(f'failed to get data-type of `{data}`')
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
with Handle(self.dbPath, readonly=False) as handle:
|
|
153
|
+
if not (resp := handle.insert('data', [
|
|
154
|
+
dates.currentTimestamp(),
|
|
155
|
+
dataType,
|
|
156
|
+
data
|
|
157
|
+
])):
|
|
158
|
+
return resp
|
|
159
|
+
|
|
160
|
+
self.__length += 1
|
|
161
|
+
return Ok(self.__length)
|
|
162
|
+
|
|
163
|
+
def peek(self, index:int, /) -> Response:
|
|
164
|
+
'''
|
|
165
|
+
get data at `index` without popping it from the queue
|
|
166
|
+
'''
|
|
167
|
+
if not (resp := self.__resolveIndex(index)): return resp
|
|
168
|
+
index = resp.data
|
|
169
|
+
|
|
170
|
+
with Handle(self.dbPath) as handle:
|
|
171
|
+
if not (resp := handle.fetch('data', ['dataType', 'data'],'',[],limit=1, offset=index, parseJson=True)):
|
|
172
|
+
return resp
|
|
173
|
+
|
|
174
|
+
dataType, data = resp.data[0]
|
|
175
|
+
|
|
176
|
+
try:
|
|
177
|
+
data = self.__dataTypeCasts[dataType](data)
|
|
178
|
+
except:
|
|
179
|
+
return Error(f'failed to convert value to data-type `{dataType}`. the database was most likely corrupted')
|
|
180
|
+
|
|
181
|
+
return Ok(data)
|
|
182
|
+
|
|
183
|
+
def pop(self, /, *, index:int = 0) -> Response:
|
|
184
|
+
'''
|
|
185
|
+
get data at `index` and remove it from the queue
|
|
186
|
+
'''
|
|
187
|
+
if not (resp := self.__resolveIndex(index)): return resp
|
|
188
|
+
index = resp.data
|
|
189
|
+
|
|
190
|
+
with Handle(self.dbPath, readonly=False) as handle:
|
|
191
|
+
if not (resp := handle.fetch('data', ['rowid','dataType', 'data'],'',[],limit=1, offset=index, parseJson=True)):
|
|
192
|
+
return resp
|
|
193
|
+
|
|
194
|
+
rowId, dataType, data = resp.data[0]
|
|
195
|
+
|
|
196
|
+
try:
|
|
197
|
+
data = self.__dataTypeCasts[dataType](data)
|
|
198
|
+
except:
|
|
199
|
+
return Error(f'failed to convert value to data-type `{dataType}`. the database was most likely corrupted')
|
|
200
|
+
|
|
201
|
+
if not (resp := handle.delete('data','rowid=?',[rowId])):
|
|
202
|
+
return resp
|
|
203
|
+
|
|
204
|
+
self.__length -= 1
|
|
205
|
+
|
|
206
|
+
return Ok(data)
|
kisa_utils/storage.py
CHANGED
|
@@ -5,7 +5,15 @@ from kisa_utils.response import Response, Error, Ok
|
|
|
5
5
|
encodeJSON = json.JSONEncoder().encode
|
|
6
6
|
decodeJSON = json.JSONDecoder().decode
|
|
7
7
|
|
|
8
|
-
class
|
|
8
|
+
class _Meta(type):
|
|
9
|
+
@property
|
|
10
|
+
def HOME(cls):
|
|
11
|
+
'''
|
|
12
|
+
get user's home directory
|
|
13
|
+
'''
|
|
14
|
+
return os.path.expanduser('~')
|
|
15
|
+
|
|
16
|
+
class Path(metaclass=_Meta):
|
|
9
17
|
join = os.path.join
|
|
10
18
|
directoryName = os.path.dirname
|
|
11
19
|
|
|
@@ -4,7 +4,7 @@ kisa_utils/codes.py,sha256=PV_S53Skggf4XetOdYoIKtEmM8cpN5wZwUlxje70WZY,904
|
|
|
4
4
|
kisa_utils/config.py,sha256=NfluzGKTh66qfNtC-Ae0zNb1XzMTgU2Me9Vi82R9c1E,2285
|
|
5
5
|
kisa_utils/dataStructures.py,sha256=ZgLpttJ66jfpU1NWzLDD1Czqxzj6sWereffgTQWhlV8,2679
|
|
6
6
|
kisa_utils/dates.py,sha256=zxe4n0PdKReZjK5ZkvnCZtJ55lk5oqu9oS8VX_nLozw,13966
|
|
7
|
-
kisa_utils/db.py,sha256=
|
|
7
|
+
kisa_utils/db.py,sha256=hWxkW21lgViOqFijxL4cD-Wpt4koWz6jzcE1v0IiT1c,54341
|
|
8
8
|
kisa_utils/encryption.py,sha256=nFzNpzWV_D9uSEq4FsgCnlS7FQtqWP9fvM_81rsfcLo,4218
|
|
9
9
|
kisa_utils/figures.py,sha256=pYIpQzu1OXRSsY1d98GhgPifnIRmgl-r7S32ai-Ms0c,3731
|
|
10
10
|
kisa_utils/functionUtils.py,sha256=PlXjnmU1uJWNdISlJJ3SCgavTsgNBoebaa9dtWSFhRA,6553
|
|
@@ -12,12 +12,13 @@ kisa_utils/log.py,sha256=0TYdxcIBts026RCSuVIQBcZ-CW1ES7n3M1nEIjmeLTM,2295
|
|
|
12
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
|
-
kisa_utils/storage.py,sha256=
|
|
15
|
+
kisa_utils/storage.py,sha256=6NdEVrHMS7WB_vmCwiGigIinu-EjxalFJhk1kj-_vWs,5990
|
|
16
16
|
kisa_utils/threads.py,sha256=qQqsf64YHMyLpboq5AEXKxYqf3iXUhxiJe6Ymg-vlxI,12840
|
|
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=q7LGl26f-MPXkLS6nxBKDotW3xdB8y7pI5S_Oo5fPOw,47976
|
|
20
|
-
kisa_utils/queues/__init__.py,sha256=
|
|
20
|
+
kisa_utils/queues/__init__.py,sha256=VvhceyN5qeiMel1JFQwLRuVk48oBXaWvDtriCubDOms,48
|
|
21
|
+
kisa_utils/queues/persistent.py,sha256=XTXMu7Q7DMizHIgB3PKrW9iq0PA0ftQa7080j3tCwN0,6668
|
|
21
22
|
kisa_utils/queues/callables/__init__.py,sha256=OJL3AQnaAS1Eek4H6WBH3WefA2wf-x03cwFmRSK8hoU,141
|
|
22
23
|
kisa_utils/queues/callables/enqueueFunctionCalls.py,sha256=VIliaMvw4MUdOqts0dXdZCYNxs-QrOVjIRAR3scGrRM,11786
|
|
23
24
|
kisa_utils/queues/callables/executorQueues.py,sha256=x6bAqxBSZRZ_kL8CK1lSN6JYAYFLxzM84LC1RmwaOLw,6626
|
|
@@ -26,7 +27,7 @@ kisa_utils/servers/flask.py,sha256=XZYY1pWnP1mSvaS5Uv8G3EFJV5BJBQtU2gDbO8suvLc,4
|
|
|
26
27
|
kisa_utils/structures/__init__.py,sha256=JBU1j3A42jQ62ALKnsS1Hav9YXcYwjDw1wQJtohXPbU,83
|
|
27
28
|
kisa_utils/structures/utils.py,sha256=665rXIapGwFqejizeJwy3DryeskCQOdgP25BCdLkGvk,2898
|
|
28
29
|
kisa_utils/structures/validator.py,sha256=JhD9jcfbjTwBr_7OfuNaJd_cYr7wR2emFhsCEo5MCHQ,4323
|
|
29
|
-
kisa_utils-0.
|
|
30
|
-
kisa_utils-0.
|
|
31
|
-
kisa_utils-0.
|
|
32
|
-
kisa_utils-0.
|
|
30
|
+
kisa_utils-0.42.1.dist-info/METADATA,sha256=PmeuHNl4KeA7DIqDWMUe40jTqtxO-yTUxzSDlqxp1pU,477
|
|
31
|
+
kisa_utils-0.42.1.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
32
|
+
kisa_utils-0.42.1.dist-info/top_level.txt,sha256=GFOLXZYqpBG9xtscGa2uGJAEiZ5NwsqHBH9NylnB29M,11
|
|
33
|
+
kisa_utils-0.42.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|