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.
Files changed (31) hide show
  1. {ab_engine-0.1.9 → ab_engine-0.2.0}/PKG-INFO +61 -2
  2. {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/__init__.py +1 -1
  3. {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/db/driver.py +3 -2
  4. {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/db/driver_valkey.py +37 -1
  5. {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/db/option.py +44 -5
  6. {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/db/processor.py +16 -17
  7. {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/env/config.py +13 -9
  8. {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/error.yaml +5 -1
  9. ab_engine-0.2.0/ab_engine/rpc/__init__.py +2 -0
  10. {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/rpc/rpc.py +116 -3
  11. {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine.egg-info/PKG-INFO +61 -2
  12. {ab_engine-0.1.9 → ab_engine-0.2.0}/pyproject.toml +1 -1
  13. {ab_engine-0.1.9 → ab_engine-0.2.0}/readme.md +60 -1
  14. ab_engine-0.1.9/ab_engine/rpc/__init__.py +0 -2
  15. {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/class_tools.py +0 -0
  16. {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/db/__init__.py +0 -0
  17. {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/db/driver_mysql.py +0 -0
  18. {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/db/driver_postgresql.py +0 -0
  19. {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/db/driver_sqlite.py +0 -0
  20. {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/db/table.py +0 -0
  21. {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/env/__init__.py +0 -0
  22. {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/env/db_context.py +0 -0
  23. {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/env/timer.py +0 -0
  24. {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/error.py +0 -0
  25. {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/rpc/fnc.py +0 -0
  26. {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine/rpc/json_rpc.py +0 -0
  27. {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine.egg-info/SOURCES.txt +0 -0
  28. {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine.egg-info/dependency_links.txt +0 -0
  29. {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine.egg-info/requires.txt +0 -0
  30. {ab_engine-0.1.9 → ab_engine-0.2.0}/ab_engine.egg-info/top_level.txt +0 -0
  31. {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.1.9
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
- for x in query.split("$"):
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 raise_error
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
- raise_error("NA_DB", connection=str(e))
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
- raise_error("NOT_CONV_DICT_LIST", data=ret_data)
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
- raise_error("BAD_LIMIT_OFFSET")
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
- raise_error("ATTR_NOT_FOUND", name=item)
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 raise_error
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
- raise_error("NEQ_ROWS", src1=one_row.__class__.__name__, src2=arg.__class__.__name__)
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
- raise_error("DEF_STR_FABRIC", src = row_factory.__class__.__name__)
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 == DB:
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
- raise_error("DB_ALRDY_DEF")
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
- raise_error("PAGE_ALRDY_DEF")
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
- raise_error("TIMEOUT_ALRDY_DEF")
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
- raise_error("PMT_BEF_OPT")
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
- raise_error("PAGE_ITERATOR")
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
- lvl += 1
483
- x = log_to.findCaller(stacklevel=lvl)
484
- if '/venv/' in x[0]:
485
- break
486
- elif not x[0].startswith(p):
487
- info = x
488
- break
489
- else:
490
- info = x
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}
@@ -0,0 +1,2 @@
1
+ from .rpc import register, call_rpc, register_list
2
+ from .json_rpc import call_json
@@ -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 sys import argv as sys_argv
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.1.9
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,6 +1,6 @@
1
1
  [project]
2
2
  name = "ab_engine"
3
- version = "0.1.9"
3
+ version = "0.2.0"
4
4
  description = "Library of standard modules"
5
5
  authors = [{name = "Andrew Bulatov", email = "BulatovAndrew@gmail.com"}]
6
6
  license = "MIT"
@@ -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
@@ -1,2 +0,0 @@
1
- from .rpc import register, call_rpc
2
- from .json_rpc import call_json
File without changes
File without changes