ab_engine 0.1.9__tar.gz → 0.2.0__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.
- {ab_engine-0.1.9 → ab_engine-0.2.0}/PKG-INFO +61 -2
- {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/__init__.py +1 -1
- {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/db/driver.py +3 -2
- {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/db/driver_valkey.py +37 -1
- {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/db/option.py +44 -5
- {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/db/processor.py +16 -17
- {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/env/config.py +13 -9
- {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/error.yaml +5 -1
- ab_engine-0.2.0/ab_engine/rpc/__init__.py +2 -0
- {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/rpc/rpc.py +116 -3
- {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine.egg-info/PKG-INFO +61 -2
- {ab_engine-0.1.9 → ab_engine-0.2.0}/pyproject.toml +1 -1
- {ab_engine-0.1.9 → ab_engine-0.2.0}/readme.md +60 -1
- ab_engine-0.1.9/ab_engine/rpc/__init__.py +0 -2
- {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/class_tools.py +0 -0
- {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/db/__init__.py +0 -0
- {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/db/driver_mysql.py +0 -0
- {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/db/driver_postgresql.py +0 -0
- {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/db/driver_sqlite.py +0 -0
- {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/db/table.py +0 -0
- {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/env/__init__.py +0 -0
- {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/env/db_context.py +0 -0
- {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/env/timer.py +0 -0
- {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/error.py +0 -0
- {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/rpc/fnc.py +0 -0
- {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/rpc/json_rpc.py +0 -0
- {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine.egg-info/SOURCES.txt +0 -0
- {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine.egg-info/dependency_links.txt +0 -0
- {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine.egg-info/requires.txt +0 -0
- {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine.egg-info/top_level.txt +0 -0
- {ab_engine-0.1.9 → ab_engine-0.2.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ab_engine
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Library of standard modules
|
|
5
5
|
Author-email: Andrew Bulatov <BulatovAndrew@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -655,6 +655,7 @@ async def sql(query:str, *args, **kwargs):
|
|
|
655
655
|
Передавать опции можно только в конце списка args, после позиционных параметров, либо вместо них.
|
|
656
656
|
В библиотеке реализованы следующие опции:
|
|
657
657
|
* DB - служит для передачи соединения в функцию sql. Передается как DB("строка соединения").
|
|
658
|
+
* NOTICE - то же, что DB, но запросы с такой опцией возвращают как результат список сообщений нотификации
|
|
658
659
|
* TIMEOUT- устанавливает максимальное время в секундах на выполнение запроса. точность - до сотых секунды.
|
|
659
660
|
По умолчанию вызывает ошибку, при превышении времени ожидания. TIMEOUT(time_sec:float, raise_error:bool=True)
|
|
660
661
|
* DICT - Указывает, что результат следует вернуть как список dict (RowFactory.DICT). Установлена по умолчанию
|
|
@@ -1217,7 +1218,65 @@ if __name__ == '__main__':
|
|
|
1217
1218
|
|
|
1218
1219
|
### Регистрация методов
|
|
1219
1220
|
|
|
1220
|
-
Методы RPC регистрируются c помощью
|
|
1221
|
+
Методы RPC регистрируются функцией register_rpc_list либо c помощью декоратора register_rpc, который также можно использовать как функцию.
|
|
1222
|
+
|
|
1223
|
+
Регистрация списка функций осуществляется с помощью register_rpc_list, которая принимает на вход парамектры defs и self.
|
|
1224
|
+
* **defs** - содержит список или словарь с описаниями
|
|
1225
|
+
* **self** - необязательный параметр, в котором передается модуль или объект из которого будут регистрироваться функции. если в self ничего не передано, то будут регистрироваться функции из модуля, в котором вызвана register_rpc_list.
|
|
1226
|
+
|
|
1227
|
+
список или словарь с орписаниями содержит элементы следующей структуры:
|
|
1228
|
+
* **name**: имя функции в API (не нужно и игнорируется, если описание передается в словаре)
|
|
1229
|
+
* **function**: имя реально вызываемой функции, если оно отличается от name
|
|
1230
|
+
* **sql**: запрос sql, реализующий функцию, если передан sql, то не нужно передавать function и type
|
|
1231
|
+
* **help**: описание функции. если не задано - берется из функции
|
|
1232
|
+
* **type**: тип функции
|
|
1233
|
+
* **self** - функция из объекта, переданного в параметре self
|
|
1234
|
+
* **py** - функция из плагина python
|
|
1235
|
+
* **db** - функция БД
|
|
1236
|
+
* **ext**: дополнительные параметры регистрации, если нужны. для sql это могут быть опции или их имена
|
|
1237
|
+
|
|
1238
|
+
для функций с типом **self** для которых name==function возможна запись:
|
|
1239
|
+
***имя_функции: self***
|
|
1240
|
+
|
|
1241
|
+
Пример списка для регистрации:
|
|
1242
|
+
```python
|
|
1243
|
+
from ab_engine import register_rpc_list, call_rpc
|
|
1244
|
+
|
|
1245
|
+
def hello():
|
|
1246
|
+
return "Здравствуй мир!"
|
|
1247
|
+
|
|
1248
|
+
register_rpc_list({
|
|
1249
|
+
"hello":"self",
|
|
1250
|
+
"hello2":{
|
|
1251
|
+
"sql":"""
|
|
1252
|
+
do $$
|
|
1253
|
+
declare
|
|
1254
|
+
nm varchar;
|
|
1255
|
+
begin
|
|
1256
|
+
nm = $name::varchar;
|
|
1257
|
+
raise notice 'Hello % !!', nm;
|
|
1258
|
+
end $$
|
|
1259
|
+
""",
|
|
1260
|
+
"ext": ["NOTICE","ONE"]
|
|
1261
|
+
},
|
|
1262
|
+
"set":{
|
|
1263
|
+
"sql":"set $key $value",
|
|
1264
|
+
"ext":"DB(valkey)",
|
|
1265
|
+
"help": "устанавливает значение ключа key равным value"
|
|
1266
|
+
},
|
|
1267
|
+
"get":{
|
|
1268
|
+
"sql":"get $key",
|
|
1269
|
+
"ext":"DB(valkey)",
|
|
1270
|
+
"help": "возвращает значение ключа key"
|
|
1271
|
+
}
|
|
1272
|
+
})
|
|
1273
|
+
|
|
1274
|
+
async def test():
|
|
1275
|
+
await call_rpc("set", key="test", value=1234)
|
|
1276
|
+
```
|
|
1277
|
+
|
|
1278
|
+
|
|
1279
|
+
Регистрация с помощью декоратора:
|
|
1221
1280
|
```python
|
|
1222
1281
|
from ab_engine import register_rpc
|
|
1223
1282
|
from ab_engine.db import ONE
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
from .env import Config, LogLevel
|
|
2
2
|
from .env.timer import TimerInterval
|
|
3
|
-
from .rpc import register as register_rpc, call_rpc, call_json
|
|
3
|
+
from .rpc import register as register_rpc, call_rpc, call_json, register_list as register_rpc_list
|
|
4
4
|
from .error import raise_error, load_errors, error
|
|
@@ -168,7 +168,8 @@ class Driver(ABC):
|
|
|
168
168
|
if callback:=kwargs.get("__PARAM_CALLBACK_GETTER"):
|
|
169
169
|
del kwargs["__PARAM_CALLBACK_GETTER"]
|
|
170
170
|
qs = None
|
|
171
|
-
|
|
171
|
+
|
|
172
|
+
for x in query.replace("$$", "🗿").split("$"):
|
|
172
173
|
if qs is None:
|
|
173
174
|
qs = x
|
|
174
175
|
continue
|
|
@@ -195,7 +196,7 @@ class Driver(ABC):
|
|
|
195
196
|
qs += f"{self.var_to_sql(n, x)}{x}"
|
|
196
197
|
|
|
197
198
|
if qs:
|
|
198
|
-
query = qs
|
|
199
|
+
query = qs.replace("🗿", "$$")
|
|
199
200
|
|
|
200
201
|
return query
|
|
201
202
|
|
|
@@ -216,4 +216,40 @@ class Driver(BaseDriver):
|
|
|
216
216
|
await super().rollback()
|
|
217
217
|
|
|
218
218
|
async def table_struct(self, table_name) -> dict:
|
|
219
|
-
return {}
|
|
219
|
+
return {}
|
|
220
|
+
|
|
221
|
+
async def parse_query(self, query, *args, **kwargs):
|
|
222
|
+
if callback:=kwargs.get("__PARAM_CALLBACK_GETTER"):
|
|
223
|
+
del kwargs["__PARAM_CALLBACK_GETTER"]
|
|
224
|
+
qs = None
|
|
225
|
+
|
|
226
|
+
for x in query.split("$"):
|
|
227
|
+
if qs is None:
|
|
228
|
+
qs = x
|
|
229
|
+
continue
|
|
230
|
+
n, p = '', 0
|
|
231
|
+
if str(x[p]).isdigit():
|
|
232
|
+
while p < len(x) and x[p].isdigit():
|
|
233
|
+
n += x[p]
|
|
234
|
+
p += 1
|
|
235
|
+
n = int(n) - 1
|
|
236
|
+
if n < 0 or n >= len(args):
|
|
237
|
+
raise AttributeError(f"Attribute with index {n} is not exists")
|
|
238
|
+
n = args[n]
|
|
239
|
+
else:
|
|
240
|
+
while p < len(x) and not(x[p].isspace() or x[p] in "(:,)"):
|
|
241
|
+
n += x[p]
|
|
242
|
+
p += 1
|
|
243
|
+
if n in kwargs:
|
|
244
|
+
n = kwargs[n]
|
|
245
|
+
elif callback:
|
|
246
|
+
n = callback[n]
|
|
247
|
+
else:
|
|
248
|
+
raise AttributeError(f"Attribute with name {n} is not exists")
|
|
249
|
+
x = x[p:]
|
|
250
|
+
qs = f"{qs}{n}{x}"
|
|
251
|
+
|
|
252
|
+
if qs:
|
|
253
|
+
query = qs
|
|
254
|
+
|
|
255
|
+
return query
|
|
@@ -5,7 +5,7 @@ from pathlib import Path
|
|
|
5
5
|
from importlib.machinery import SourceFileLoader
|
|
6
6
|
from json5 import loads
|
|
7
7
|
from asyncio import BoundedSemaphore, wait_for, TimeoutError, sleep
|
|
8
|
-
from ..error import
|
|
8
|
+
from ..error import error
|
|
9
9
|
from inspect import iscoroutinefunction
|
|
10
10
|
from typing import Callable
|
|
11
11
|
from gc import collect as garbage_collect
|
|
@@ -66,6 +66,25 @@ class Option(ABC):
|
|
|
66
66
|
"""
|
|
67
67
|
return ret_data, row_factory
|
|
68
68
|
|
|
69
|
+
@staticmethod
|
|
70
|
+
def create(option:str, *args, **kwargs):
|
|
71
|
+
|
|
72
|
+
def find(parent_cls, name):
|
|
73
|
+
for x in parent_cls.__subclasses__():
|
|
74
|
+
if x.__name__ == name:
|
|
75
|
+
return x
|
|
76
|
+
else:
|
|
77
|
+
x = find(x, name)
|
|
78
|
+
if x:
|
|
79
|
+
return x
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
x = find(Option, option)
|
|
83
|
+
if x is None:
|
|
84
|
+
raise error("CLASS_NOT_FOUND", name=option)
|
|
85
|
+
return x(*args, **kwargs)
|
|
86
|
+
|
|
87
|
+
|
|
69
88
|
_set_is_option(Option.is_option)
|
|
70
89
|
|
|
71
90
|
class ALL(Option):
|
|
@@ -98,7 +117,7 @@ class DB(Option):
|
|
|
98
117
|
try:
|
|
99
118
|
_check_cfg()
|
|
100
119
|
except Exception as e:
|
|
101
|
-
|
|
120
|
+
raise error("NA_DB", connection=str(e))
|
|
102
121
|
connection_string = _CFG_.db_connection(connection_string)
|
|
103
122
|
driver_name, connection_string = connection_string.split("://", 1)
|
|
104
123
|
|
|
@@ -216,6 +235,22 @@ class TIMEOUT(Option):
|
|
|
216
235
|
raise e
|
|
217
236
|
return None
|
|
218
237
|
|
|
238
|
+
class NOTICE(DB):
|
|
239
|
+
|
|
240
|
+
def __init__(self, connection_string: str=""):
|
|
241
|
+
self._notify = []
|
|
242
|
+
super().__init__(connection_string, self._notify)
|
|
243
|
+
|
|
244
|
+
@classproperty
|
|
245
|
+
def can_process(cls):
|
|
246
|
+
return True
|
|
247
|
+
|
|
248
|
+
async def process(self, ret_data=None, connection=None, row_factory=None):
|
|
249
|
+
ret = self._notify.copy()
|
|
250
|
+
self._notify.clear()
|
|
251
|
+
return ret, None
|
|
252
|
+
|
|
253
|
+
|
|
219
254
|
class DICT(Option):
|
|
220
255
|
"""
|
|
221
256
|
Указывает, что результат следует вернуть как список dict (по умолчанию)
|
|
@@ -260,6 +295,10 @@ class ONE(ROW):
|
|
|
260
295
|
async def process(ret_data, connection, row_factory):
|
|
261
296
|
if ret_data is None or isinstance(ret_data, int):
|
|
262
297
|
return ret_data, None
|
|
298
|
+
elif row_factory is None and isinstance(ret_data, list):
|
|
299
|
+
if len(ret_data) == 0:
|
|
300
|
+
return None
|
|
301
|
+
return ret_data[0], RowFactory.ANY
|
|
263
302
|
match row_factory:
|
|
264
303
|
case RowFactory.TUPLE:
|
|
265
304
|
return ret_data[0], None
|
|
@@ -286,7 +325,7 @@ class JSON(ONE):
|
|
|
286
325
|
elif isinstance(ret_data, str):
|
|
287
326
|
return loads(ret_data), None
|
|
288
327
|
else:
|
|
289
|
-
|
|
328
|
+
raise error("NOT_CONV_DICT_LIST", data=ret_data)
|
|
290
329
|
|
|
291
330
|
class ROLLBACK(Option):
|
|
292
331
|
"""
|
|
@@ -325,7 +364,7 @@ class PAGE(Option):
|
|
|
325
364
|
:param offset: смещение от начала
|
|
326
365
|
"""
|
|
327
366
|
if not(isinstance(limit, int) and isinstance(offset, int)) or limit < 0 or offset < 0:
|
|
328
|
-
|
|
367
|
+
raise error("BAD_LIMIT_OFFSET")
|
|
329
368
|
self._limit = limit
|
|
330
369
|
self._offset = offset
|
|
331
370
|
|
|
@@ -361,7 +400,7 @@ class CALLBACK(Option):
|
|
|
361
400
|
|
|
362
401
|
def __getitem__(self, item):
|
|
363
402
|
if self._attrs is None:
|
|
364
|
-
|
|
403
|
+
raise error("ATTR_NOT_FOUND", name=item)
|
|
365
404
|
return self._attrs[item]
|
|
366
405
|
|
|
367
406
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from .option import Option, DB, TIMEOUT, PAGE, CALLBACK, ITERATOR, RAW
|
|
1
|
+
from .option import Option, DB, TIMEOUT, PAGE, CALLBACK, ITERATOR, RAW, NOTICE
|
|
2
2
|
from .driver import RowFactory
|
|
3
|
-
from ..error import
|
|
3
|
+
from ..error import error
|
|
4
4
|
|
|
5
5
|
CONNECTION:str = ""
|
|
6
6
|
|
|
@@ -19,38 +19,38 @@ async def sql(query:str, *args, **kwargs):
|
|
|
19
19
|
if one_row is None and arg.one_row is not None:
|
|
20
20
|
one_row = arg
|
|
21
21
|
elif one_row and arg.one_row is not None and one_row.one_row!=arg.one_row:
|
|
22
|
-
|
|
22
|
+
raise error("NEQ_ROWS", src1=one_row.__class__.__name__, src2=arg.__class__.__name__)
|
|
23
23
|
if row_factory is None or row_factory.row_factory == RowFactory.ANY:
|
|
24
24
|
row_factory = arg
|
|
25
25
|
elif row_factory and arg.row_factory != RowFactory.ANY:
|
|
26
|
-
|
|
26
|
+
raise error("DEF_STR_FABRIC", src = row_factory.__class__.__name__)
|
|
27
|
+
if arg in (DB, NOTICE):
|
|
28
|
+
arg = arg()
|
|
29
|
+
if isinstance(arg, DB) and db is None:
|
|
30
|
+
db = arg
|
|
27
31
|
if arg.can_process:
|
|
28
32
|
process.append(arg)
|
|
29
33
|
continue
|
|
30
|
-
if arg
|
|
31
|
-
arg = DB()
|
|
32
|
-
if isinstance(arg, DB) and db is None:
|
|
33
|
-
db = arg
|
|
34
|
-
elif isinstance(arg, CALLBACK) and callback is None:
|
|
34
|
+
if isinstance(arg, CALLBACK) and callback is None:
|
|
35
35
|
callback = arg
|
|
36
36
|
elif isinstance(arg, DB):
|
|
37
|
-
|
|
37
|
+
continue #raise error("DB_ALRDY_DEF")
|
|
38
38
|
elif isinstance(arg, PAGE) and page is None:
|
|
39
39
|
page = arg
|
|
40
40
|
elif isinstance(arg, PAGE):
|
|
41
|
-
|
|
41
|
+
raise error("PAGE_ALRDY_DEF")
|
|
42
42
|
elif isinstance(arg, TIMEOUT) and tm is None:
|
|
43
43
|
tm = arg
|
|
44
44
|
elif isinstance(arg, TIMEOUT):
|
|
45
|
-
|
|
45
|
+
raise error("TIMEOUT_ALRDY_DEF")
|
|
46
46
|
elif arg == ITERATOR:
|
|
47
47
|
itr = ITERATOR()
|
|
48
48
|
elif isinstance(arg, ITERATOR):
|
|
49
49
|
itr = arg
|
|
50
|
-
elif arg == RAW:
|
|
50
|
+
elif arg == RAW or isinstance(arg, RAW):
|
|
51
51
|
parse = False
|
|
52
52
|
elif process:
|
|
53
|
-
|
|
53
|
+
raise error("PMT_BEF_OPT")
|
|
54
54
|
if process:
|
|
55
55
|
args = args[:process.pop(0)]
|
|
56
56
|
if db is None:
|
|
@@ -61,17 +61,16 @@ async def sql(query:str, *args, **kwargs):
|
|
|
61
61
|
await db.garbage_collect(False)
|
|
62
62
|
one_row = one_row.one_row if one_row else False
|
|
63
63
|
row_factory = row_factory.row_factory if row_factory and row_factory.row_factory != RowFactory.ANY else RowFactory.DICT
|
|
64
|
-
if callback:
|
|
65
|
-
kwargs["__PARAM_CALLBACK_GETTER"] = callback
|
|
66
64
|
if parse:
|
|
67
65
|
query = await db.connection.parse_query(query, *args, **kwargs)
|
|
68
66
|
if page:
|
|
69
67
|
query = await page(db.connection, query)
|
|
70
68
|
if callback is not None:
|
|
69
|
+
kwargs["__PARAM_CALLBACK_GETTER"] = callback
|
|
71
70
|
query = await callback(query)
|
|
72
71
|
if itr:
|
|
73
72
|
if page:
|
|
74
|
-
|
|
73
|
+
raise error("PAGE_ITERATOR")
|
|
75
74
|
return itr(query, db, row_factory, process)
|
|
76
75
|
try:
|
|
77
76
|
if tm is None:
|
|
@@ -451,7 +451,9 @@ class Config:
|
|
|
451
451
|
|
|
452
452
|
if isinstance(msg, Exception):
|
|
453
453
|
level = LogLevel.ERROR
|
|
454
|
+
sql = False
|
|
454
455
|
else:
|
|
456
|
+
sql = level == LogLevel.SQL
|
|
455
457
|
level = self._parse_log_level(level)
|
|
456
458
|
msg = str(msg)
|
|
457
459
|
if level.value < self.log_level.value:
|
|
@@ -479,15 +481,15 @@ class Config:
|
|
|
479
481
|
info = log_to.findCaller(stacklevel=lvl)
|
|
480
482
|
p = str(Path(__file__).parent.parent)
|
|
481
483
|
while info[0].startswith(p):
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
484
|
+
lvl += 1
|
|
485
|
+
x = log_to.findCaller(stacklevel=lvl)
|
|
486
|
+
if '/venv/' in x[0]:
|
|
487
|
+
break
|
|
488
|
+
elif not x[0].startswith(p):
|
|
489
|
+
info = x
|
|
490
|
+
break
|
|
491
|
+
else:
|
|
492
|
+
info = x
|
|
491
493
|
else:
|
|
492
494
|
info = log_to.findCaller(stacklevel=kwargs.get('stacklevel', 2))
|
|
493
495
|
|
|
@@ -510,6 +512,8 @@ class Config:
|
|
|
510
512
|
|
|
511
513
|
params["msg"] = f"{msg}"
|
|
512
514
|
info = log_to.makeRecord(**params)
|
|
515
|
+
if sql:
|
|
516
|
+
info.levelname = "SQL"
|
|
513
517
|
log_to.handle(info)
|
|
514
518
|
except Exception as e:
|
|
515
519
|
# Больше вывести некуда - выводим на стандартный вывод
|
|
@@ -116,4 +116,8 @@
|
|
|
116
116
|
class: RuntimeError
|
|
117
117
|
- code: BAD_RPC_METHOD
|
|
118
118
|
msg: Имя метода содержит недопустимое значение
|
|
119
|
-
class: ValueError
|
|
119
|
+
class: ValueError
|
|
120
|
+
- code: CLASS_NOT_FOUND
|
|
121
|
+
msg: Класс {name} не найден
|
|
122
|
+
- code: NOT_FOUND_)
|
|
123
|
+
msg: Нет закрывающей скобки {context}
|
|
@@ -3,9 +3,10 @@ from os import sep as file_sep
|
|
|
3
3
|
from .fnc import *
|
|
4
4
|
from ..error import error
|
|
5
5
|
from ..env import Config, DB_ENV
|
|
6
|
-
from
|
|
6
|
+
from ..db.option import Option
|
|
7
|
+
from sys import argv as sys_argv, modules
|
|
7
8
|
from pathlib import Path
|
|
8
|
-
|
|
9
|
+
import inspect
|
|
9
10
|
|
|
10
11
|
def _split_module_fnc(module_name: str):
|
|
11
12
|
# можно передать в пути реальное имя функции после :
|
|
@@ -142,4 +143,116 @@ async def call_rpc(name_of_rpc_method_for_call, current_rpc_environment_for_call
|
|
|
142
143
|
except Exception as e:
|
|
143
144
|
if current_rpc_environment_for_call.in_transaction:
|
|
144
145
|
await current_rpc_environment_for_call.rollback()
|
|
145
|
-
raise e
|
|
146
|
+
raise e
|
|
147
|
+
|
|
148
|
+
def split_params(params:str)->list:
|
|
149
|
+
ret, s, q, lq = [], "", "", False
|
|
150
|
+
|
|
151
|
+
def add_ret():
|
|
152
|
+
nonlocal ret, s, lq
|
|
153
|
+
if lq:
|
|
154
|
+
ret.append(s)
|
|
155
|
+
lq = False
|
|
156
|
+
return
|
|
157
|
+
elif s == "":
|
|
158
|
+
ret.append(None)
|
|
159
|
+
return
|
|
160
|
+
elif s.isnumeric():
|
|
161
|
+
try:
|
|
162
|
+
s = int(s)
|
|
163
|
+
except Exception as e:
|
|
164
|
+
s = float(s)
|
|
165
|
+
ret.append(s)
|
|
166
|
+
return
|
|
167
|
+
lwr = s.lower()
|
|
168
|
+
if s == "true":
|
|
169
|
+
ret.append(True)
|
|
170
|
+
elif s == "false":
|
|
171
|
+
ret.append(False)
|
|
172
|
+
else:
|
|
173
|
+
ret.append(s)
|
|
174
|
+
|
|
175
|
+
for ch in params:
|
|
176
|
+
if ch == q:
|
|
177
|
+
q = ""
|
|
178
|
+
lq = True
|
|
179
|
+
elif ch in ('"',"'"):
|
|
180
|
+
q = ch
|
|
181
|
+
elif ch == "," and q == "":
|
|
182
|
+
add_ret()
|
|
183
|
+
s = ""
|
|
184
|
+
else:
|
|
185
|
+
s += ch
|
|
186
|
+
|
|
187
|
+
if s:
|
|
188
|
+
add_ret()
|
|
189
|
+
return ret
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def register_list(defs:list | dict, self=None)->None:
|
|
193
|
+
"""
|
|
194
|
+
Регистрирует для RPC функции, переданные в списке или в словаре.
|
|
195
|
+
в случае, если описания передаются в словаре, то ключ словаря это имя функции в API
|
|
196
|
+
если описания передаются списком, то имя функции передается в атрибуте name
|
|
197
|
+
:param defs: писок или словарь с описанием функций, элементыы которого имеют следующую структуру:
|
|
198
|
+
name: имя функции в API (не нужно, если описание передается в словаре)
|
|
199
|
+
function: имя реально вызываемой функции, если оно отличается от name
|
|
200
|
+
sql: запрос sql, реализующий функцию, если передан sql, то не нужно передавать function и type
|
|
201
|
+
help: описание функции. если не задано - берется из функции
|
|
202
|
+
type: тип функции
|
|
203
|
+
self - функция из объекта, переданного в параметре self (см. ниже)
|
|
204
|
+
py - функция из плагина python
|
|
205
|
+
db - функция БД
|
|
206
|
+
для функций с типом self для которых name==function возможна запись:
|
|
207
|
+
имя_функции: self
|
|
208
|
+
ext: дополнительные параметры регистрации, если нужны. для sql это могут быть опции или их имена
|
|
209
|
+
:param self: объект или модуль, из которого берем функции с типом self. если не передан,
|
|
210
|
+
то из модуля, откуда вызвана данная функция
|
|
211
|
+
"""
|
|
212
|
+
if self is None:
|
|
213
|
+
frame = inspect.currentframe().f_back
|
|
214
|
+
x = frame.f_globals.get('__name__')
|
|
215
|
+
self = modules.get(x)
|
|
216
|
+
if isinstance(defs, dict):
|
|
217
|
+
...
|
|
218
|
+
elif isinstance(defs, list):
|
|
219
|
+
defs = {x["name"]:x for x in defs}
|
|
220
|
+
else:
|
|
221
|
+
raise error("BAD_RPC_FORMAT")
|
|
222
|
+
for x in defs:
|
|
223
|
+
fnc = defs[x]
|
|
224
|
+
if "sql" in fnc:
|
|
225
|
+
if "ext" in fnc:
|
|
226
|
+
fn = []
|
|
227
|
+
if isinstance(fnc["ext"], str):
|
|
228
|
+
fnc["ext"] = [fnc["ext"],]
|
|
229
|
+
for opt in fnc["ext"]:
|
|
230
|
+
if not isinstance(opt, str):
|
|
231
|
+
fn.append(opt)
|
|
232
|
+
continue
|
|
233
|
+
if "(" in opt:
|
|
234
|
+
opt, params = opt.split("(",1)
|
|
235
|
+
params = params.strip()
|
|
236
|
+
if not params.endswith(")"):
|
|
237
|
+
raise error("NOT_FOUND_)", context=f"ext: {fnc['ext']}")
|
|
238
|
+
params = split_params(params[:-1].strip())
|
|
239
|
+
opt = Option.create(opt, *params)
|
|
240
|
+
else:
|
|
241
|
+
opt = Option.create(opt)
|
|
242
|
+
fn.append(opt)
|
|
243
|
+
fn = tuple([fnc["sql"]] + fn)
|
|
244
|
+
else:
|
|
245
|
+
fn = fnc["sql"]
|
|
246
|
+
SqlFnc(x, fn, help=fnc.get('help'))
|
|
247
|
+
elif fnc == "self":
|
|
248
|
+
fn = getattr(self, x)
|
|
249
|
+
PythonFnc(x, fn)
|
|
250
|
+
else:
|
|
251
|
+
match fnc.get("type", "db"):
|
|
252
|
+
case "db":
|
|
253
|
+
SqlFnc(x, f"\JSON {fnc['function']}(JSONB)", help=fnc.get('help'))
|
|
254
|
+
case "self":
|
|
255
|
+
fn = getattr(self, fnc['function'])
|
|
256
|
+
PythonFnc(x, fn, help = fnc.get('help') )
|
|
257
|
+
case _:
|
|
258
|
+
register(x, fnc["function"], help = fnc.get('help'))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ab_engine
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Library of standard modules
|
|
5
5
|
Author-email: Andrew Bulatov <BulatovAndrew@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -655,6 +655,7 @@ async def sql(query:str, *args, **kwargs):
|
|
|
655
655
|
Передавать опции можно только в конце списка args, после позиционных параметров, либо вместо них.
|
|
656
656
|
В библиотеке реализованы следующие опции:
|
|
657
657
|
* DB - служит для передачи соединения в функцию sql. Передается как DB("строка соединения").
|
|
658
|
+
* NOTICE - то же, что DB, но запросы с такой опцией возвращают как результат список сообщений нотификации
|
|
658
659
|
* TIMEOUT- устанавливает максимальное время в секундах на выполнение запроса. точность - до сотых секунды.
|
|
659
660
|
По умолчанию вызывает ошибку, при превышении времени ожидания. TIMEOUT(time_sec:float, raise_error:bool=True)
|
|
660
661
|
* DICT - Указывает, что результат следует вернуть как список dict (RowFactory.DICT). Установлена по умолчанию
|
|
@@ -1217,7 +1218,65 @@ if __name__ == '__main__':
|
|
|
1217
1218
|
|
|
1218
1219
|
### Регистрация методов
|
|
1219
1220
|
|
|
1220
|
-
Методы RPC регистрируются c помощью
|
|
1221
|
+
Методы RPC регистрируются функцией register_rpc_list либо c помощью декоратора register_rpc, который также можно использовать как функцию.
|
|
1222
|
+
|
|
1223
|
+
Регистрация списка функций осуществляется с помощью register_rpc_list, которая принимает на вход парамектры defs и self.
|
|
1224
|
+
* **defs** - содержит список или словарь с описаниями
|
|
1225
|
+
* **self** - необязательный параметр, в котором передается модуль или объект из которого будут регистрироваться функции. если в self ничего не передано, то будут регистрироваться функции из модуля, в котором вызвана register_rpc_list.
|
|
1226
|
+
|
|
1227
|
+
список или словарь с орписаниями содержит элементы следующей структуры:
|
|
1228
|
+
* **name**: имя функции в API (не нужно и игнорируется, если описание передается в словаре)
|
|
1229
|
+
* **function**: имя реально вызываемой функции, если оно отличается от name
|
|
1230
|
+
* **sql**: запрос sql, реализующий функцию, если передан sql, то не нужно передавать function и type
|
|
1231
|
+
* **help**: описание функции. если не задано - берется из функции
|
|
1232
|
+
* **type**: тип функции
|
|
1233
|
+
* **self** - функция из объекта, переданного в параметре self
|
|
1234
|
+
* **py** - функция из плагина python
|
|
1235
|
+
* **db** - функция БД
|
|
1236
|
+
* **ext**: дополнительные параметры регистрации, если нужны. для sql это могут быть опции или их имена
|
|
1237
|
+
|
|
1238
|
+
для функций с типом **self** для которых name==function возможна запись:
|
|
1239
|
+
***имя_функции: self***
|
|
1240
|
+
|
|
1241
|
+
Пример списка для регистрации:
|
|
1242
|
+
```python
|
|
1243
|
+
from ab_engine import register_rpc_list, call_rpc
|
|
1244
|
+
|
|
1245
|
+
def hello():
|
|
1246
|
+
return "Здравствуй мир!"
|
|
1247
|
+
|
|
1248
|
+
register_rpc_list({
|
|
1249
|
+
"hello":"self",
|
|
1250
|
+
"hello2":{
|
|
1251
|
+
"sql":"""
|
|
1252
|
+
do $$
|
|
1253
|
+
declare
|
|
1254
|
+
nm varchar;
|
|
1255
|
+
begin
|
|
1256
|
+
nm = $name::varchar;
|
|
1257
|
+
raise notice 'Hello % !!', nm;
|
|
1258
|
+
end $$
|
|
1259
|
+
""",
|
|
1260
|
+
"ext": ["NOTICE","ONE"]
|
|
1261
|
+
},
|
|
1262
|
+
"set":{
|
|
1263
|
+
"sql":"set $key $value",
|
|
1264
|
+
"ext":"DB(valkey)",
|
|
1265
|
+
"help": "устанавливает значение ключа key равным value"
|
|
1266
|
+
},
|
|
1267
|
+
"get":{
|
|
1268
|
+
"sql":"get $key",
|
|
1269
|
+
"ext":"DB(valkey)",
|
|
1270
|
+
"help": "возвращает значение ключа key"
|
|
1271
|
+
}
|
|
1272
|
+
})
|
|
1273
|
+
|
|
1274
|
+
async def test():
|
|
1275
|
+
await call_rpc("set", key="test", value=1234)
|
|
1276
|
+
```
|
|
1277
|
+
|
|
1278
|
+
|
|
1279
|
+
Регистрация с помощью декоратора:
|
|
1221
1280
|
```python
|
|
1222
1281
|
from ab_engine import register_rpc
|
|
1223
1282
|
from ab_engine.db import ONE
|
|
@@ -641,6 +641,7 @@ async def sql(query:str, *args, **kwargs):
|
|
|
641
641
|
Передавать опции можно только в конце списка args, после позиционных параметров, либо вместо них.
|
|
642
642
|
В библиотеке реализованы следующие опции:
|
|
643
643
|
* DB - служит для передачи соединения в функцию sql. Передается как DB("строка соединения").
|
|
644
|
+
* NOTICE - то же, что DB, но запросы с такой опцией возвращают как результат список сообщений нотификации
|
|
644
645
|
* TIMEOUT- устанавливает максимальное время в секундах на выполнение запроса. точность - до сотых секунды.
|
|
645
646
|
По умолчанию вызывает ошибку, при превышении времени ожидания. TIMEOUT(time_sec:float, raise_error:bool=True)
|
|
646
647
|
* DICT - Указывает, что результат следует вернуть как список dict (RowFactory.DICT). Установлена по умолчанию
|
|
@@ -1203,7 +1204,65 @@ if __name__ == '__main__':
|
|
|
1203
1204
|
|
|
1204
1205
|
### Регистрация методов
|
|
1205
1206
|
|
|
1206
|
-
Методы RPC регистрируются c помощью
|
|
1207
|
+
Методы RPC регистрируются функцией register_rpc_list либо c помощью декоратора register_rpc, который также можно использовать как функцию.
|
|
1208
|
+
|
|
1209
|
+
Регистрация списка функций осуществляется с помощью register_rpc_list, которая принимает на вход парамектры defs и self.
|
|
1210
|
+
* **defs** - содержит список или словарь с описаниями
|
|
1211
|
+
* **self** - необязательный параметр, в котором передается модуль или объект из которого будут регистрироваться функции. если в self ничего не передано, то будут регистрироваться функции из модуля, в котором вызвана register_rpc_list.
|
|
1212
|
+
|
|
1213
|
+
список или словарь с орписаниями содержит элементы следующей структуры:
|
|
1214
|
+
* **name**: имя функции в API (не нужно и игнорируется, если описание передается в словаре)
|
|
1215
|
+
* **function**: имя реально вызываемой функции, если оно отличается от name
|
|
1216
|
+
* **sql**: запрос sql, реализующий функцию, если передан sql, то не нужно передавать function и type
|
|
1217
|
+
* **help**: описание функции. если не задано - берется из функции
|
|
1218
|
+
* **type**: тип функции
|
|
1219
|
+
* **self** - функция из объекта, переданного в параметре self
|
|
1220
|
+
* **py** - функция из плагина python
|
|
1221
|
+
* **db** - функция БД
|
|
1222
|
+
* **ext**: дополнительные параметры регистрации, если нужны. для sql это могут быть опции или их имена
|
|
1223
|
+
|
|
1224
|
+
для функций с типом **self** для которых name==function возможна запись:
|
|
1225
|
+
***имя_функции: self***
|
|
1226
|
+
|
|
1227
|
+
Пример списка для регистрации:
|
|
1228
|
+
```python
|
|
1229
|
+
from ab_engine import register_rpc_list, call_rpc
|
|
1230
|
+
|
|
1231
|
+
def hello():
|
|
1232
|
+
return "Здравствуй мир!"
|
|
1233
|
+
|
|
1234
|
+
register_rpc_list({
|
|
1235
|
+
"hello":"self",
|
|
1236
|
+
"hello2":{
|
|
1237
|
+
"sql":"""
|
|
1238
|
+
do $$
|
|
1239
|
+
declare
|
|
1240
|
+
nm varchar;
|
|
1241
|
+
begin
|
|
1242
|
+
nm = $name::varchar;
|
|
1243
|
+
raise notice 'Hello % !!', nm;
|
|
1244
|
+
end $$
|
|
1245
|
+
""",
|
|
1246
|
+
"ext": ["NOTICE","ONE"]
|
|
1247
|
+
},
|
|
1248
|
+
"set":{
|
|
1249
|
+
"sql":"set $key $value",
|
|
1250
|
+
"ext":"DB(valkey)",
|
|
1251
|
+
"help": "устанавливает значение ключа key равным value"
|
|
1252
|
+
},
|
|
1253
|
+
"get":{
|
|
1254
|
+
"sql":"get $key",
|
|
1255
|
+
"ext":"DB(valkey)",
|
|
1256
|
+
"help": "возвращает значение ключа key"
|
|
1257
|
+
}
|
|
1258
|
+
})
|
|
1259
|
+
|
|
1260
|
+
async def test():
|
|
1261
|
+
await call_rpc("set", key="test", value=1234)
|
|
1262
|
+
```
|
|
1263
|
+
|
|
1264
|
+
|
|
1265
|
+
Регистрация с помощью декоратора:
|
|
1207
1266
|
```python
|
|
1208
1267
|
from ab_engine import register_rpc
|
|
1209
1268
|
from ab_engine.db import ONE
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|