ab_engine 0.2.0__tar.gz → 0.2.1__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.2.0/ab_engine.egg-info → ab_engine-0.2.1}/PKG-INFO +41 -9
- {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/db/driver.py +2 -2
- {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/db/driver_sqlite.py +6 -2
- {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/db/option.py +6 -2
- {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/db/processor.py +2 -1
- {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/db/table.py +2 -2
- {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/env/config.py +18 -11
- {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/env/db_context.py +3 -3
- {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/error.yaml +6 -0
- {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/rpc/json_rpc.py +10 -10
- {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/rpc/rpc.py +1 -1
- ab_engine-0.2.0/readme.md → ab_engine-0.2.1/ab_engine.egg-info/PKG-INFO +53 -7
- {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine.egg-info/requires.txt +1 -1
- {ab_engine-0.2.0 → ab_engine-0.2.1}/pyproject.toml +2 -2
- ab_engine-0.2.0/PKG-INFO → ab_engine-0.2.1/readme.md +39 -21
- {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/__init__.py +0 -0
- {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/class_tools.py +0 -0
- {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/db/__init__.py +0 -0
- {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/db/driver_mysql.py +0 -0
- {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/db/driver_postgresql.py +0 -0
- {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/db/driver_valkey.py +0 -0
- {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/env/__init__.py +0 -0
- {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/env/timer.py +0 -0
- {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/error.py +0 -0
- {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/rpc/__init__.py +0 -0
- {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/rpc/fnc.py +0 -0
- {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine.egg-info/SOURCES.txt +0 -0
- {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine.egg-info/dependency_links.txt +0 -0
- {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine.egg-info/top_level.txt +0 -0
- {ab_engine-0.2.0 → ab_engine-0.2.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ab_engine
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Library of standard modules
|
|
5
5
|
Author-email: Andrew Bulatov <BulatovAndrew@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -9,7 +9,7 @@ Requires-Python: >=3.10
|
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
Requires-Dist: json5>=0.12.0
|
|
11
11
|
Requires-Dist: psycopg>=3.2.9
|
|
12
|
-
Requires-Dist:
|
|
12
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
13
13
|
Requires-Dist: toml>=0.10.2
|
|
14
14
|
|
|
15
15
|
Библиотека стандартных модулей
|
|
@@ -377,12 +377,39 @@ logging:
|
|
|
377
377
|
|
|
378
378
|
Можно описывать драйвера к другим БД. Для того чтобы использовать собственный драйвер, можно добавить класс драйвера
|
|
379
379
|
в словарь DRIVER_CLASSES, который находится в модуле ab_engine/db/option, либо положить драйвер в каталог драйверов.
|
|
380
|
+
|
|
380
381
|
Пример драйвера в DRIVER_CLASSES:
|
|
382
|
+
```python
|
|
383
|
+
from ab_engine.db import sql, ONE, DB
|
|
384
|
+
from ab_engine.db.option import DRIVER_CLASSES
|
|
385
|
+
from ab_engine.db.driver_postgresql import Driver
|
|
386
|
+
import asyncio
|
|
387
|
+
|
|
388
|
+
class MyPgDriver(Driver):
|
|
389
|
+
|
|
390
|
+
async def begin(self):
|
|
391
|
+
await super().begin()
|
|
392
|
+
print("BEGIN TRANSACTION", flush=True)
|
|
393
|
+
|
|
394
|
+
async def rollback(self):
|
|
395
|
+
await super().rollback()
|
|
396
|
+
print("END TRANSACTION", flush=True)
|
|
381
397
|
|
|
382
|
-
|
|
398
|
+
DRIVER_CLASSES["my_pg"] = MyPgDriver
|
|
399
|
+
|
|
400
|
+
async def main():
|
|
401
|
+
x = await sql("select version();", ONE, DB("my_pg://postgres:postgres@localhost:5432/postgres"))
|
|
402
|
+
print(x)
|
|
403
|
+
await DB.garbage_collect()
|
|
404
|
+
|
|
405
|
+
if __name__ == '__main__':
|
|
406
|
+
asyncio.run(main())
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
При необходимости задать путь поиска с собственным драйвером, можно в строке соединения, в
|
|
383
410
|
фигурных скобках задать параметр **driver_path**, определяющий путь для поиска драйвера.
|
|
384
411
|
|
|
385
|
-
Если нужно задать свой путь поиска для всех драйверов БД, можно задать параметр db_driver_path в defaults
|
|
412
|
+
Если нужно задать свой путь поиска для всех драйверов БД, можно задать параметр **db_driver_path** в секции конфигурации **defaults**.
|
|
386
413
|
|
|
387
414
|
Если приложению требуется только одна строка соединения, то ее можно задать в переменной database, например:
|
|
388
415
|
|
|
@@ -390,7 +417,7 @@ logging:
|
|
|
390
417
|
database: postgresql://localhost:5432/postgres?user=postgres&password=postgres
|
|
391
418
|
```
|
|
392
419
|
|
|
393
|
-
|
|
420
|
+
Чтобы задать несколько соединений, нужно использовать database как секцию:
|
|
394
421
|
|
|
395
422
|
```toml
|
|
396
423
|
[database]
|
|
@@ -401,7 +428,7 @@ mydb = "postgresql://localhost:5432/mydb?user=postgres&password=postgres"
|
|
|
401
428
|
database = "test"
|
|
402
429
|
```
|
|
403
430
|
|
|
404
|
-
При этом, в секции defaults можно задать имя соединения БД, которое будет использоваться по
|
|
431
|
+
При этом, в секции **defaults** можно задать имя соединения БД, которое будет использоваться по умолчанию. Для этого следует задать значение **database** в секции **defaults**.
|
|
405
432
|
|
|
406
433
|
Строка соединения с БД имеет следующую структуру:
|
|
407
434
|
<имя_драйвера>**://**<адрес сервера>**:**<номер порта>**/**<имя БД>**?**<параметры соединения>**{**дополнительные параметры**}**
|
|
@@ -418,10 +445,15 @@ database = "test"
|
|
|
418
445
|
который ограничит максимальное количество таких соединений. Т.е. по достижении данного числа соединений система
|
|
419
446
|
не станет выполнять очередную команду до тех пор, пока не освободится одно из текущих соединений с БД.
|
|
420
447
|
|
|
421
|
-
Для некоторых
|
|
448
|
+
Для некоторых драйверов, например, для встроенного драйвера posstgresql строка сооединения с БД
|
|
422
449
|
может иметь альтернативную структуру:
|
|
423
450
|
|
|
424
|
-
<имя_драйвера>**://**<имя
|
|
451
|
+
<имя_драйвера>**://**<имя пользователя>**:**<пароль>**@**<адрес сервера>**:**<номер порта>**/**<имя БД>**?**<параметры соединения>**{**дополнительные параметры**}**
|
|
452
|
+
|
|
453
|
+
Если нужно установить **peer** соединение c БД postgresql, то в строке соединения достаточно передать имя БД и доп. параметры (если нужно). Например:
|
|
454
|
+
```python
|
|
455
|
+
x = await sql("select version()", ONE, DB("postgresql://postgres?application_name=test"))
|
|
456
|
+
```
|
|
425
457
|
|
|
426
458
|
Драйвер БД должен иметь имя driver_<имя в строке соединения>.py и предоставлять следующий контракт:
|
|
427
459
|
|
|
@@ -1220,7 +1252,7 @@ if __name__ == '__main__':
|
|
|
1220
1252
|
|
|
1221
1253
|
Методы RPC регистрируются функцией register_rpc_list либо c помощью декоратора register_rpc, который также можно использовать как функцию.
|
|
1222
1254
|
|
|
1223
|
-
Регистрация списка функций осуществляется с помощью register_rpc_list, которая принимает на вход
|
|
1255
|
+
Регистрация списка функций осуществляется с помощью register_rpc_list, которая принимает на вход параметры defs и self.
|
|
1224
1256
|
* **defs** - содержит список или словарь с описаниями
|
|
1225
1257
|
* **self** - необязательный параметр, в котором передается модуль или объект из которого будут регистрироваться функции. если в self ничего не передано, то будут регистрироваться функции из модуля, в котором вызвана register_rpc_list.
|
|
1226
1258
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
2
|
from json import dumps
|
|
3
|
-
from datetime import datetime
|
|
3
|
+
from datetime import datetime, time
|
|
4
4
|
from abc import ABC, abstractmethod
|
|
5
5
|
from collections import namedtuple
|
|
6
6
|
from typing import Optional
|
|
@@ -158,7 +158,7 @@ class Driver(ABC):
|
|
|
158
158
|
elif isinstance(var, dict):
|
|
159
159
|
var = dumps(var, ensure_ascii=False).replace("'", "''")
|
|
160
160
|
return reencode(var)
|
|
161
|
-
elif isinstance(var, datetime):
|
|
161
|
+
elif isinstance(var, (datetime, time)):
|
|
162
162
|
return f" '{var}'"
|
|
163
163
|
else:
|
|
164
164
|
return f" {var}"
|
|
@@ -19,7 +19,11 @@ _Info = namedtuple("Info", ["column_name", "data_type", "is_nullable", "characte
|
|
|
19
19
|
"numeric_precision", "numeric_scale", "column_default", "autoincrement", "pk"])
|
|
20
20
|
|
|
21
21
|
def _field_ifo_tuples(fields, str_fields):
|
|
22
|
-
str_fields = str_fields["sql"].split("\n", 1)
|
|
22
|
+
str_fields = str_fields["sql"].split("\n", 1)
|
|
23
|
+
if len(str_fields) > 1:
|
|
24
|
+
str_fields = str_fields[1][:-1].strip().split("\n")
|
|
25
|
+
else:
|
|
26
|
+
str_fields = []
|
|
23
27
|
str_def = {}
|
|
24
28
|
for x in str_fields:
|
|
25
29
|
n, x = x.strip().split(" ", 1)
|
|
@@ -30,7 +34,7 @@ def _field_ifo_tuples(fields, str_fields):
|
|
|
30
34
|
sz = int(sz)
|
|
31
35
|
else:
|
|
32
36
|
sz = None
|
|
33
|
-
ai = str_def
|
|
37
|
+
ai = str_def.get(row["name"],[])
|
|
34
38
|
fields[n] = _Info(column_name=row["name"], data_type=row["type"].lower(), character_maximum_length=sz,
|
|
35
39
|
column_default=row["dflt_value"], is_nullable=row["notnull"] == 0 and row["pk"] == 0,
|
|
36
40
|
autoincrement=" autoincrement" in ai, pk=row["pk"],
|
|
@@ -126,11 +126,15 @@ class DB(Option):
|
|
|
126
126
|
driver = DRIVER_CLASSES.get(driver_name)
|
|
127
127
|
driver_path = None
|
|
128
128
|
if conn_params:
|
|
129
|
-
conn_params = conn_params[:-1]
|
|
129
|
+
conn_params = conn_params[:-1]
|
|
130
|
+
mono_params = [x for x in conn_params.split("&") if "=" not in x ]
|
|
131
|
+
conn_params = {x[0].strip():x[1].strip() for x in [x.split("=", 1) for x in conn_params.split("&") if "=" in x]}
|
|
130
132
|
if "driver_path" in conn_params:
|
|
131
133
|
driver_path = conn_params["driver_path"]
|
|
132
134
|
del conn_params["driver_path"]
|
|
133
|
-
conn_params = "?" + "&".join(conn_params)
|
|
135
|
+
conn_params = "?" + "&".join([f"{x}={conn_params[x]}" for x in conn_params])
|
|
136
|
+
if mono_params:
|
|
137
|
+
conn_params +="&" + "&".join(mono_params)
|
|
134
138
|
connection_string += conn_params
|
|
135
139
|
if driver is None:
|
|
136
140
|
if driver_path is None and _CFG_ and _CFG_.initialized:
|
|
@@ -61,12 +61,13 @@ 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 is not None:
|
|
65
|
+
kwargs["__PARAM_CALLBACK_GETTER"] = callback
|
|
64
66
|
if parse:
|
|
65
67
|
query = await db.connection.parse_query(query, *args, **kwargs)
|
|
66
68
|
if page:
|
|
67
69
|
query = await page(db.connection, query)
|
|
68
70
|
if callback is not None:
|
|
69
|
-
kwargs["__PARAM_CALLBACK_GETTER"] = callback
|
|
70
71
|
query = await callback(query)
|
|
71
72
|
if itr:
|
|
72
73
|
if page:
|
|
@@ -193,7 +193,7 @@ class Field:
|
|
|
193
193
|
q = f"{q} where {self.table._filter}"
|
|
194
194
|
ret = await sql(q, self.table._db, ONE)
|
|
195
195
|
if self.table._auto_close_conn:
|
|
196
|
-
self.table._db.rollback()
|
|
196
|
+
await self.table._db.connection.rollback()
|
|
197
197
|
return ret
|
|
198
198
|
|
|
199
199
|
async def min(self):
|
|
@@ -648,7 +648,7 @@ class EnvTable(Table):
|
|
|
648
648
|
|
|
649
649
|
@classmethod
|
|
650
650
|
async def create(cls, table_name, env, page_size=100, async_delay=0.0001):
|
|
651
|
-
table_struct = await env.sql("\d "+table_name)
|
|
651
|
+
table_struct = await env.sql(r"\d "+table_name)
|
|
652
652
|
if not table_struct:
|
|
653
653
|
raise_error("NA_TABLE", name=table_name)
|
|
654
654
|
t = cls(table_struct, env, page_size, async_delay)
|
|
@@ -10,7 +10,7 @@ import logging as LOG
|
|
|
10
10
|
from enum import Enum
|
|
11
11
|
from .timer import TimerList
|
|
12
12
|
from ..class_tools import classproperty
|
|
13
|
-
from ..error import
|
|
13
|
+
from ..error import error, error_msg
|
|
14
14
|
import asyncio
|
|
15
15
|
|
|
16
16
|
|
|
@@ -60,7 +60,7 @@ def typed_val(key, values: dict):
|
|
|
60
60
|
case 'STR)':
|
|
61
61
|
return str(v), key
|
|
62
62
|
case 'FLOAT)':
|
|
63
|
-
return float(
|
|
63
|
+
return float(v), key
|
|
64
64
|
case _:
|
|
65
65
|
return v
|
|
66
66
|
return v, key
|
|
@@ -74,6 +74,10 @@ def from_env(key:str, key_map:dict):
|
|
|
74
74
|
eval_key = "x" + "".join(eval_key)
|
|
75
75
|
else:
|
|
76
76
|
eval_key = f"x['{key}']"
|
|
77
|
+
|
|
78
|
+
if any(char in eval_key for char in ("+", "-", "*", "/", "(", ")", "%", " ")):
|
|
79
|
+
raise error("BAD_KEY", key=eval_key)
|
|
80
|
+
|
|
77
81
|
node = eval(eval_key, {"x": Config._settings}) if key else Config._settings[key]
|
|
78
82
|
node.update(ret)
|
|
79
83
|
|
|
@@ -114,11 +118,11 @@ class Config:
|
|
|
114
118
|
case ".toml":
|
|
115
119
|
return toml_load(data)
|
|
116
120
|
case _:
|
|
117
|
-
|
|
121
|
+
raise error("BAD_FORMAT", format_name=fn)
|
|
118
122
|
|
|
119
123
|
self._log_level = LogLevel.INFO
|
|
120
124
|
if path is None:
|
|
121
|
-
|
|
125
|
+
raise error("BAD_CONFIG_PATH")
|
|
122
126
|
elif isinstance(path, dict):
|
|
123
127
|
Config._settings = path
|
|
124
128
|
elif isinstance(path, str):
|
|
@@ -133,7 +137,9 @@ class Config:
|
|
|
133
137
|
if isinstance(dest, dict):
|
|
134
138
|
dest.update(item)
|
|
135
139
|
else:
|
|
136
|
-
|
|
140
|
+
Config._settings[x] = item
|
|
141
|
+
else:
|
|
142
|
+
raise error("BAD_CONFIG_TYPE")
|
|
137
143
|
|
|
138
144
|
if not Config._settings:
|
|
139
145
|
Config._settings = {}
|
|
@@ -145,6 +151,7 @@ class Config:
|
|
|
145
151
|
f = Config._settings.get(x)
|
|
146
152
|
if isinstance(f, str):
|
|
147
153
|
Config._settings[x] = load(f)
|
|
154
|
+
self.__log_timers = {}
|
|
148
155
|
if "logging" in Config._settings:
|
|
149
156
|
self._logger = ...
|
|
150
157
|
if self.logger:
|
|
@@ -262,7 +269,7 @@ class Config:
|
|
|
262
269
|
|
|
263
270
|
def __getattr__(self, item):
|
|
264
271
|
if not(item in Config._settings):
|
|
265
|
-
|
|
272
|
+
raise error("ATTR_NOT_FOUND", name=item)
|
|
266
273
|
return Config._settings[item]
|
|
267
274
|
|
|
268
275
|
@property
|
|
@@ -359,7 +366,7 @@ class Config:
|
|
|
359
366
|
|
|
360
367
|
hndl = self.logging["handler"]["class"]
|
|
361
368
|
for x in LOG.Handler.__subclasses__():
|
|
362
|
-
if x.__name__ ==
|
|
369
|
+
if x.__name__ == hndl:
|
|
363
370
|
hndl = x
|
|
364
371
|
break
|
|
365
372
|
|
|
@@ -373,7 +380,7 @@ class Config:
|
|
|
373
380
|
hndl = x
|
|
374
381
|
break
|
|
375
382
|
if isinstance(hndl, str):
|
|
376
|
-
|
|
383
|
+
raise error("BAD_LOG_HANDLER", handler=self.logging['handler'])
|
|
377
384
|
|
|
378
385
|
self._logger = logging.getLogger(self._name)
|
|
379
386
|
|
|
@@ -407,7 +414,7 @@ class Config:
|
|
|
407
414
|
if self.logging["handler"]["class"] == "TimedRotatingFileHandler":
|
|
408
415
|
x = params.get('filename')
|
|
409
416
|
if x is None:
|
|
410
|
-
|
|
417
|
+
raise error("NA_FILE_NAME")
|
|
411
418
|
Path(x).parents[0].mkdir(parents=True, exist_ok=True)
|
|
412
419
|
with open(x, 'a'):
|
|
413
420
|
...
|
|
@@ -522,7 +529,7 @@ class Config:
|
|
|
522
529
|
|
|
523
530
|
def db_connection(self, connection:str="")->str:
|
|
524
531
|
if not Config.hasattr("database"):
|
|
525
|
-
|
|
532
|
+
raise error("NA_DB_IN_CONFIG")
|
|
526
533
|
db = self.database
|
|
527
534
|
if isinstance(db, str) and connection=="":
|
|
528
535
|
return db
|
|
@@ -530,5 +537,5 @@ class Config:
|
|
|
530
537
|
dflt = self.defaults if self.hasattr("defaults") else {}
|
|
531
538
|
db = db.get(connection, db.get(dflt.get("database", "main")))
|
|
532
539
|
if not db:
|
|
533
|
-
|
|
540
|
+
raise error("NA_DB", connection=connection)
|
|
534
541
|
return db
|
|
@@ -88,11 +88,11 @@ class DB_ENV(UserDict):
|
|
|
88
88
|
return ret.value if isinstance(ret, Property) else ret
|
|
89
89
|
|
|
90
90
|
def __setitem__(self, key, value):
|
|
91
|
-
x = self.get(key)
|
|
91
|
+
x = self.data.get(key)
|
|
92
92
|
if isinstance(x, Property):
|
|
93
93
|
x.value = value
|
|
94
94
|
else:
|
|
95
|
-
super.__setitem__(key, value)
|
|
95
|
+
super().__setitem__(key, value)
|
|
96
96
|
|
|
97
97
|
def has_item(self, key):
|
|
98
98
|
# возвращает True если в данном DB_ENV хранится переменная с именем key
|
|
@@ -140,7 +140,7 @@ class DB_ENV(UserDict):
|
|
|
140
140
|
query = await self._context.connection.parse_func(query, **kwargs)
|
|
141
141
|
elif cmd==r"\d":
|
|
142
142
|
return await self._context.connection.table_struct(query.strip())
|
|
143
|
-
elif cmd=="\json":
|
|
143
|
+
elif cmd==r"\json":
|
|
144
144
|
query, params = query.split("(", 1)
|
|
145
145
|
if params.lower() in ("json)", "jsonb)"):
|
|
146
146
|
args = (kwargs,) + args
|
|
@@ -35,6 +35,9 @@
|
|
|
35
35
|
- code: BAD_FN_PARAMS
|
|
36
36
|
class: RuntimeError
|
|
37
37
|
msg: Ошибка параметров регистрации функции {name} {defs}
|
|
38
|
+
- code: BAD_KEY
|
|
39
|
+
class: RuntimeError
|
|
40
|
+
msg: ключ не может содержать знаков операций, открывающих круглых скобок и пробелов {key}
|
|
38
41
|
### Ошибки JsonRPC
|
|
39
42
|
- code: -32700
|
|
40
43
|
msg: Ошибка разбора сообщения # "Parse error"
|
|
@@ -56,6 +59,9 @@
|
|
|
56
59
|
- code: BAD_CONFIG_PATH
|
|
57
60
|
class: AttributeError
|
|
58
61
|
msg: Не задан путь к файлу конфигурации
|
|
62
|
+
- code: BAD_CONFIG_TYPE
|
|
63
|
+
class: AttributeError
|
|
64
|
+
msg: Неизвестный формат данных конфигурации
|
|
59
65
|
- code: INFO_LOG_START
|
|
60
66
|
msg: Настройки логирования инициализированы
|
|
61
67
|
- code: ATTR_NOT_FOUND
|
|
@@ -29,19 +29,17 @@ class JSON_RPC:
|
|
|
29
29
|
if header:
|
|
30
30
|
if "id" in header:
|
|
31
31
|
code["id"] = header["id"]
|
|
32
|
-
|
|
33
|
-
header = {}
|
|
34
|
-
code["jsonrpc"] = header.get("jsonrpc", "2.0")
|
|
32
|
+
code["jsonrpc"] = header.get("jsonrpc", "2.0")
|
|
35
33
|
return code
|
|
36
34
|
|
|
37
35
|
def __init__(self, connection = None):
|
|
38
36
|
self._con = connection
|
|
39
37
|
|
|
40
|
-
async def _rpc(self, env, method,
|
|
38
|
+
async def _rpc(self, env, method, params):
|
|
41
39
|
# можно перезагрузить в наследниках, чтобы например, вызывать команды мз другого списка
|
|
42
40
|
f = Fnc.search(method)
|
|
43
41
|
if f is None:
|
|
44
|
-
return self.rpc_error(-32601
|
|
42
|
+
return self.rpc_error(-32601)
|
|
45
43
|
if isinstance(params, list):
|
|
46
44
|
params = {"LIST_OF_PARAMS": params}
|
|
47
45
|
elif not isinstance(params, dict):
|
|
@@ -59,12 +57,14 @@ class JSON_RPC:
|
|
|
59
57
|
f = message['method']
|
|
60
58
|
self.log(f"CALL:{f}")
|
|
61
59
|
try:
|
|
62
|
-
res = await self._rpc(env, f,
|
|
63
|
-
if isinstance(res,
|
|
64
|
-
|
|
60
|
+
res = await self._rpc(env, f, params)
|
|
61
|
+
if isinstance(res, dict) and len(res) == 1 and "error" in res:
|
|
62
|
+
...
|
|
63
|
+
elif isinstance(res, tuple):
|
|
64
|
+
res = {"result": list(res)}
|
|
65
65
|
elif not isinstance(res, (dict, list, str)):
|
|
66
|
-
res = str(res)
|
|
67
|
-
|
|
66
|
+
res = {"result": str(res)}
|
|
67
|
+
else:
|
|
68
68
|
res = {"result": res}
|
|
69
69
|
res["jsonrpc"]=message.get("jsonrpc", "2.0")
|
|
70
70
|
if res["jsonrpc"]=="1.0":
|
|
@@ -250,7 +250,7 @@ def register_list(defs:list | dict, self=None)->None:
|
|
|
250
250
|
else:
|
|
251
251
|
match fnc.get("type", "db"):
|
|
252
252
|
case "db":
|
|
253
|
-
SqlFnc(x, f"\JSON {fnc['function']}(JSONB)", help=fnc.get('help'))
|
|
253
|
+
SqlFnc(x, f"{r'\JSON'} {fnc['function']}(JSONB)", help=fnc.get('help'))
|
|
254
254
|
case "self":
|
|
255
255
|
fn = getattr(self, fnc['function'])
|
|
256
256
|
PythonFnc(x, fn, help = fnc.get('help') )
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ab_engine
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Library of standard modules
|
|
5
|
+
Author-email: Andrew Bulatov <BulatovAndrew@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/a-bulatov/ab_engine
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: json5>=0.12.0
|
|
11
|
+
Requires-Dist: psycopg>=3.2.9
|
|
12
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
13
|
+
Requires-Dist: toml>=0.10.2
|
|
14
|
+
|
|
1
15
|
Библиотека стандартных модулей
|
|
2
16
|
==========================================================
|
|
3
17
|
|
|
@@ -363,12 +377,39 @@ logging:
|
|
|
363
377
|
|
|
364
378
|
Можно описывать драйвера к другим БД. Для того чтобы использовать собственный драйвер, можно добавить класс драйвера
|
|
365
379
|
в словарь DRIVER_CLASSES, который находится в модуле ab_engine/db/option, либо положить драйвер в каталог драйверов.
|
|
380
|
+
|
|
366
381
|
Пример драйвера в DRIVER_CLASSES:
|
|
382
|
+
```python
|
|
383
|
+
from ab_engine.db import sql, ONE, DB
|
|
384
|
+
from ab_engine.db.option import DRIVER_CLASSES
|
|
385
|
+
from ab_engine.db.driver_postgresql import Driver
|
|
386
|
+
import asyncio
|
|
387
|
+
|
|
388
|
+
class MyPgDriver(Driver):
|
|
389
|
+
|
|
390
|
+
async def begin(self):
|
|
391
|
+
await super().begin()
|
|
392
|
+
print("BEGIN TRANSACTION", flush=True)
|
|
393
|
+
|
|
394
|
+
async def rollback(self):
|
|
395
|
+
await super().rollback()
|
|
396
|
+
print("END TRANSACTION", flush=True)
|
|
397
|
+
|
|
398
|
+
DRIVER_CLASSES["my_pg"] = MyPgDriver
|
|
367
399
|
|
|
368
|
-
|
|
400
|
+
async def main():
|
|
401
|
+
x = await sql("select version();", ONE, DB("my_pg://postgres:postgres@localhost:5432/postgres"))
|
|
402
|
+
print(x)
|
|
403
|
+
await DB.garbage_collect()
|
|
404
|
+
|
|
405
|
+
if __name__ == '__main__':
|
|
406
|
+
asyncio.run(main())
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
При необходимости задать путь поиска с собственным драйвером, можно в строке соединения, в
|
|
369
410
|
фигурных скобках задать параметр **driver_path**, определяющий путь для поиска драйвера.
|
|
370
411
|
|
|
371
|
-
Если нужно задать свой путь поиска для всех драйверов БД, можно задать параметр db_driver_path в defaults
|
|
412
|
+
Если нужно задать свой путь поиска для всех драйверов БД, можно задать параметр **db_driver_path** в секции конфигурации **defaults**.
|
|
372
413
|
|
|
373
414
|
Если приложению требуется только одна строка соединения, то ее можно задать в переменной database, например:
|
|
374
415
|
|
|
@@ -376,7 +417,7 @@ logging:
|
|
|
376
417
|
database: postgresql://localhost:5432/postgres?user=postgres&password=postgres
|
|
377
418
|
```
|
|
378
419
|
|
|
379
|
-
|
|
420
|
+
Чтобы задать несколько соединений, нужно использовать database как секцию:
|
|
380
421
|
|
|
381
422
|
```toml
|
|
382
423
|
[database]
|
|
@@ -387,7 +428,7 @@ mydb = "postgresql://localhost:5432/mydb?user=postgres&password=postgres"
|
|
|
387
428
|
database = "test"
|
|
388
429
|
```
|
|
389
430
|
|
|
390
|
-
При этом, в секции defaults можно задать имя соединения БД, которое будет использоваться по
|
|
431
|
+
При этом, в секции **defaults** можно задать имя соединения БД, которое будет использоваться по умолчанию. Для этого следует задать значение **database** в секции **defaults**.
|
|
391
432
|
|
|
392
433
|
Строка соединения с БД имеет следующую структуру:
|
|
393
434
|
<имя_драйвера>**://**<адрес сервера>**:**<номер порта>**/**<имя БД>**?**<параметры соединения>**{**дополнительные параметры**}**
|
|
@@ -404,10 +445,15 @@ database = "test"
|
|
|
404
445
|
который ограничит максимальное количество таких соединений. Т.е. по достижении данного числа соединений система
|
|
405
446
|
не станет выполнять очередную команду до тех пор, пока не освободится одно из текущих соединений с БД.
|
|
406
447
|
|
|
407
|
-
Для некоторых
|
|
448
|
+
Для некоторых драйверов, например, для встроенного драйвера posstgresql строка сооединения с БД
|
|
408
449
|
может иметь альтернативную структуру:
|
|
409
450
|
|
|
410
|
-
<имя_драйвера>**://**<имя
|
|
451
|
+
<имя_драйвера>**://**<имя пользователя>**:**<пароль>**@**<адрес сервера>**:**<номер порта>**/**<имя БД>**?**<параметры соединения>**{**дополнительные параметры**}**
|
|
452
|
+
|
|
453
|
+
Если нужно установить **peer** соединение c БД postgresql, то в строке соединения достаточно передать имя БД и доп. параметры (если нужно). Например:
|
|
454
|
+
```python
|
|
455
|
+
x = await sql("select version()", ONE, DB("postgresql://postgres?application_name=test"))
|
|
456
|
+
```
|
|
411
457
|
|
|
412
458
|
Драйвер БД должен иметь имя driver_<имя в строке соединения>.py и предоставлять следующий контракт:
|
|
413
459
|
|
|
@@ -1206,7 +1252,7 @@ if __name__ == '__main__':
|
|
|
1206
1252
|
|
|
1207
1253
|
Методы RPC регистрируются функцией register_rpc_list либо c помощью декоратора register_rpc, который также можно использовать как функцию.
|
|
1208
1254
|
|
|
1209
|
-
Регистрация списка функций осуществляется с помощью register_rpc_list, которая принимает на вход
|
|
1255
|
+
Регистрация списка функций осуществляется с помощью register_rpc_list, которая принимает на вход параметры defs и self.
|
|
1210
1256
|
* **defs** - содержит список или словарь с описаниями
|
|
1211
1257
|
* **self** - необязательный параметр, в котором передается модуль или объект из которого будут регистрироваться функции. если в self ничего не передано, то будут регистрироваться функции из модуля, в котором вызвана register_rpc_list.
|
|
1212
1258
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "ab_engine"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.1"
|
|
4
4
|
description = "Library of standard modules"
|
|
5
5
|
authors = [{name = "Andrew Bulatov", email = "BulatovAndrew@gmail.com"}]
|
|
6
6
|
license = "MIT"
|
|
@@ -9,7 +9,7 @@ requires-python = ">=3.10"
|
|
|
9
9
|
dependencies = [
|
|
10
10
|
"json5>=0.12.0",
|
|
11
11
|
"psycopg>=3.2.9",
|
|
12
|
-
"
|
|
12
|
+
"pyyaml>=6.0.3",
|
|
13
13
|
"toml>=0.10.2",
|
|
14
14
|
]
|
|
15
15
|
[project.urls]
|
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: ab_engine
|
|
3
|
-
Version: 0.2.0
|
|
4
|
-
Summary: Library of standard modules
|
|
5
|
-
Author-email: Andrew Bulatov <BulatovAndrew@gmail.com>
|
|
6
|
-
License-Expression: MIT
|
|
7
|
-
Project-URL: Repository, https://github.com/a-bulatov/ab_engine
|
|
8
|
-
Requires-Python: >=3.10
|
|
9
|
-
Description-Content-Type: text/markdown
|
|
10
|
-
Requires-Dist: json5>=0.12.0
|
|
11
|
-
Requires-Dist: psycopg>=3.2.9
|
|
12
|
-
Requires-Dist: pyaml>=25.7.0
|
|
13
|
-
Requires-Dist: toml>=0.10.2
|
|
14
|
-
|
|
15
1
|
Библиотека стандартных модулей
|
|
16
2
|
==========================================================
|
|
17
3
|
|
|
@@ -377,12 +363,39 @@ logging:
|
|
|
377
363
|
|
|
378
364
|
Можно описывать драйвера к другим БД. Для того чтобы использовать собственный драйвер, можно добавить класс драйвера
|
|
379
365
|
в словарь DRIVER_CLASSES, который находится в модуле ab_engine/db/option, либо положить драйвер в каталог драйверов.
|
|
366
|
+
|
|
380
367
|
Пример драйвера в DRIVER_CLASSES:
|
|
368
|
+
```python
|
|
369
|
+
from ab_engine.db import sql, ONE, DB
|
|
370
|
+
from ab_engine.db.option import DRIVER_CLASSES
|
|
371
|
+
from ab_engine.db.driver_postgresql import Driver
|
|
372
|
+
import asyncio
|
|
373
|
+
|
|
374
|
+
class MyPgDriver(Driver):
|
|
375
|
+
|
|
376
|
+
async def begin(self):
|
|
377
|
+
await super().begin()
|
|
378
|
+
print("BEGIN TRANSACTION", flush=True)
|
|
379
|
+
|
|
380
|
+
async def rollback(self):
|
|
381
|
+
await super().rollback()
|
|
382
|
+
print("END TRANSACTION", flush=True)
|
|
383
|
+
|
|
384
|
+
DRIVER_CLASSES["my_pg"] = MyPgDriver
|
|
381
385
|
|
|
382
|
-
|
|
386
|
+
async def main():
|
|
387
|
+
x = await sql("select version();", ONE, DB("my_pg://postgres:postgres@localhost:5432/postgres"))
|
|
388
|
+
print(x)
|
|
389
|
+
await DB.garbage_collect()
|
|
390
|
+
|
|
391
|
+
if __name__ == '__main__':
|
|
392
|
+
asyncio.run(main())
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
При необходимости задать путь поиска с собственным драйвером, можно в строке соединения, в
|
|
383
396
|
фигурных скобках задать параметр **driver_path**, определяющий путь для поиска драйвера.
|
|
384
397
|
|
|
385
|
-
Если нужно задать свой путь поиска для всех драйверов БД, можно задать параметр db_driver_path в defaults
|
|
398
|
+
Если нужно задать свой путь поиска для всех драйверов БД, можно задать параметр **db_driver_path** в секции конфигурации **defaults**.
|
|
386
399
|
|
|
387
400
|
Если приложению требуется только одна строка соединения, то ее можно задать в переменной database, например:
|
|
388
401
|
|
|
@@ -390,7 +403,7 @@ logging:
|
|
|
390
403
|
database: postgresql://localhost:5432/postgres?user=postgres&password=postgres
|
|
391
404
|
```
|
|
392
405
|
|
|
393
|
-
|
|
406
|
+
Чтобы задать несколько соединений, нужно использовать database как секцию:
|
|
394
407
|
|
|
395
408
|
```toml
|
|
396
409
|
[database]
|
|
@@ -401,7 +414,7 @@ mydb = "postgresql://localhost:5432/mydb?user=postgres&password=postgres"
|
|
|
401
414
|
database = "test"
|
|
402
415
|
```
|
|
403
416
|
|
|
404
|
-
При этом, в секции defaults можно задать имя соединения БД, которое будет использоваться по
|
|
417
|
+
При этом, в секции **defaults** можно задать имя соединения БД, которое будет использоваться по умолчанию. Для этого следует задать значение **database** в секции **defaults**.
|
|
405
418
|
|
|
406
419
|
Строка соединения с БД имеет следующую структуру:
|
|
407
420
|
<имя_драйвера>**://**<адрес сервера>**:**<номер порта>**/**<имя БД>**?**<параметры соединения>**{**дополнительные параметры**}**
|
|
@@ -418,10 +431,15 @@ database = "test"
|
|
|
418
431
|
который ограничит максимальное количество таких соединений. Т.е. по достижении данного числа соединений система
|
|
419
432
|
не станет выполнять очередную команду до тех пор, пока не освободится одно из текущих соединений с БД.
|
|
420
433
|
|
|
421
|
-
Для некоторых
|
|
434
|
+
Для некоторых драйверов, например, для встроенного драйвера posstgresql строка сооединения с БД
|
|
422
435
|
может иметь альтернативную структуру:
|
|
423
436
|
|
|
424
|
-
<имя_драйвера>**://**<имя
|
|
437
|
+
<имя_драйвера>**://**<имя пользователя>**:**<пароль>**@**<адрес сервера>**:**<номер порта>**/**<имя БД>**?**<параметры соединения>**{**дополнительные параметры**}**
|
|
438
|
+
|
|
439
|
+
Если нужно установить **peer** соединение c БД postgresql, то в строке соединения достаточно передать имя БД и доп. параметры (если нужно). Например:
|
|
440
|
+
```python
|
|
441
|
+
x = await sql("select version()", ONE, DB("postgresql://postgres?application_name=test"))
|
|
442
|
+
```
|
|
425
443
|
|
|
426
444
|
Драйвер БД должен иметь имя driver_<имя в строке соединения>.py и предоставлять следующий контракт:
|
|
427
445
|
|
|
@@ -1220,7 +1238,7 @@ if __name__ == '__main__':
|
|
|
1220
1238
|
|
|
1221
1239
|
Методы RPC регистрируются функцией register_rpc_list либо c помощью декоратора register_rpc, который также можно использовать как функцию.
|
|
1222
1240
|
|
|
1223
|
-
Регистрация списка функций осуществляется с помощью register_rpc_list, которая принимает на вход
|
|
1241
|
+
Регистрация списка функций осуществляется с помощью register_rpc_list, которая принимает на вход параметры defs и self.
|
|
1224
1242
|
* **defs** - содержит список или словарь с описаниями
|
|
1225
1243
|
* **self** - необязательный параметр, в котором передается модуль или объект из которого будут регистрироваться функции. если в self ничего не передано, то будут регистрироваться функции из модуля, в котором вызвана register_rpc_list.
|
|
1226
1244
|
|
|
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
|