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.
Files changed (30) hide show
  1. {ab_engine-0.2.0/ab_engine.egg-info → ab_engine-0.2.1}/PKG-INFO +41 -9
  2. {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/db/driver.py +2 -2
  3. {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/db/driver_sqlite.py +6 -2
  4. {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/db/option.py +6 -2
  5. {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/db/processor.py +2 -1
  6. {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/db/table.py +2 -2
  7. {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/env/config.py +18 -11
  8. {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/env/db_context.py +3 -3
  9. {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/error.yaml +6 -0
  10. {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/rpc/json_rpc.py +10 -10
  11. {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/rpc/rpc.py +1 -1
  12. ab_engine-0.2.0/readme.md → ab_engine-0.2.1/ab_engine.egg-info/PKG-INFO +53 -7
  13. {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine.egg-info/requires.txt +1 -1
  14. {ab_engine-0.2.0 → ab_engine-0.2.1}/pyproject.toml +2 -2
  15. ab_engine-0.2.0/PKG-INFO → ab_engine-0.2.1/readme.md +39 -21
  16. {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/__init__.py +0 -0
  17. {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/class_tools.py +0 -0
  18. {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/db/__init__.py +0 -0
  19. {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/db/driver_mysql.py +0 -0
  20. {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/db/driver_postgresql.py +0 -0
  21. {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/db/driver_valkey.py +0 -0
  22. {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/env/__init__.py +0 -0
  23. {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/env/timer.py +0 -0
  24. {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/error.py +0 -0
  25. {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/rpc/__init__.py +0 -0
  26. {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine/rpc/fnc.py +0 -0
  27. {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine.egg-info/SOURCES.txt +0 -0
  28. {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine.egg-info/dependency_links.txt +0 -0
  29. {ab_engine-0.2.0 → ab_engine-0.2.1}/ab_engine.egg-info/top_level.txt +0 -0
  30. {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.0
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: pyaml>=25.7.0
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
- Также можно задать несколько соединений, если использовать database как секцию:
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
- Для некоторых драйверо, например, для встроенного драйвера posstgresql строка сооединения с БД
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, которая принимает на вход парамектры defs и self.
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)[1][:-1].strip().split("\n")
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[row["name"]]
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].split("&")
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 raise_error, error_msg
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(t), key
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
- raise_error("BAD_FORMAT", format_name=fn)
121
+ raise error("BAD_FORMAT", format_name=fn)
118
122
 
119
123
  self._log_level = LogLevel.INFO
120
124
  if path is None:
121
- raise_error("BAD_CONFIG_PATH")
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
- data[x] = item
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
- raise_error("ATTR_NOT_FOUND", name=item)
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__ == self.logging["handler"]:
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
- raise_error("BAD_LOG_HANDLER", handler=self.logging['handler'])
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
- raise_error("NA_FILE_NAME")
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
- raise_error("NA_DB_IN_CONFIG")
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
- raise_error("NA_DB", connection=connection)
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
- else:
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, header, params):
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, header=header)
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, message, params)
63
- if isinstance(res, tuple):
64
- res = list(res)
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
- elif not (isinstance(res, dict) and len(res) == 1 and "error" in res):
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
- Также можно задать несколько соединений, если использовать database как секцию:
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
- Для некоторых драйверо, например, для встроенного драйвера posstgresql строка сооединения с БД
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, которая принимает на вход парамектры defs и self.
1255
+ Регистрация списка функций осуществляется с помощью register_rpc_list, которая принимает на вход параметры defs и self.
1210
1256
  * **defs** - содержит список или словарь с описаниями
1211
1257
  * **self** - необязательный параметр, в котором передается модуль или объект из которого будут регистрироваться функции. если в self ничего не передано, то будут регистрироваться функции из модуля, в котором вызвана register_rpc_list.
1212
1258
 
@@ -1,4 +1,4 @@
1
1
  json5>=0.12.0
2
2
  psycopg>=3.2.9
3
- pyaml>=25.7.0
3
+ pyyaml>=6.0.3
4
4
  toml>=0.10.2
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ab_engine"
3
- version = "0.2.0"
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
- "pyaml>=25.7.0",
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
- Также можно задать несколько соединений, если использовать database как секцию:
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
- Для некоторых драйверо, например, для встроенного драйвера posstgresql строка сооединения с БД
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, которая принимает на вход парамектры defs и self.
1241
+ Регистрация списка функций осуществляется с помощью register_rpc_list, которая принимает на вход параметры defs и self.
1224
1242
  * **defs** - содержит список или словарь с описаниями
1225
1243
  * **self** - необязательный параметр, в котором передается модуль или объект из которого будут регистрироваться функции. если в self ничего не передано, то будут регистрироваться функции из модуля, в котором вызвана register_rpc_list.
1226
1244
 
File without changes
File without changes