reydb 1.3.61__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.
@@ -0,0 +1,10 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
@@ -0,0 +1 @@
1
+ 3.13
reydb-1.3.61/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2025 ReyXBo
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
reydb-1.3.61/PKG-INFO ADDED
@@ -0,0 +1,34 @@
1
+ Metadata-Version: 2.4
2
+ Name: reydb
3
+ Version: 1.3.61
4
+ Summary: Database method set.
5
+ Project-URL: Homepage, https://www.reyxbo.com
6
+ Project-URL: Repository, https://github.com/reyxbo/reydb-py.git
7
+ Author-email: Rey <reyxbo@163.com>
8
+ License: Copyright 2025 ReyXBo
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15
+ License-File: LICENSE
16
+ Keywords: PostgreSQL,async,asynchronous,database,db,orm,postgres,rey,reyxbo
17
+ Requires-Python: >=3.12
18
+ Requires-Dist: asyncpg
19
+ Requires-Dist: psycopg[binary]
20
+ Requires-Dist: pydantic[email]
21
+ Requires-Dist: reykit
22
+ Requires-Dist: sqlalchemy==2.0.42
23
+ Requires-Dist: sqlmodel
24
+ Description-Content-Type: text/markdown
25
+
26
+ # reydb
27
+
28
+ > Backend database method set.
29
+
30
+ ## Install
31
+
32
+ ```
33
+ pip install reydb
34
+ ```
reydb-1.3.61/README.md ADDED
@@ -0,0 +1,9 @@
1
+ # reydb
2
+
3
+ > Backend database method set.
4
+
5
+ ## Install
6
+
7
+ ```
8
+ pip install reydb
9
+ ```
@@ -0,0 +1,27 @@
1
+ [project]
2
+ name = "reydb"
3
+ version = "1.3.61"
4
+ description = "Database method set."
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ license = {file = "LICENSE"}
8
+ keywords = ["rey", "reyxbo", "PostgreSQL", "postgres", "db", "database", "orm", "async", "asynchronous"]
9
+ authors = [
10
+ {name = "Rey", email = "reyxbo@163.com"}
11
+ ]
12
+ dependencies = [
13
+ "asyncpg",
14
+ "psycopg[binary]",
15
+ "pydantic[email]",
16
+ "reykit",
17
+ "sqlalchemy==2.0.42",
18
+ "sqlmodel"
19
+ ]
20
+
21
+ [project.urls]
22
+ Homepage = "https://www.reyxbo.com"
23
+ Repository = "https://github.com/reyxbo/reydb-py.git"
24
+
25
+ [build-system]
26
+ requires = ["hatchling"]
27
+ build-backend = "hatchling.build"
@@ -0,0 +1,26 @@
1
+ # !/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ @Time : 2024-01-07
6
+ @Author : Rey
7
+ @Contact : reyxbo@163.com
8
+ @Explain : Backend database method set.
9
+
10
+ Modules
11
+ -------
12
+ rall : All import methods.
13
+ rbase : Base methods.
14
+ rbuild : Database build methods.
15
+ rconfig : Database config methods.
16
+ rconn : Database connection methods.
17
+ rdb : Database methods.
18
+ rengine : Database engine methods.
19
+ rerror : Database error methods.
20
+ rexec : Database execute methods.
21
+ rorm : Database ORM methods.
22
+ rinfo : Database information methods.
23
+ """
24
+
25
+ from .rdb import Database, DatabaseAsync
26
+ from .rengine import DatabaseEngine, DatabaseEngineAsync
@@ -0,0 +1,22 @@
1
+ # !/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ @Time : 2024-01-07
6
+ @Author : Rey
7
+ @Contact : reyxbo@163.com
8
+ @Explain : All import methods.
9
+ """
10
+
11
+ # ruff: noqa: F403
12
+
13
+ from .rbase import *
14
+ from .rbuild import *
15
+ from .rconfig import *
16
+ from .rconn import *
17
+ from .rdb import *
18
+ from .rengine import *
19
+ from .rerror import *
20
+ from .rexec import *
21
+ from .rorm import *
22
+ from .rinfo import *
@@ -0,0 +1,314 @@
1
+ # !/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ @Time : 2025-07-18
6
+ @Author : Rey
7
+ @Contact : reyxbo@163.com
8
+ @Explain : Base methods.
9
+ """
10
+
11
+ from typing import Any, TypedDict, Literal, TypeVar
12
+ from enum import EnumType
13
+ from sqlalchemy import Engine, Connection, Transaction, text as sqlalchemy_text, bindparam as sqlalchemy_bindparam
14
+ from sqlalchemy.orm import Session, SessionTransaction
15
+ from sqlalchemy.ext.asyncio import AsyncEngine, AsyncConnection, AsyncTransaction, AsyncSession, AsyncSessionTransaction
16
+ from sqlalchemy.engine.url import URL
17
+ from sqlalchemy.sql.elements import TextClause
18
+ from reykit.rbase import Base, throw
19
+ from reykit.rre import search
20
+ from reykit.rdata import to_json
21
+ from reykit.rre import findall
22
+
23
+ __all__ = (
24
+ 'DatabaseBase',
25
+ 'handle_sql_data',
26
+ 'extract_url',
27
+ 'extract_engine',
28
+ 'get_syntax',
29
+ 'is_multi_sql'
30
+ )
31
+
32
+ EngineT = TypeVar('EngineT', Engine, AsyncEngine)
33
+ ConnectionT = TypeVar('ConnectionT', Connection, AsyncConnection)
34
+ TransactionT = TypeVar('TransactionT', Transaction, AsyncTransaction)
35
+ SessionT = TypeVar('SessionT', Session, AsyncSession)
36
+ SessionTransactionT = TypeVar('SessionTransactionT', SessionTransaction, AsyncSessionTransaction)
37
+
38
+ URLParameters = TypedDict(
39
+ 'URLParameters',
40
+ {
41
+ 'drivername': str,
42
+ 'backend': str,
43
+ 'driver': str | None,
44
+ 'username': str | None,
45
+ 'password': str | None,
46
+ 'host': str | None,
47
+ 'port': str | None,
48
+ 'database': str | None,
49
+ 'query': dict[str, str] | None
50
+ }
51
+ )
52
+
53
+ class DatabaseBase(Base):
54
+ """
55
+ Database base type.
56
+ """
57
+
58
+ def handle_sql_data(sql: str | TextClause, data: list[dict]) -> tuple[TextClause, list[dict]]:
59
+ """
60
+ Handle sql and data.
61
+
62
+ Parameters
63
+ ----------
64
+ sql : SQL in method `sqlalchemy.text` format, or TextClause object.
65
+ data : Data set for filling.
66
+
67
+ Returns
68
+ -------
69
+ TextClause instance and filled data.
70
+ """
71
+
72
+ # Parameter.
73
+ if type(sql) is TextClause:
74
+ sql = sql.text
75
+
76
+ ## Extract keys.
77
+ pattern = '(?<!\\\\):(\\w+)'
78
+ sql_keys = findall(pattern, sql)
79
+
80
+ ## Extract keys of syntax "in".
81
+ pattern = '[iI][nN]\\s+(?<!\\\\):(\\w+)'
82
+ sql_keys_in = findall(pattern, sql)
83
+
84
+ # Handle SQL.
85
+ sql = sql.strip()
86
+ if sql[-1] != ';':
87
+ sql += ';'
88
+ sql = sqlalchemy_text(sql)
89
+ if len(data) != 0:
90
+ row = data[0]
91
+ for key, value in row.items():
92
+ if key in sql_keys_in:
93
+ param = sqlalchemy_bindparam(key, expanding=True)
94
+ sql = sql.bindparams(param)
95
+
96
+ # Handle data.
97
+ for row in data:
98
+ if row == {}:
99
+ continue
100
+ for key in sql_keys:
101
+ value = row.get(key)
102
+
103
+ # Empty string.
104
+ if value == '':
105
+ value = None
106
+
107
+ # JSON.
108
+ elif (
109
+ isinstance(value, list)
110
+ and key not in sql_keys_in
111
+ ) or isinstance(value, dict):
112
+ value = to_json(value)
113
+
114
+ # Array.
115
+ elif isinstance(value, tuple):
116
+ value = list(value)
117
+
118
+ # Enum.
119
+ elif isinstance(type(value), EnumType):
120
+ value = value.value
121
+
122
+ row[key] = value
123
+
124
+ return sql, data
125
+
126
+ def extract_url(url: str | URL) -> URLParameters:
127
+ """
128
+ Extract parameters from URL of string.
129
+
130
+ Parameters
131
+ ----------
132
+ url : URL of string.
133
+
134
+ Returns
135
+ -------
136
+ URL parameters.
137
+ """
138
+
139
+ # Extract.
140
+ match url:
141
+
142
+ ## Type str.
143
+ case str():
144
+ pattern_remote = r'^([^+]+)\+?([^:]+)??://([^:]+):([^@]+)@([^:]+):(\d+)[/]?([^\?]+)?\??(\S+)?$'
145
+ pattern_local = r'^([^+]+)\+?([^:]+)??:////?([^\?]+)[\?]?(\S+)?$'
146
+
147
+ ### Server.
148
+ if (result_remote := search(pattern_remote, url)) is not None:
149
+ (
150
+ backend,
151
+ driver,
152
+ username,
153
+ password,
154
+ host,
155
+ port,
156
+ database,
157
+ query_str
158
+ ) = result_remote
159
+ port = int(port)
160
+
161
+ ### SQLite.
162
+ elif (result_local := search(pattern_local, url)) is not None:
163
+ username = password = host = port = None
164
+ (
165
+ backend,
166
+ driver,
167
+ database,
168
+ query_str
169
+ ) = result_local
170
+
171
+ ### Throw exception.
172
+ else:
173
+ throw(ValueError, url)
174
+
175
+ if query_str is not None:
176
+ query = {
177
+ key: value
178
+ for query_item_str in query_str.split('&')
179
+ for key, value in (query_item_str.split('=', 1),)
180
+ }
181
+ else:
182
+ query = {}
183
+
184
+ ## Type URL.
185
+ case URL():
186
+ drivername = url.drivername
187
+ username = url.username
188
+ password = url.password
189
+ host = url.host
190
+ port = url.port
191
+ database = url.database
192
+ query = dict(url.query)
193
+
194
+ ## Drivername.
195
+ if driver is None:
196
+ drivername = backend
197
+ else:
198
+ drivername = f'{backend}+{driver}'
199
+
200
+ # Generate parameter.
201
+ params = {
202
+ 'drivername': drivername,
203
+ 'backend': backend,
204
+ 'driver': driver,
205
+ 'username': username,
206
+ 'password': password,
207
+ 'host': host,
208
+ 'port': port,
209
+ 'database': database,
210
+ 'query': query
211
+ }
212
+
213
+ return params
214
+
215
+ def extract_engine(engine: Engine | Connection) -> dict[
216
+ Literal[
217
+ 'drivername', 'username', 'password', 'host', 'port', 'database', 'query',
218
+ 'pool_size', 'max_overflow', 'pool_timeout', 'pool_recycle'
219
+ ],
220
+ Any
221
+ ]:
222
+ """
223
+ Extract parameters from `Engine` or `Connection` object.
224
+
225
+ Parameters
226
+ ----------
227
+ engine : Engine or Connection object.
228
+
229
+ Returns
230
+ -------
231
+ Extracted parameters.
232
+ """
233
+
234
+ ## Extract Engine object from Connection boject.
235
+ if type(engine) is Connection:
236
+ engine = engine.engine
237
+
238
+ ## Extract.
239
+ drivername: str = engine.url.drivername
240
+ username: str | None = engine.url.username
241
+ password: str | None = engine.url.password
242
+ host: str | None = engine.url.host
243
+ port: str | None = engine.url.port
244
+ database: str | None = engine.url.database
245
+ query: dict[str, str] = dict(engine.url.query)
246
+ pool_size: int = engine.pool._pool.maxsize
247
+ max_overflow: int = engine.pool._max_overflow
248
+ pool_timeout: float = engine.pool._timeout
249
+ pool_recycle: int = engine.pool._recycle
250
+
251
+ # Generate parameter.
252
+ params = {
253
+ 'drivername': drivername,
254
+ 'username': username,
255
+ 'password': password,
256
+ 'host': host,
257
+ 'port': port,
258
+ 'database': database,
259
+ 'query': query,
260
+ 'pool_size': pool_size,
261
+ 'max_overflow': max_overflow,
262
+ 'pool_timeout': pool_timeout,
263
+ 'pool_recycle': pool_recycle
264
+ }
265
+
266
+ return params
267
+
268
+ def get_syntax(self, sql: str | TextClause) -> list[str]:
269
+ """
270
+ Extract SQL syntax type for each segment form SQL.
271
+
272
+ Parameters
273
+ ----------
274
+ sql : SQL text or TextClause object.
275
+
276
+ Returns
277
+ -------
278
+ SQL syntax type for each segment.
279
+ """
280
+
281
+ # Parameter.
282
+ if type(sql) is TextClause:
283
+ sql = sql.text
284
+
285
+ # Extract.
286
+ syntax = [
287
+ search('[a-zA-Z]+', sql_part).upper()
288
+ for sql_part in sql.split(';')
289
+ if sql_part != ''
290
+ ]
291
+
292
+ return syntax
293
+
294
+ def is_multi_sql(self, sql: str | TextClause) -> bool:
295
+ """
296
+ Judge whether it is multi segment SQL.
297
+
298
+ Parameters
299
+ ----------
300
+ sql : SQL text or TextClause object.
301
+
302
+ Returns
303
+ -------
304
+ Judgment result.
305
+ """
306
+
307
+ # Parameter.
308
+ if type(sql) is TextClause:
309
+ sql = sql.text
310
+
311
+ # Judge.
312
+ if ';' in sql.rstrip()[:-1]:
313
+ return True
314
+ return False