ab_engine 0.2.3__tar.gz → 0.2.5__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.3 → ab_engine-0.2.5}/PKG-INFO +1 -2
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine/class_tools.py +7 -9
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine/db/driver.py +19 -5
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine/db/option.py +8 -14
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine/db/table.py +25 -20
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine/env/config.py +12 -3
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine/env/db_context.py +1 -1
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine/rpc/json_rpc.py +1 -0
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine.egg-info/PKG-INFO +1 -2
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine.egg-info/requires.txt +0 -1
- {ab_engine-0.2.3 → ab_engine-0.2.5}/pyproject.toml +2 -3
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine/__init__.py +0 -0
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine/db/__init__.py +0 -0
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine/db/driver_mysql.py +0 -0
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine/db/driver_postgresql.py +0 -0
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine/db/driver_sqlite.py +0 -0
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine/db/driver_valkey.py +0 -0
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine/db/processor.py +0 -0
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine/env/__init__.py +0 -0
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine/env/timer.py +0 -0
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine/error.py +0 -0
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine/error.yaml +0 -0
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine/rpc/__init__.py +0 -0
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine/rpc/fnc.py +0 -0
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine/rpc/rpc.py +0 -0
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine.egg-info/SOURCES.txt +0 -0
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine.egg-info/dependency_links.txt +0 -0
- {ab_engine-0.2.3 → ab_engine-0.2.5}/ab_engine.egg-info/top_level.txt +0 -0
- {ab_engine-0.2.3 → ab_engine-0.2.5}/readme.md +0 -0
- {ab_engine-0.2.3 → ab_engine-0.2.5}/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.5
|
|
4
4
|
Summary: Library of standard modules
|
|
5
5
|
Author-email: Andrew Bulatov <BulatovAndrew@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -10,7 +10,6 @@ Description-Content-Type: text/markdown
|
|
|
10
10
|
Requires-Dist: json5>=0.12.0
|
|
11
11
|
Requires-Dist: psycopg>=3.2.9
|
|
12
12
|
Requires-Dist: pyyaml>=6.0.3
|
|
13
|
-
Requires-Dist: toml>=0.10.2
|
|
14
13
|
|
|
15
14
|
Библиотека стандартных модулей
|
|
16
15
|
==========================================================
|
|
@@ -41,19 +41,17 @@ class ReadOnlyPropDict:
|
|
|
41
41
|
|
|
42
42
|
def __init__(self, **kwargs):
|
|
43
43
|
self._data = kwargs
|
|
44
|
-
for x in kwargs:
|
|
45
|
-
self._set_f(x)
|
|
46
|
-
|
|
47
|
-
def _set_f(self, x):
|
|
48
|
-
setattr(self.__class__, x, property(lambda p: self._data[x]))
|
|
49
44
|
|
|
50
45
|
def __getitem__(self, item):
|
|
51
46
|
return self._data[item]
|
|
52
47
|
|
|
48
|
+
def __getattr__(self, item):
|
|
49
|
+
return self._data[item]
|
|
50
|
+
|
|
53
51
|
|
|
54
52
|
class PropDict(ReadOnlyPropDict):
|
|
55
53
|
|
|
56
|
-
def
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
def __setattr__(self, name, value):
|
|
55
|
+
if name not in self._data:
|
|
56
|
+
raise KeyError(name)
|
|
57
|
+
self._data[name] = value
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
2
|
from json import dumps
|
|
3
|
-
from datetime import datetime, time
|
|
4
3
|
from abc import ABC, abstractmethod
|
|
5
4
|
from collections import namedtuple
|
|
6
5
|
from typing import Optional
|
|
7
6
|
from urllib.parse import unquote
|
|
8
7
|
from inspect import iscoroutinefunction
|
|
8
|
+
from uuid import UUID
|
|
9
|
+
import datetime, uuid
|
|
9
10
|
|
|
10
11
|
_is_option = None
|
|
11
12
|
|
|
@@ -32,6 +33,17 @@ def reencode(s):
|
|
|
32
33
|
return s
|
|
33
34
|
|
|
34
35
|
|
|
36
|
+
def in_arr_str(s):
|
|
37
|
+
if not isinstance(s, str):
|
|
38
|
+
return str(s)
|
|
39
|
+
s = s.replace('"','\\"')
|
|
40
|
+
as_is = True
|
|
41
|
+
for x in s:
|
|
42
|
+
if x in '{} ,"\\;':
|
|
43
|
+
as_is = False
|
|
44
|
+
break
|
|
45
|
+
return s if as_is else f'"{s}"'
|
|
46
|
+
|
|
35
47
|
class Driver(ABC):
|
|
36
48
|
|
|
37
49
|
LIKE = "LIKE" # что использовать как like, например для postgresql это ILIKE
|
|
@@ -142,6 +154,8 @@ class Driver(ABC):
|
|
|
142
154
|
def var_to_sql(var, str_after=" "):
|
|
143
155
|
if var is None:
|
|
144
156
|
return " NULL"
|
|
157
|
+
elif isinstance(var, (int, float, bool)):
|
|
158
|
+
return f" {var}"
|
|
145
159
|
elif isinstance(var, str):
|
|
146
160
|
var = var.replace("'", "''")
|
|
147
161
|
return reencode(var)
|
|
@@ -150,15 +164,15 @@ class Driver(ABC):
|
|
|
150
164
|
var = dumps(var, ensure_ascii=False).replace("'", "''")
|
|
151
165
|
return reencode(var)
|
|
152
166
|
else:
|
|
153
|
-
var = ','.join(['NULL' if x is None else
|
|
154
|
-
return reencode(f"{{{var}}}")
|
|
167
|
+
var = ','.join(['NULL' if x is None else in_arr_str(x) for x in var])
|
|
168
|
+
return reencode(f"{{{var.replace("'", "''")}}}")
|
|
155
169
|
elif isinstance(var, set):
|
|
156
|
-
var = ','.join('NULL' if x is None else
|
|
170
|
+
var = ','.join('NULL' if x is None else in_arr_str(x) for x in var)
|
|
157
171
|
return reencode(f"{{{var}}}")
|
|
158
172
|
elif isinstance(var, dict):
|
|
159
173
|
var = dumps(var, ensure_ascii=False).replace("'", "''")
|
|
160
174
|
return reencode(var)
|
|
161
|
-
elif
|
|
175
|
+
elif hasattr(datetime, var.__class__.__name__) or isinstance(var, UUID):
|
|
162
176
|
return f" '{var}'"
|
|
163
177
|
else:
|
|
164
178
|
return f" {var}"
|
|
@@ -113,6 +113,7 @@ class DB(Option):
|
|
|
113
113
|
self._params[x.replace("$",".")] = v
|
|
114
114
|
else:
|
|
115
115
|
self._params = {}
|
|
116
|
+
self._hash = hash(connection_string)
|
|
116
117
|
if "://" not in connection_string:
|
|
117
118
|
try:
|
|
118
119
|
_check_cfg()
|
|
@@ -155,14 +156,10 @@ class DB(Option):
|
|
|
155
156
|
else:
|
|
156
157
|
driver_path = ""
|
|
157
158
|
|
|
158
|
-
self._hash = hash(driver_path + connection_string)
|
|
159
159
|
if "LIMIT" in self._params:
|
|
160
160
|
m = self._params["LIMIT"]
|
|
161
161
|
del self._params["LIMIT"]
|
|
162
|
-
|
|
163
|
-
m = 0
|
|
164
|
-
self._conn_limit = None
|
|
165
|
-
self.connection_limit = m
|
|
162
|
+
self.connection_limit = m
|
|
166
163
|
self._connection = driver(connection_string, self._on_open_close, notify)
|
|
167
164
|
|
|
168
165
|
@property
|
|
@@ -192,21 +189,18 @@ class DB(Option):
|
|
|
192
189
|
else:
|
|
193
190
|
lmt = BoundedSemaphore(value)
|
|
194
191
|
_LIMITS_[self._hash] = lmt
|
|
195
|
-
self._conn_limit = lmt
|
|
196
192
|
|
|
197
193
|
|
|
198
194
|
async def _on_open_close(self, close=False)->dict:
|
|
199
|
-
if self.
|
|
195
|
+
if lmt:=_LIMITS_.get(self._hash):
|
|
200
196
|
if close:
|
|
201
|
-
|
|
202
|
-
if self._hash not in _LIMITS_:
|
|
203
|
-
self._conn_limit = None
|
|
204
|
-
elif self._hash not in _LIMITS_:
|
|
205
|
-
self._conn_limit = None
|
|
197
|
+
lmt.release()
|
|
206
198
|
else:
|
|
207
|
-
if
|
|
199
|
+
if lmt.locked():
|
|
208
200
|
await self.garbage_collect()
|
|
209
|
-
|
|
201
|
+
while lmt.locked():
|
|
202
|
+
await sleep(0.0001)
|
|
203
|
+
await lmt.acquire()
|
|
210
204
|
return self._params
|
|
211
205
|
|
|
212
206
|
@property
|
|
@@ -4,22 +4,6 @@ from ..error import raise_error
|
|
|
4
4
|
from .processor import sql
|
|
5
5
|
import asyncio
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
class ROW(ReadOnlyPropDict):
|
|
9
|
-
|
|
10
|
-
def __str__(self):
|
|
11
|
-
s = ""
|
|
12
|
-
for x in self._data:
|
|
13
|
-
v = str(self._data[x].value).replace("\t"," ")
|
|
14
|
-
s += f" {x}: {v}" + "\t"
|
|
15
|
-
return s[:-1]
|
|
16
|
-
|
|
17
|
-
def __getitem__(self, item):
|
|
18
|
-
if item not in self._data:
|
|
19
|
-
raise_error("FIELD_NOT_FOUND", name=item)
|
|
20
|
-
return self._data[item]
|
|
21
|
-
|
|
22
|
-
|
|
23
7
|
class Field:
|
|
24
8
|
|
|
25
9
|
class _Condition:
|
|
@@ -42,7 +26,8 @@ class Field:
|
|
|
42
26
|
if isinstance(right, Field):
|
|
43
27
|
if right._table == left._table:
|
|
44
28
|
right = Field._Condition.Name(right.name)
|
|
45
|
-
|
|
29
|
+
else:
|
|
30
|
+
right = right.value
|
|
46
31
|
if not isinstance(left, Field):
|
|
47
32
|
raise_error("NOT_LFIELD")
|
|
48
33
|
elif op in ("%", "//"):
|
|
@@ -222,6 +207,20 @@ class Table:
|
|
|
222
207
|
class _FILTER(ALL):
|
|
223
208
|
...
|
|
224
209
|
|
|
210
|
+
class ROW(ReadOnlyPropDict):
|
|
211
|
+
|
|
212
|
+
def __str__(self):
|
|
213
|
+
s = ""
|
|
214
|
+
for x in self._data:
|
|
215
|
+
v = str(self._data[x].value).replace("\t"," ")
|
|
216
|
+
s += f" {x}: {v}" + "\t"
|
|
217
|
+
return s[:-1]
|
|
218
|
+
|
|
219
|
+
def __getitem__(self, item):
|
|
220
|
+
if item not in self._data:
|
|
221
|
+
raise_error("FIELD_NOT_FOUND", name=item)
|
|
222
|
+
return self._data[item]
|
|
223
|
+
|
|
225
224
|
def __init__(self, table_struct:dict, db:DB, page_size=100, auto_close_conn=True, async_delay=0.001):
|
|
226
225
|
self._table = table_struct["table"]
|
|
227
226
|
self._db = db
|
|
@@ -233,7 +232,7 @@ class Table:
|
|
|
233
232
|
self._offset = 0
|
|
234
233
|
self._page = None
|
|
235
234
|
self._filter = ""
|
|
236
|
-
self._row = ROW(**{x["name"]:Field(x, self) for x in table_struct.get("fields", [])})
|
|
235
|
+
self._row = Table.ROW(**{x["name"]:Field(x, self) for x in table_struct.get("fields", [])})
|
|
237
236
|
self._key = 1
|
|
238
237
|
constraint = table_struct.get("constraints")
|
|
239
238
|
for x in constraint:
|
|
@@ -329,7 +328,6 @@ class Table:
|
|
|
329
328
|
"""строка, на которой стоит курсор (счет с 0)"""
|
|
330
329
|
return self._offset + (self._ptr or 0)
|
|
331
330
|
|
|
332
|
-
|
|
333
331
|
async def seek(self, *args, **kwargs):
|
|
334
332
|
if kwargs or len(args) != 1 or (len(args) > 1 and not isinstance(args[0], int)):
|
|
335
333
|
if self._filter:
|
|
@@ -371,6 +369,10 @@ class Table:
|
|
|
371
369
|
self._offset = need_ofs
|
|
372
370
|
return True
|
|
373
371
|
|
|
372
|
+
async def refresh(self):
|
|
373
|
+
pos = self.position
|
|
374
|
+
await self.seek(pos)
|
|
375
|
+
|
|
374
376
|
async def next(self):
|
|
375
377
|
"""
|
|
376
378
|
переход на следующую запись
|
|
@@ -482,7 +484,7 @@ class Table:
|
|
|
482
484
|
if x not in nms:
|
|
483
485
|
raise_error("NA_FIELD", field=x, table=self._table)
|
|
484
486
|
if qry:
|
|
485
|
-
qry += "and"
|
|
487
|
+
qry += " and "
|
|
486
488
|
qry += f"{x} = {Field._Condition._lquot(self.row._data[x]._p_type, kwargs[x])}"
|
|
487
489
|
return qry
|
|
488
490
|
|
|
@@ -573,6 +575,8 @@ class Table:
|
|
|
573
575
|
"""
|
|
574
576
|
if args or kwargs:
|
|
575
577
|
qry = self._gen_filter(*args, **kwargs)
|
|
578
|
+
elif self.key and isinstance(self.key, str):
|
|
579
|
+
qry = {self.key:self.row[self.key].value}
|
|
576
580
|
elif self.key:
|
|
577
581
|
qry = {x:self.row[x].value for x in self.key}
|
|
578
582
|
else:
|
|
@@ -598,6 +602,7 @@ class Table:
|
|
|
598
602
|
while n < len(args):
|
|
599
603
|
if args[n]==ALL:
|
|
600
604
|
fltr = None
|
|
605
|
+
args = list(args)
|
|
601
606
|
del args[n]
|
|
602
607
|
else:
|
|
603
608
|
n += 1
|
|
@@ -3,7 +3,10 @@ from datetime import datetime
|
|
|
3
3
|
from json import dumps, loads
|
|
4
4
|
from json5 import loads as json5_load
|
|
5
5
|
from yaml import safe_load as yaml_load
|
|
6
|
-
|
|
6
|
+
try:
|
|
7
|
+
from tomllib import loads as toml_load
|
|
8
|
+
except:
|
|
9
|
+
toml_load = None
|
|
7
10
|
from pathlib import Path
|
|
8
11
|
from os import environ as ENV
|
|
9
12
|
import logging as LOG
|
|
@@ -69,7 +72,11 @@ def typed_val(key, values: dict):
|
|
|
69
72
|
return v, key
|
|
70
73
|
|
|
71
74
|
|
|
72
|
-
def from_env(key:str, key_map:dict):
|
|
75
|
+
def from_env(key:str, key_map:dict | str):
|
|
76
|
+
if isinstance(key_map, str):
|
|
77
|
+
if key in ENV:
|
|
78
|
+
Config._settings[key_map] = ENV[key]
|
|
79
|
+
return
|
|
73
80
|
ret = {key_map[x]: ENV[x] for x in key_map if x in ENV}
|
|
74
81
|
if key and "." in key:
|
|
75
82
|
eval_key = key.split(".")
|
|
@@ -100,7 +107,7 @@ class Config:
|
|
|
100
107
|
def initialized(cls):
|
|
101
108
|
return cls._settings is not None
|
|
102
109
|
|
|
103
|
-
def __init__(self, path: Optional[str | list[str] | dict] = None, env_map: Optional[dict] = None, can_include: Optional[
|
|
110
|
+
def __init__(self, path: Optional[str | list[str] | dict] = None, env_map: Optional[dict] = None, can_include: Optional[set]=None) -> None:
|
|
104
111
|
if Config.initialized:
|
|
105
112
|
return
|
|
106
113
|
|
|
@@ -119,6 +126,8 @@ class Config:
|
|
|
119
126
|
case ".yaml" | ".yml":
|
|
120
127
|
return yaml_load(data)
|
|
121
128
|
case ".toml":
|
|
129
|
+
if toml_load is None:
|
|
130
|
+
raise error("BAD_FORMAT", format_name=fn)
|
|
122
131
|
return toml_load(data)
|
|
123
132
|
case _:
|
|
124
133
|
raise error("BAD_FORMAT", format_name=fn)
|
|
@@ -39,7 +39,7 @@ class DB_ENV(UserDict):
|
|
|
39
39
|
if isinstance(connection, DB_ENV):
|
|
40
40
|
env = connection
|
|
41
41
|
self._db_params = env._db_params.copy()
|
|
42
|
-
params = env.
|
|
42
|
+
params = env.data.copy()
|
|
43
43
|
connection = env.connection_string
|
|
44
44
|
elif isinstance(connection, DB):
|
|
45
45
|
connection = DB.connection.connection_string
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ab_engine
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5
|
|
4
4
|
Summary: Library of standard modules
|
|
5
5
|
Author-email: Andrew Bulatov <BulatovAndrew@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -10,7 +10,6 @@ Description-Content-Type: text/markdown
|
|
|
10
10
|
Requires-Dist: json5>=0.12.0
|
|
11
11
|
Requires-Dist: psycopg>=3.2.9
|
|
12
12
|
Requires-Dist: pyyaml>=6.0.3
|
|
13
|
-
Requires-Dist: toml>=0.10.2
|
|
14
13
|
|
|
15
14
|
Библиотека стандартных модулей
|
|
16
15
|
==========================================================
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "ab_engine"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.5"
|
|
4
4
|
description = "Library of standard modules"
|
|
5
5
|
authors = [{name = "Andrew Bulatov", email = "BulatovAndrew@gmail.com"}]
|
|
6
6
|
license = "MIT"
|
|
@@ -9,8 +9,7 @@ requires-python = ">=3.10"
|
|
|
9
9
|
dependencies = [
|
|
10
10
|
"json5>=0.12.0",
|
|
11
11
|
"psycopg>=3.2.9",
|
|
12
|
-
"pyyaml>=6.0.3"
|
|
13
|
-
"toml>=0.10.2",
|
|
12
|
+
"pyyaml>=6.0.3"
|
|
14
13
|
]
|
|
15
14
|
[project.urls]
|
|
16
15
|
Repository = "https://github.com/a-bulatov/ab_engine"
|
|
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
|
|
File without changes
|
|
File without changes
|