cloudoll 3.0.6__tar.gz → 3.0.7__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.
- {cloudoll-3.0.6 → cloudoll-3.0.7}/PKG-INFO +1 -1
- cloudoll-3.0.7/cloudoll/__init__.py +1 -0
- cloudoll-3.0.7/cloudoll/orm/__init__.py +44 -0
- cloudoll-3.0.7/cloudoll/orm/awsmysql.py +79 -0
- cloudoll-3.0.7/cloudoll/orm/awspostgres.py +93 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/orm/base.py +3 -5
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/orm/model.py +9 -5
- cloudoll-3.0.7/cloudoll/orm/mysql.py +112 -0
- cloudoll-3.0.7/cloudoll/orm/postgres.py +116 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/orm/test_sql.py +1 -1
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/web/core.py +51 -28
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll.egg-info/PKG-INFO +1 -1
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll.egg-info/SOURCES.txt +2 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/pyproject.toml +1 -1
- cloudoll-3.0.6/cloudoll/__init__.py +0 -1
- cloudoll-3.0.6/cloudoll/orm/__init__.py +0 -247
- cloudoll-3.0.6/cloudoll/orm/mysql.py +0 -82
- cloudoll-3.0.6/cloudoll/orm/postgres.py +0 -68
- {cloudoll-3.0.6 → cloudoll-3.0.7}/LICENSE +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/README.md +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/__main__.py +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/cli.py +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/clitool/__init__.py +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/clitool/cli_main.py +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/clitool/m2d.py +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/clitool/process.py +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/clitool/spinner.py +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/clitool/watch.py +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/logging/__init__.py +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/mail/__init__.py +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/mail/smtp.py +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/orm/err.py +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/orm/field.py +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/orm/parse.py +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/template/__init__.py +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/utils/__init__.py +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/utils/common.py +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/utils/snowflake.py +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/web/__init__.py +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/web/exception.py +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/web/html.py +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/web/jwt.py +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/web/requests.py +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll/web/settings.py +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll.egg-info/dependency_links.txt +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll.egg-info/entry_points.txt +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll.egg-info/requires.txt +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/cloudoll.egg-info/top_level.txt +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/setup.cfg +0 -0
- {cloudoll-3.0.6 → cloudoll-3.0.7}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cloudoll
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.7
|
|
4
4
|
Summary: Assist in quickly creating distributable microservices based on aiohttp, with a focus on simplicity and ease of use.
|
|
5
5
|
Home-page: https://github.com/smallerqiu/cloudoll-py
|
|
6
6
|
Author: Qiu
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "3.0.7"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from redis import asyncio as aioredis
|
|
2
|
+
from cloudoll.orm.parse import parse_coon
|
|
3
|
+
import aiopg as pg
|
|
4
|
+
from .mysql import Mysql
|
|
5
|
+
from .postgres import Postgres
|
|
6
|
+
from .awspostgres import AwsPostgres
|
|
7
|
+
from .awsmysql import AwsMysql
|
|
8
|
+
|
|
9
|
+
__all__ = ["create_engine"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def create_engine(**kw):
|
|
13
|
+
url = kw.get("url")
|
|
14
|
+
driver = None
|
|
15
|
+
configs = {}
|
|
16
|
+
query = {}
|
|
17
|
+
|
|
18
|
+
if url is not None:
|
|
19
|
+
configs, query = parse_coon(url)
|
|
20
|
+
driver = configs["type"]
|
|
21
|
+
else:
|
|
22
|
+
driver = kw.get("type")
|
|
23
|
+
configs = kw
|
|
24
|
+
|
|
25
|
+
# info("DB Config:", configs, query)
|
|
26
|
+
|
|
27
|
+
if driver == "mysql":
|
|
28
|
+
return await Mysql().create_engine(**configs, **query)
|
|
29
|
+
elif driver == "aws-mysql":
|
|
30
|
+
return await AwsMysql().create_engine(**configs, **query)
|
|
31
|
+
elif driver in ["aws-postgres", "aws-postgressql"]:
|
|
32
|
+
return await AwsPostgres().create_engine(**configs, **query)
|
|
33
|
+
elif driver in ["postgres", "postgressql"]:
|
|
34
|
+
return await Postgres().create_engine(**configs, **query)
|
|
35
|
+
elif driver in ["redis", "rediss"]:
|
|
36
|
+
"""
|
|
37
|
+
redis://[[username]:[password]]@localhost:6379/0
|
|
38
|
+
rediss://[[username]:[password]]@localhost:6379/0
|
|
39
|
+
"""
|
|
40
|
+
if url is None:
|
|
41
|
+
url = f"{driver}://{configs['username']}:{configs['password']}@{configs['host']}:{configs['port']}/{configs['db']}"
|
|
42
|
+
return await aioredis.from_url(url, **query)
|
|
43
|
+
else:
|
|
44
|
+
raise ValueError("Not support this database type.")
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from mysql.connector import Connect
|
|
2
|
+
|
|
3
|
+
from aws_advanced_python_wrapper import AwsWrapperConnection
|
|
4
|
+
from aws_advanced_python_wrapper.connection_provider import ConnectionProviderManager
|
|
5
|
+
from aws_advanced_python_wrapper.sql_alchemy_connection_provider import (
|
|
6
|
+
SqlAlchemyPooledConnectionProvider,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
from cloudoll.orm.base import MeteBase, QueryTypes
|
|
10
|
+
from cloudoll.logging import info, error
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AwsMysql(MeteBase):
|
|
14
|
+
def __init__(self):
|
|
15
|
+
self.driver = "aws-mysql"
|
|
16
|
+
provider = SqlAlchemyPooledConnectionProvider()
|
|
17
|
+
ConnectionProviderManager.set_connection_provider(provider)
|
|
18
|
+
|
|
19
|
+
async def close(self):
|
|
20
|
+
ConnectionProviderManager.release_resources()
|
|
21
|
+
|
|
22
|
+
async def create_engine(self, **kw):
|
|
23
|
+
self._dsn = f"host={kw.get("host")} database={kw.get("db")} user={kw.get("username")} password={kw.get("password","")}"
|
|
24
|
+
self._params = {
|
|
25
|
+
"plugins": kw.get("plugins", ""),
|
|
26
|
+
"wrapper_dialect": kw.get("wrapper_dialect", ""),
|
|
27
|
+
"autocommit": True,
|
|
28
|
+
}
|
|
29
|
+
return self
|
|
30
|
+
|
|
31
|
+
async def query(self, sql, params=None, query_type=QueryTypes.ONE, size=10):
|
|
32
|
+
sql = sql.replace("?", "%s")
|
|
33
|
+
try:
|
|
34
|
+
with AwsWrapperConnection.connect(
|
|
35
|
+
Connect, self._dsn, **self._params
|
|
36
|
+
) as conn:
|
|
37
|
+
with conn.cursor() as cursor:
|
|
38
|
+
if (
|
|
39
|
+
query_type == QueryTypes.CREATEBATCH
|
|
40
|
+
or query_type == QueryTypes.UPDATEBATCH
|
|
41
|
+
):
|
|
42
|
+
cursor.executemany(sql, params)
|
|
43
|
+
else:
|
|
44
|
+
cursor.execute(sql, params)
|
|
45
|
+
|
|
46
|
+
if query_type == QueryTypes.ALL:
|
|
47
|
+
return cursor.fetchall()
|
|
48
|
+
elif query_type == QueryTypes.ONE:
|
|
49
|
+
return cursor.fetchone()
|
|
50
|
+
elif query_type == QueryTypes.MANY:
|
|
51
|
+
return cursor.fetchmany(size)
|
|
52
|
+
elif query_type == QueryTypes.COUNT:
|
|
53
|
+
rows = cursor.fetchone()
|
|
54
|
+
count = 0
|
|
55
|
+
if rows is None:
|
|
56
|
+
return count
|
|
57
|
+
for row in rows:
|
|
58
|
+
count = rows[row]
|
|
59
|
+
return count
|
|
60
|
+
elif query_type == QueryTypes.GROUP_COUNT:
|
|
61
|
+
result = cursor.fetchall()
|
|
62
|
+
return 0 if not result else len(result)
|
|
63
|
+
elif query_type == QueryTypes.CREATE:
|
|
64
|
+
result = cursor.rowcount > 0
|
|
65
|
+
id = cursor.target_cursor.lastrowid
|
|
66
|
+
return result, id
|
|
67
|
+
elif query_type == QueryTypes.CREATEBATCH:
|
|
68
|
+
count = cursor.rowcount
|
|
69
|
+
id = cursor.target_cursor.lastrowid
|
|
70
|
+
return count, id
|
|
71
|
+
elif query_type == QueryTypes.UPDATE:
|
|
72
|
+
return cursor.rowcount > 0
|
|
73
|
+
elif query_type == QueryTypes.UPDATEBATCH:
|
|
74
|
+
return cursor.rowcount
|
|
75
|
+
elif query_type == QueryTypes.DELETE:
|
|
76
|
+
return cursor.rowcount > 0
|
|
77
|
+
except Exception as e:
|
|
78
|
+
error(f"[AWS PG] query error: {e}, SQL: {sql}")
|
|
79
|
+
raise e
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import psycopg
|
|
2
|
+
from aws_advanced_python_wrapper import AwsWrapperConnection
|
|
3
|
+
from aws_advanced_python_wrapper.connection_provider import ConnectionProviderManager
|
|
4
|
+
from aws_advanced_python_wrapper.sql_alchemy_connection_provider import (
|
|
5
|
+
SqlAlchemyPooledConnectionProvider,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
from cloudoll.orm.base import MeteBase, QueryTypes
|
|
9
|
+
from cloudoll.logging import info, error
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AwsPostgres(MeteBase):
|
|
13
|
+
def __init__(self):
|
|
14
|
+
self.driver = "aws-postgres"
|
|
15
|
+
provider = SqlAlchemyPooledConnectionProvider()
|
|
16
|
+
ConnectionProviderManager.set_connection_provider(provider)
|
|
17
|
+
|
|
18
|
+
async def close(self):
|
|
19
|
+
ConnectionProviderManager.release_resources()
|
|
20
|
+
|
|
21
|
+
async def create_engine(self, **kw):
|
|
22
|
+
self._dsn = f"host={kw.get("host")} dbname={kw.get("db")} user={kw.get("username")} password={kw.get("username")}"
|
|
23
|
+
self._params = {
|
|
24
|
+
"plugins": kw.get("plugins", ""),
|
|
25
|
+
"wrapper_dialect": kw.get("wrapper_dialect"),
|
|
26
|
+
"autocommit": True,
|
|
27
|
+
}
|
|
28
|
+
return self
|
|
29
|
+
|
|
30
|
+
async def query(self, sql, params=None, query_type=QueryTypes.ONE, size=10):
|
|
31
|
+
sql = sql.replace("?", "%s").replace("`", '"')
|
|
32
|
+
try:
|
|
33
|
+
with AwsWrapperConnection.connect(
|
|
34
|
+
psycopg.Connection.connect, self._dsn, **self._params
|
|
35
|
+
) as conn:
|
|
36
|
+
with conn.cursor() as cursor:
|
|
37
|
+
if (
|
|
38
|
+
query_type == QueryTypes.CREATEBATCH
|
|
39
|
+
or query_type == QueryTypes.UPDATEBATCH
|
|
40
|
+
):
|
|
41
|
+
cursor.executemany(sql, params)
|
|
42
|
+
else:
|
|
43
|
+
cursor.execute(sql, params)
|
|
44
|
+
|
|
45
|
+
result = None
|
|
46
|
+
|
|
47
|
+
if query_type == QueryTypes.ALL and cursor.description is not None:
|
|
48
|
+
columns = [desc[0] for desc in cursor.target_cursor.description]
|
|
49
|
+
rows = cursor.fetchall()
|
|
50
|
+
result = [dict(zip(columns, row)) for row in rows]
|
|
51
|
+
return result
|
|
52
|
+
elif (
|
|
53
|
+
query_type == QueryTypes.ONE and cursor.description is not None
|
|
54
|
+
):
|
|
55
|
+
columns = [desc[0] for desc in cursor.target_cursor.description]
|
|
56
|
+
row = cursor.fetchone()
|
|
57
|
+
result = dict(zip(columns, row)) if row else {}
|
|
58
|
+
return result
|
|
59
|
+
elif (
|
|
60
|
+
query_type == QueryTypes.MANY and cursor.description is not None
|
|
61
|
+
):
|
|
62
|
+
columns = [desc[0] for desc in cursor.target_cursor.description]
|
|
63
|
+
rows = cursor.fetchmany(size)
|
|
64
|
+
result = [dict(zip(columns, row)) for row in rows]
|
|
65
|
+
return result
|
|
66
|
+
elif query_type == QueryTypes.COUNT:
|
|
67
|
+
result = cursor.fetchone()
|
|
68
|
+
count = 0
|
|
69
|
+
if result is None:
|
|
70
|
+
return count
|
|
71
|
+
for value in result:
|
|
72
|
+
count = value
|
|
73
|
+
return count
|
|
74
|
+
elif query_type == QueryTypes.GROUP_COUNT:
|
|
75
|
+
result = cursor.fetchall()
|
|
76
|
+
return 0 if not result else len(result)
|
|
77
|
+
elif query_type == QueryTypes.CREATE:
|
|
78
|
+
result = cursor.rowcount > 0
|
|
79
|
+
id = cursor.target_cursor.lastrowid
|
|
80
|
+
return result, id
|
|
81
|
+
elif query_type == QueryTypes.CREATEBATCH:
|
|
82
|
+
count = cursor.rowcount
|
|
83
|
+
id = cursor.target_cursor.lastrowid
|
|
84
|
+
return count, id
|
|
85
|
+
elif query_type == QueryTypes.UPDATE:
|
|
86
|
+
return cursor.rowcount > 0
|
|
87
|
+
elif query_type == QueryTypes.UPDATEBATCH:
|
|
88
|
+
return cursor.rowcount
|
|
89
|
+
elif query_type == QueryTypes.DELETE:
|
|
90
|
+
return cursor.rowcount > 0
|
|
91
|
+
except Exception as e:
|
|
92
|
+
error(f"[AWS PG] query error: {e}, SQL: {sql}")
|
|
93
|
+
raise e
|
|
@@ -19,8 +19,8 @@ class QueryTypes(Enum):
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class MeteBase:
|
|
22
|
-
def __init__(self):
|
|
23
|
-
self.pool: Optional[MyPool | PGPool] = None
|
|
22
|
+
# def __init__(self):
|
|
23
|
+
# self.pool: Optional[MyPool | PGPool] = None
|
|
24
24
|
# self.cursor: Optional[Cursor] = None
|
|
25
25
|
# self.conn: Optional[Connection] = None
|
|
26
26
|
|
|
@@ -28,9 +28,7 @@ class MeteBase:
|
|
|
28
28
|
await self.close()
|
|
29
29
|
|
|
30
30
|
async def close(self):
|
|
31
|
-
|
|
32
|
-
self.pool.close()
|
|
33
|
-
await self.pool.wait_closed()
|
|
31
|
+
...
|
|
34
32
|
|
|
35
33
|
# async def begin_transaction(self):
|
|
36
34
|
# conn = await self._set_conn()
|
|
@@ -150,14 +150,18 @@ class Model(metaclass=ModelMetaclass):
|
|
|
150
150
|
@classmethod
|
|
151
151
|
def use(cls, pool=None):
|
|
152
152
|
cls.__pool__ = pool
|
|
153
|
-
cls.
|
|
153
|
+
cls.__is_pg = (
|
|
154
|
+
pool.driver in ["postgres", "postgressql", "aws-postgres"]
|
|
155
|
+
if pool
|
|
156
|
+
else False
|
|
157
|
+
)
|
|
154
158
|
return cls()
|
|
155
159
|
|
|
156
160
|
def select(cls, *args):
|
|
157
161
|
cols = []
|
|
158
162
|
for col in args:
|
|
159
163
|
if isinstance(col, Field):
|
|
160
|
-
cols.append(col.name if cls.
|
|
164
|
+
cols.append(col.name if cls.__is_pg else col.full_name)
|
|
161
165
|
elif isinstance(col, Function):
|
|
162
166
|
q, p = col.sql()
|
|
163
167
|
cols.append(q)
|
|
@@ -200,7 +204,7 @@ class Model(metaclass=ModelMetaclass):
|
|
|
200
204
|
if isinstance(f, str):
|
|
201
205
|
by.append(f)
|
|
202
206
|
else:
|
|
203
|
-
by.append(f"{f.lhs.name if self.
|
|
207
|
+
by.append(f"{f.lhs.name if self.__is_pg else f.lhs.full_name} {f.op}")
|
|
204
208
|
if self.__order_by__ is not None:
|
|
205
209
|
by = self.__order_by__ + by
|
|
206
210
|
self.__order_by__ = by
|
|
@@ -209,7 +213,7 @@ class Model(metaclass=ModelMetaclass):
|
|
|
209
213
|
def group_by(self, *args):
|
|
210
214
|
by = []
|
|
211
215
|
for f in args:
|
|
212
|
-
by.append(f.name if self.
|
|
216
|
+
by.append(f.name if self.__is_pg else f.full_name)
|
|
213
217
|
if self.__group_by__ is not None:
|
|
214
218
|
by = self.__group_by__ + by
|
|
215
219
|
self.__group_by__ = by
|
|
@@ -365,7 +369,7 @@ class Model(metaclass=ModelMetaclass):
|
|
|
365
369
|
return await self.__pool__.all(sql, args)
|
|
366
370
|
|
|
367
371
|
def _exchange_sql(self, sql: str):
|
|
368
|
-
if self.
|
|
372
|
+
if self.__is_pg:
|
|
369
373
|
sql = sql.replace("CURDATE()", "CURRENT_DATE")
|
|
370
374
|
sql = sql.replace("NOW()", "CURRENT_TIMESTAMP")
|
|
371
375
|
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
from typing import Any, Optional
|
|
2
|
+
import aiomysql
|
|
3
|
+
from cloudoll.logging import error
|
|
4
|
+
from cloudoll.orm.base import MeteBase, QueryTypes
|
|
5
|
+
from cloudoll.logging import info, error
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AttrDict(dict):
|
|
9
|
+
def __getattr__(self, name):
|
|
10
|
+
try:
|
|
11
|
+
return self[name]
|
|
12
|
+
except KeyError:
|
|
13
|
+
return None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AttrDictCursor(aiomysql.DictCursor):
|
|
17
|
+
dict_type = AttrDict
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Mysql(MeteBase):
|
|
21
|
+
def __init__(self):
|
|
22
|
+
self.pool: aiomysql.Pool
|
|
23
|
+
self.driver = "mysql"
|
|
24
|
+
|
|
25
|
+
def __call__(self, *args: Any, **kwds: Any) -> Any:
|
|
26
|
+
self.__init__(*args, **kwds)
|
|
27
|
+
return self
|
|
28
|
+
|
|
29
|
+
async def close(self):
|
|
30
|
+
if self.pool:
|
|
31
|
+
self.pool.close()
|
|
32
|
+
await self.pool.wait_closed()
|
|
33
|
+
|
|
34
|
+
async def query(
|
|
35
|
+
self, sql, params=None, query_type: QueryTypes = QueryTypes.ONE, size: int = 10
|
|
36
|
+
):
|
|
37
|
+
sql = sql.replace("?", "%s")
|
|
38
|
+
if not self.pool:
|
|
39
|
+
raise ValueError("must be create_engine first.")
|
|
40
|
+
if self.pool._closing or self.pool._closed:
|
|
41
|
+
return None
|
|
42
|
+
async with self.pool.acquire() as conn:
|
|
43
|
+
if conn.echo:
|
|
44
|
+
info("sql: %s , %s", sql, params)
|
|
45
|
+
|
|
46
|
+
async with conn.cursor() as cursor:
|
|
47
|
+
cursor: aiomysql.Cursor = cursor
|
|
48
|
+
if (
|
|
49
|
+
query_type == QueryTypes.CREATEBATCH
|
|
50
|
+
or query_type == QueryTypes.UPDATEBATCH
|
|
51
|
+
):
|
|
52
|
+
await cursor.executemany(sql, params)
|
|
53
|
+
else:
|
|
54
|
+
await cursor.execute(sql, params)
|
|
55
|
+
|
|
56
|
+
await conn.commit()
|
|
57
|
+
|
|
58
|
+
if query_type == QueryTypes.ALL:
|
|
59
|
+
return await cursor.fetchall()
|
|
60
|
+
elif query_type == QueryTypes.ONE:
|
|
61
|
+
return await cursor.fetchone()
|
|
62
|
+
elif query_type == QueryTypes.MANY:
|
|
63
|
+
return await cursor.fetchmany(size)
|
|
64
|
+
elif query_type == QueryTypes.COUNT:
|
|
65
|
+
rows = await cursor.fetchone()
|
|
66
|
+
count = 0
|
|
67
|
+
if rows is None:
|
|
68
|
+
return count
|
|
69
|
+
for row in rows:
|
|
70
|
+
count = rows[row]
|
|
71
|
+
return count
|
|
72
|
+
elif query_type == QueryTypes.GROUP_COUNT:
|
|
73
|
+
result = await cursor.fetchall()
|
|
74
|
+
return 0 if not result else len(result)
|
|
75
|
+
elif query_type == QueryTypes.CREATE:
|
|
76
|
+
result = cursor.rowcount > 0
|
|
77
|
+
id = cursor.lastrowid
|
|
78
|
+
return result, id
|
|
79
|
+
elif query_type == QueryTypes.CREATEBATCH:
|
|
80
|
+
count = cursor.rowcount
|
|
81
|
+
id = cursor.lastrowid
|
|
82
|
+
return count, id
|
|
83
|
+
elif query_type == QueryTypes.UPDATE:
|
|
84
|
+
return cursor.rowcount > 0
|
|
85
|
+
elif query_type == QueryTypes.UPDATEBATCH:
|
|
86
|
+
return cursor.rowcount
|
|
87
|
+
elif query_type == QueryTypes.DELETE:
|
|
88
|
+
return cursor.rowcount > 0
|
|
89
|
+
|
|
90
|
+
self.pool.release(conn)
|
|
91
|
+
|
|
92
|
+
async def create_engine(self, loop=None, **kw):
|
|
93
|
+
try:
|
|
94
|
+
self.pool = await aiomysql.create_pool(
|
|
95
|
+
host=kw.get("host", "localhost"),
|
|
96
|
+
port=kw.get("port", 3306),
|
|
97
|
+
user=kw.get("username"),
|
|
98
|
+
password=str(kw.get("password", "")),
|
|
99
|
+
db=kw.get("db"),
|
|
100
|
+
echo=kw.get("echo", False),
|
|
101
|
+
charset=kw.get("charset", "utf8"),
|
|
102
|
+
autocommit=False, # kw.get("autocommit", False),
|
|
103
|
+
maxsize=kw.get("maxsize", 10),
|
|
104
|
+
minsize=kw.get("minsize", 5),
|
|
105
|
+
cursorclass=AttrDictCursor,
|
|
106
|
+
loop=loop,
|
|
107
|
+
)
|
|
108
|
+
info(f"Database connection successfully for mysql/{kw.get('db')}.")
|
|
109
|
+
except Exception as e:
|
|
110
|
+
# print(traceback.format_exc())
|
|
111
|
+
error(f"Database connection failed,the instance : mysql/{kw.get('db')}")
|
|
112
|
+
return self
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import aiopg
|
|
2
|
+
from cloudoll.logging import error
|
|
3
|
+
from cloudoll.orm.base import MeteBase, QueryTypes
|
|
4
|
+
from cloudoll.logging import info, error
|
|
5
|
+
from typing import Any, Optional
|
|
6
|
+
|
|
7
|
+
class Postgres(MeteBase):
|
|
8
|
+
def __init__(self):
|
|
9
|
+
self.pool: Optional[aiopg.Pool] = None
|
|
10
|
+
self.driver = "postgres"
|
|
11
|
+
|
|
12
|
+
def __call__(self, *args: Any, **kwds: Any) -> Any:
|
|
13
|
+
self.__init__(*args, **kwds)
|
|
14
|
+
return self
|
|
15
|
+
|
|
16
|
+
async def close(self):
|
|
17
|
+
if self.pool:
|
|
18
|
+
self.pool.close()
|
|
19
|
+
await self.pool.wait_closed()
|
|
20
|
+
|
|
21
|
+
async def query(
|
|
22
|
+
self,
|
|
23
|
+
sql,
|
|
24
|
+
params=None,
|
|
25
|
+
query_type: QueryTypes = QueryTypes.COUNT,
|
|
26
|
+
size: int = 10,
|
|
27
|
+
):
|
|
28
|
+
sql = sql.replace("?", "%s").replace("`", '"')
|
|
29
|
+
if not self.pool:
|
|
30
|
+
raise ValueError("must be create_engine first.")
|
|
31
|
+
if self.pool._closing or self.pool._closed:
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
async with self.pool.acquire() as conn:
|
|
35
|
+
if conn.echo:
|
|
36
|
+
info("sql: %s ,%s", sql, params)
|
|
37
|
+
|
|
38
|
+
async with conn.cursor() as cursor:
|
|
39
|
+
# current_cursor = getattr(cursor, 'lastrowid', None)
|
|
40
|
+
if (
|
|
41
|
+
query_type == QueryTypes.CREATEBATCH
|
|
42
|
+
or query_type == QueryTypes.UPDATEBATCH
|
|
43
|
+
):
|
|
44
|
+
await cursor.executemany(sql, params)
|
|
45
|
+
else:
|
|
46
|
+
await cursor.execute(sql, params)
|
|
47
|
+
|
|
48
|
+
# await conn.commit()
|
|
49
|
+
result = None
|
|
50
|
+
|
|
51
|
+
if query_type == QueryTypes.ALL and cursor.description is not None:
|
|
52
|
+
columns = [desc[0] for desc in cursor.description]
|
|
53
|
+
rows = await cursor.fetchall()
|
|
54
|
+
result = [dict(zip(columns, row)) for row in rows]
|
|
55
|
+
return result
|
|
56
|
+
elif query_type == QueryTypes.ONE and cursor.description is not None:
|
|
57
|
+
columns = [desc[0] for desc in cursor.description]
|
|
58
|
+
row = await cursor.fetchone()
|
|
59
|
+
result = dict(zip(columns, row)) if row else {}
|
|
60
|
+
return result
|
|
61
|
+
elif query_type == QueryTypes.MANY and cursor.description is not None:
|
|
62
|
+
columns = [desc[0] for desc in cursor.description]
|
|
63
|
+
rows = await cursor.fetchmany(size)
|
|
64
|
+
result = [dict(zip(columns, row)) for row in rows]
|
|
65
|
+
return result
|
|
66
|
+
elif query_type == QueryTypes.COUNT:
|
|
67
|
+
result = await cursor.fetchone()
|
|
68
|
+
count = 0
|
|
69
|
+
if result is None:
|
|
70
|
+
return count
|
|
71
|
+
for value in result:
|
|
72
|
+
count = value
|
|
73
|
+
return count
|
|
74
|
+
elif query_type == QueryTypes.GROUP_COUNT:
|
|
75
|
+
result = await cursor.fetchall()
|
|
76
|
+
return 0 if not result else len(result)
|
|
77
|
+
elif query_type == QueryTypes.CREATE:
|
|
78
|
+
result = cursor.rowcount > 0
|
|
79
|
+
id = cursor.lastrowid
|
|
80
|
+
return result, id
|
|
81
|
+
elif query_type == QueryTypes.CREATEBATCH:
|
|
82
|
+
count = cursor.rowcount
|
|
83
|
+
id = cursor.lastrowid
|
|
84
|
+
return count, id
|
|
85
|
+
elif query_type == QueryTypes.UPDATE:
|
|
86
|
+
return cursor.rowcount > 0
|
|
87
|
+
elif query_type == QueryTypes.UPDATEBATCH:
|
|
88
|
+
return cursor.rowcount
|
|
89
|
+
elif query_type == QueryTypes.DELETE:
|
|
90
|
+
return cursor.rowcount > 0
|
|
91
|
+
|
|
92
|
+
async def create_engine(self, **kw):
|
|
93
|
+
try:
|
|
94
|
+
host = (kw.get("host", "localhost"),)
|
|
95
|
+
port = (kw.get("port", 5432),)
|
|
96
|
+
user = (kw.get("username"),)
|
|
97
|
+
password = (str(kw.get("password", "")),)
|
|
98
|
+
db = (kw.get("db"),)
|
|
99
|
+
dsn = f"dbname={db[0]} user={user[0]} password={password[0]} host={host[0]} port={port[0]}" # aiopg
|
|
100
|
+
# dsn = f"postgres://{user[0]}:{password[0]}@{host[0]}:{port[0]}/{db[0]}" # asyncpg
|
|
101
|
+
self.pool = await aiopg.create_pool(
|
|
102
|
+
dsn=dsn,
|
|
103
|
+
timeout=float(kw.get("timeout", 60)),
|
|
104
|
+
echo=kw.get("echo", False), # aiopg
|
|
105
|
+
# max_size=kw.get("maxsize", 10), # asyncpg
|
|
106
|
+
# min_size=kw.get("minsize", 5), # asyncpg
|
|
107
|
+
maxsize=kw.get("maxsize", 10), # aiopg
|
|
108
|
+
minsize=kw.get("minsize", 5), # aiopg
|
|
109
|
+
)
|
|
110
|
+
info(f"Database connection successfully for postgres/{kw.get('db')}")
|
|
111
|
+
except Exception as e:
|
|
112
|
+
error(e)
|
|
113
|
+
# print(traceback.format_exc())
|
|
114
|
+
error(f"Database connection failed,the instance : postgres/{kw.get('db')}")
|
|
115
|
+
|
|
116
|
+
return self
|
|
@@ -42,7 +42,7 @@ class B(Model):
|
|
|
42
42
|
|
|
43
43
|
class C(Model):
|
|
44
44
|
|
|
45
|
-
__table__ = "
|
|
45
|
+
__table__ = "c"
|
|
46
46
|
|
|
47
47
|
id = models.BigIntegerField(primary_key=True, max_length=64, not_null=True)
|
|
48
48
|
pack_id = models.BigIntegerField(max_length=64, default="0", not_null=True)
|
|
@@ -16,7 +16,7 @@ import time
|
|
|
16
16
|
from urllib import parse
|
|
17
17
|
from aiohttp import web, hdrs
|
|
18
18
|
from aiohttp.web import Response
|
|
19
|
-
from aiohttp.web_ws import WebSocketResponse,WSMsgType
|
|
19
|
+
from aiohttp.web_ws import WebSocketResponse, WSMsgType
|
|
20
20
|
from aiohttp.web_response import StreamResponse
|
|
21
21
|
from aiohttp.web_request import Request
|
|
22
22
|
from aiohttp.typedefs import LooseHeaders
|
|
@@ -28,8 +28,6 @@ from aiohttp_session import (
|
|
|
28
28
|
cookie_storage,
|
|
29
29
|
)
|
|
30
30
|
from cloudoll.web.settings import get_config
|
|
31
|
-
import aiomcache
|
|
32
|
-
from redis import asyncio as aioredis
|
|
33
31
|
from cloudoll.logging import info
|
|
34
32
|
from cloudoll.orm.model import Model
|
|
35
33
|
from cloudoll.web import jwt
|
|
@@ -62,6 +60,7 @@ async def _set_session_route(request: Request):
|
|
|
62
60
|
|
|
63
61
|
|
|
64
62
|
def _auto_reg_module(module_dir: str):
|
|
63
|
+
info(f"Auto-registration {module_dir}")
|
|
65
64
|
base_path = Path(module_dir).resolve()
|
|
66
65
|
if str(base_path.parent) not in sys.path:
|
|
67
66
|
sys.path.insert(0, str(base_path.parent))
|
|
@@ -129,8 +128,8 @@ async def _render_result(request: Request, func):
|
|
|
129
128
|
|
|
130
129
|
def _sa_ignore_hash(method, path):
|
|
131
130
|
md5 = hashlib.md5()
|
|
132
|
-
|
|
133
|
-
md5.update(
|
|
131
|
+
hash_str = f"{method}{path}"
|
|
132
|
+
md5.update(hash_str.encode("utf-8"))
|
|
134
133
|
return md5.hexdigest()
|
|
135
134
|
|
|
136
135
|
|
|
@@ -140,8 +139,8 @@ def _parse_int(num):
|
|
|
140
139
|
|
|
141
140
|
def _sa_ignore_middleware():
|
|
142
141
|
async def set_ignore(ctx, handler):
|
|
143
|
-
|
|
144
|
-
ctx.is_sa_ignore =
|
|
142
|
+
hash_str = _sa_ignore_hash(ctx.method, ctx.path)
|
|
143
|
+
ctx.is_sa_ignore = hash_str in ctx.app.ignore_paths
|
|
145
144
|
start_time = time.time()
|
|
146
145
|
response = await handler(ctx)
|
|
147
146
|
end_time = time.time()
|
|
@@ -162,13 +161,18 @@ class Application(object):
|
|
|
162
161
|
self.config = {}
|
|
163
162
|
self.clean_up = False
|
|
164
163
|
|
|
165
|
-
def _load_life_cycle(self, entry_model=None):
|
|
164
|
+
def _load_life_cycle(self, entry_model=None, func_name=None):
|
|
166
165
|
try:
|
|
167
166
|
if not entry_model:
|
|
168
167
|
return
|
|
169
168
|
|
|
170
169
|
entry = importlib.import_module(entry_model, ".")
|
|
171
170
|
|
|
171
|
+
if func_name:
|
|
172
|
+
func = getattr(entry, func_name)
|
|
173
|
+
func(self)
|
|
174
|
+
return
|
|
175
|
+
|
|
172
176
|
life_cycle = ["on_startup", "on_shutdown", "on_cleanup", "on_task"]
|
|
173
177
|
for cycle in life_cycle:
|
|
174
178
|
if hasattr(entry, cycle):
|
|
@@ -199,15 +203,17 @@ class Application(object):
|
|
|
199
203
|
config = get_config(env or "local")
|
|
200
204
|
self.config = config
|
|
201
205
|
|
|
206
|
+
# try to load func and override configuration
|
|
207
|
+
self._load_life_cycle(entry_model, func_name="on_create")
|
|
208
|
+
|
|
202
209
|
sa_ignore_mid = _sa_ignore_middleware()
|
|
203
210
|
sa_ignore_mid.__middleware_version__ = 1
|
|
204
211
|
self._middleware.append(sa_ignore_mid)
|
|
205
212
|
|
|
206
213
|
# middlewares
|
|
207
214
|
_auto_reg_module("middlewares")
|
|
208
|
-
info("Auto-registration middleware")
|
|
209
215
|
|
|
210
|
-
conf_server = config.get("server", {})
|
|
216
|
+
conf_server = self.config.get("server", {})
|
|
211
217
|
client_max_size = 1024**2 * 2
|
|
212
218
|
if conf_server is not None:
|
|
213
219
|
client_max_size = conf_server.get("client_max_size", client_max_size)
|
|
@@ -218,12 +224,16 @@ class Application(object):
|
|
|
218
224
|
client_max_size=_parse_int(client_max_size),
|
|
219
225
|
)
|
|
220
226
|
|
|
227
|
+
# load life
|
|
228
|
+
# entry = conf_server.get("entry", entry_model)
|
|
229
|
+
# self._load_life_cycle(entry)
|
|
230
|
+
|
|
221
231
|
# database
|
|
222
232
|
self.app.ignore_paths = set()
|
|
223
233
|
self.app.db = Object()
|
|
224
234
|
self.app.on_startup.append(self._init_database)
|
|
225
235
|
self.app.on_cleanup.append(self._close_database)
|
|
226
|
-
self.app.config = config
|
|
236
|
+
self.app.config = self.config
|
|
227
237
|
self.app.env = env
|
|
228
238
|
self.app.jwt_encode = self.jwt_encode
|
|
229
239
|
self.app.jwt_decode = self.jwt_decode
|
|
@@ -231,14 +241,9 @@ class Application(object):
|
|
|
231
241
|
self.app.on_startup.append(self._init_session)
|
|
232
242
|
# router:
|
|
233
243
|
_auto_reg_module("controllers")
|
|
234
|
-
info("Auto-registration controller")
|
|
235
244
|
|
|
236
245
|
self.app.add_routes(self._route_table)
|
|
237
246
|
|
|
238
|
-
# load life
|
|
239
|
-
entry = conf_server.get("entry", entry_model)
|
|
240
|
-
self._load_life_cycle(entry)
|
|
241
|
-
|
|
242
247
|
# static
|
|
243
248
|
if conf_server is not None:
|
|
244
249
|
conf_st = conf_server.get("static", {})
|
|
@@ -283,7 +288,7 @@ class Application(object):
|
|
|
283
288
|
if conf_db:
|
|
284
289
|
# for db_key in conf_db:
|
|
285
290
|
# apps.db[db_key] = await create_engine(**conf_db[db_key])
|
|
286
|
-
|
|
291
|
+
|
|
287
292
|
tasks = [create_engine(**conf_db[db_key]) for db_key in conf_db]
|
|
288
293
|
engines = await asyncio.gather(*tasks)
|
|
289
294
|
for db_key, engine in zip(conf_db.keys(), engines):
|
|
@@ -309,6 +314,8 @@ class Application(object):
|
|
|
309
314
|
cfg, qs = parse_coon(redis_url)
|
|
310
315
|
redis_url = f"{cfg['type']}://{cfg['username']}:{cfg['password']}@{cfg['host']}:{cfg['port']}/{cfg['db']}"
|
|
311
316
|
|
|
317
|
+
from redis import asyncio as aioredis
|
|
318
|
+
|
|
312
319
|
redis = await aioredis.from_url(redis_url, **qs)
|
|
313
320
|
apps.redis = redis
|
|
314
321
|
storage = redis_storage.RedisStorage(
|
|
@@ -324,6 +331,8 @@ class Application(object):
|
|
|
324
331
|
host = mcache_conf.get("host")
|
|
325
332
|
port = mcache_conf.get("port", 11211)
|
|
326
333
|
|
|
334
|
+
import aiomcache
|
|
335
|
+
|
|
327
336
|
mc = aiomcache.Client(host, port)
|
|
328
337
|
apps.memcached = mc
|
|
329
338
|
storage = memcached_storage.MemcachedStorage(
|
|
@@ -382,12 +391,14 @@ class Application(object):
|
|
|
382
391
|
def add_router(self, path, method, name, sa_ignore):
|
|
383
392
|
def inner(handler):
|
|
384
393
|
handler = RequestHandler(handler)
|
|
385
|
-
self.router
|
|
394
|
+
if self.router is not None:
|
|
395
|
+
self.router.add_route(method, path, handler, name=name)
|
|
386
396
|
return handler
|
|
387
397
|
|
|
388
398
|
if sa_ignore:
|
|
389
|
-
|
|
390
|
-
|
|
399
|
+
if self.app is not None:
|
|
400
|
+
hash_str = _sa_ignore_hash(method, path)
|
|
401
|
+
self.app.ignore_paths.add(hash_str)
|
|
391
402
|
return inner
|
|
392
403
|
|
|
393
404
|
def add_middleware(self, func):
|
|
@@ -414,27 +425,39 @@ class Application(object):
|
|
|
414
425
|
|
|
415
426
|
@property
|
|
416
427
|
def router(self):
|
|
417
|
-
|
|
428
|
+
if self.app is not None:
|
|
429
|
+
return self.app.router
|
|
430
|
+
return None
|
|
418
431
|
|
|
419
432
|
@property
|
|
420
433
|
def middlewares(self):
|
|
421
|
-
|
|
434
|
+
if self.app is not None:
|
|
435
|
+
return self.app.middlewares
|
|
436
|
+
return None
|
|
422
437
|
|
|
423
438
|
@property
|
|
424
439
|
def on_startup(self):
|
|
425
|
-
|
|
440
|
+
if self.app is not None:
|
|
441
|
+
return self.app.on_startup
|
|
442
|
+
return None
|
|
426
443
|
|
|
427
444
|
@property
|
|
428
445
|
def on_shutdown(self):
|
|
429
|
-
|
|
446
|
+
if self.app is not None:
|
|
447
|
+
return self.app.on_shutdown
|
|
448
|
+
return None
|
|
430
449
|
|
|
431
450
|
@property
|
|
432
451
|
def on_cleanup(self):
|
|
433
|
-
|
|
452
|
+
if self.app is not None:
|
|
453
|
+
return self.app.on_cleanup
|
|
454
|
+
return None
|
|
434
455
|
|
|
435
456
|
@property
|
|
436
457
|
def on_task(self):
|
|
437
|
-
|
|
458
|
+
if self.app is not None:
|
|
459
|
+
return self.app.cleanup_ctx
|
|
460
|
+
return None
|
|
438
461
|
|
|
439
462
|
|
|
440
463
|
class View(web.View):
|
|
@@ -525,8 +548,8 @@ def delete(path: str, name=None, sa_ignore=False):
|
|
|
525
548
|
def routes(path: str, sa_ignore=False):
|
|
526
549
|
if sa_ignore:
|
|
527
550
|
for method in hdrs.METH_ALL:
|
|
528
|
-
|
|
529
|
-
app.app.ignore_paths.add(
|
|
551
|
+
hash_str = _sa_ignore_hash(method, path)
|
|
552
|
+
app.app.ignore_paths.add(hash_str)
|
|
530
553
|
return app.route_table.view(path)
|
|
531
554
|
|
|
532
555
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cloudoll
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.7
|
|
4
4
|
Summary: Assist in quickly creating distributable microservices based on aiohttp, with a focus on simplicity and ease of use.
|
|
5
5
|
Home-page: https://github.com/smallerqiu/cloudoll-py
|
|
6
6
|
Author: Qiu
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "cloudoll"
|
|
7
|
-
version = "3.0.
|
|
7
|
+
version = "3.0.7"
|
|
8
8
|
description = "Assist in quickly creating distributable microservices based on aiohttp, with a focus on simplicity and ease of use."
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "Qiu", email = "smallerqiu@gmail.com"},
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "3.0.6"
|
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
from redis import asyncio as aioredis
|
|
2
|
-
from cloudoll.orm.parse import parse_coon
|
|
3
|
-
from cloudoll.orm.base import MeteBase, QueryTypes
|
|
4
|
-
from typing import Any
|
|
5
|
-
from cloudoll.logging import info, error
|
|
6
|
-
import traceback
|
|
7
|
-
import aiomysql
|
|
8
|
-
from aiomysql import Pool as MYPool
|
|
9
|
-
|
|
10
|
-
import aiopg as pg
|
|
11
|
-
from aiopg.pool import Pool as PGPool
|
|
12
|
-
from typing import Optional
|
|
13
|
-
|
|
14
|
-
__all__ = ["create_engine"]
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
async def create_engine(**kw):
|
|
18
|
-
url = kw.get("url")
|
|
19
|
-
driver = None
|
|
20
|
-
configs = {}
|
|
21
|
-
query = {}
|
|
22
|
-
|
|
23
|
-
if url is not None:
|
|
24
|
-
configs, query = parse_coon(url)
|
|
25
|
-
driver = configs["type"]
|
|
26
|
-
else:
|
|
27
|
-
driver = kw.get("type")
|
|
28
|
-
configs = kw
|
|
29
|
-
|
|
30
|
-
# info("DB Config:", configs, query)
|
|
31
|
-
|
|
32
|
-
if driver == "mysql":
|
|
33
|
-
return await Mysql().create_engine(**configs, **query)
|
|
34
|
-
elif driver == "postgres" or driver == "postgressql":
|
|
35
|
-
return await Postgres().create_engine(**configs, **query)
|
|
36
|
-
elif driver == "redis" or driver == "rediss":
|
|
37
|
-
"""
|
|
38
|
-
redis://[[username]:[password]]@localhost:6379/0
|
|
39
|
-
rediss://[[username]:[password]]@localhost:6379/0
|
|
40
|
-
"""
|
|
41
|
-
if url is None:
|
|
42
|
-
url = f"{driver}://{configs['username']}:{configs['password']}@{configs['host']}:{configs['port']}/{configs['db']}"
|
|
43
|
-
return await aioredis.from_url(url, **query)
|
|
44
|
-
else:
|
|
45
|
-
raise ValueError("Not support this database type.")
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
class Postgres(MeteBase):
|
|
49
|
-
def __init__(self, pool=None):
|
|
50
|
-
# self.pool: Optional[PGPool] = pool
|
|
51
|
-
self.driver = "postgres"
|
|
52
|
-
|
|
53
|
-
def __call__(self, *args: Any, **kwds: Any) -> Any:
|
|
54
|
-
self.__init__(*args, **kwds)
|
|
55
|
-
|
|
56
|
-
async def query(self, sql, params=None, query_type: QueryTypes = QueryTypes.COUNT, size: int = 10):
|
|
57
|
-
sql = sql.replace("?", "%s").replace("`", '"')
|
|
58
|
-
if not self.pool:
|
|
59
|
-
raise ValueError("must be create_engine first.")
|
|
60
|
-
if self.pool._closing or self.pool._closed:
|
|
61
|
-
return None
|
|
62
|
-
|
|
63
|
-
async with self.pool.acquire() as conn:
|
|
64
|
-
if conn.echo:
|
|
65
|
-
info("sql: %s ,%s", sql, params)
|
|
66
|
-
|
|
67
|
-
async with conn.cursor() as cursor:
|
|
68
|
-
# current_cursor = getattr(cursor, 'lastrowid', None)
|
|
69
|
-
if (
|
|
70
|
-
query_type == QueryTypes.CREATEBATCH
|
|
71
|
-
or query_type == QueryTypes.UPDATEBATCH
|
|
72
|
-
):
|
|
73
|
-
# aiopg don't support executemany
|
|
74
|
-
raise RuntimeError("postgres don't support executemany")
|
|
75
|
-
else:
|
|
76
|
-
await cursor.execute(sql, params)
|
|
77
|
-
|
|
78
|
-
result = None
|
|
79
|
-
|
|
80
|
-
if query_type == QueryTypes.ALL and cursor.description is not None:
|
|
81
|
-
columns = [desc[0] for desc in cursor.description]
|
|
82
|
-
rows = await cursor.fetchall()
|
|
83
|
-
result = [dict(zip(columns, row)) for row in rows]
|
|
84
|
-
return result
|
|
85
|
-
elif query_type == QueryTypes.ONE and cursor.description is not None:
|
|
86
|
-
columns = [desc[0] for desc in cursor.description]
|
|
87
|
-
row = await cursor.fetchone()
|
|
88
|
-
result = dict(zip(columns, row)) if row else {}
|
|
89
|
-
return result
|
|
90
|
-
elif query_type == QueryTypes.MANY and cursor.description is not None:
|
|
91
|
-
columns = [desc[0] for desc in cursor.description]
|
|
92
|
-
rows = await cursor.fetchmany(size)
|
|
93
|
-
result = [dict(zip(columns, row)) for row in rows]
|
|
94
|
-
return result
|
|
95
|
-
elif query_type == QueryTypes.COUNT:
|
|
96
|
-
result = await cursor.fetchone()
|
|
97
|
-
count = 0
|
|
98
|
-
if result is None:
|
|
99
|
-
return count
|
|
100
|
-
for value in result:
|
|
101
|
-
count = value
|
|
102
|
-
return count
|
|
103
|
-
elif query_type == QueryTypes.GROUP_COUNT:
|
|
104
|
-
result = await cursor.fetchall()
|
|
105
|
-
return 0 if not result else len(result)
|
|
106
|
-
elif query_type == QueryTypes.CREATE:
|
|
107
|
-
result = cursor.rowcount > 0
|
|
108
|
-
id = cursor.lastrowid
|
|
109
|
-
return result, id
|
|
110
|
-
elif query_type == QueryTypes.CREATEBATCH:
|
|
111
|
-
count = cursor.rowcount
|
|
112
|
-
id = cursor.lastrowid
|
|
113
|
-
return count, id
|
|
114
|
-
elif query_type == QueryTypes.UPDATE:
|
|
115
|
-
return cursor.rowcount > 0
|
|
116
|
-
elif query_type == QueryTypes.UPDATEBATCH:
|
|
117
|
-
return cursor.rowcount
|
|
118
|
-
elif query_type == QueryTypes.DELETE:
|
|
119
|
-
return cursor.rowcount > 0
|
|
120
|
-
|
|
121
|
-
async def create_engine(self, **kw):
|
|
122
|
-
try:
|
|
123
|
-
host = (kw.get("host", "localhost"),)
|
|
124
|
-
port = (kw.get("port", 5432),)
|
|
125
|
-
user = (kw.get("username"),)
|
|
126
|
-
password = (str(kw.get("password", "")),)
|
|
127
|
-
db = (kw.get("db"),)
|
|
128
|
-
dsn = f"dbname={db[0]} user={user[0]} password={password[0]} host={host[0]} port={port[0]}" # aiopg
|
|
129
|
-
# dsn = f"postgres://{user[0]}:{password[0]}@{host[0]}:{port[0]}/{db[0]}" # asyncpg
|
|
130
|
-
self.pool = await pg.create_pool(
|
|
131
|
-
dsn=dsn,
|
|
132
|
-
timeout=float(kw.get("timeout", 60)),
|
|
133
|
-
echo=kw.get("echo", False), # aiopg
|
|
134
|
-
# max_size=kw.get("maxsize", 10), # asyncpg
|
|
135
|
-
# min_size=kw.get("minsize", 5), # asyncpg
|
|
136
|
-
maxsize=kw.get("maxsize", 10), # aiopg
|
|
137
|
-
minsize=kw.get("minsize", 5), # aiopg
|
|
138
|
-
)
|
|
139
|
-
info(f"Database connection successfuly for postgres/{kw.get('db')}")
|
|
140
|
-
except Exception as e:
|
|
141
|
-
error(e)
|
|
142
|
-
# print(traceback.format_exc())
|
|
143
|
-
error(f"Database connection failed,the instance : postgres/{kw.get('db')}")
|
|
144
|
-
|
|
145
|
-
return self
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
class AttrDict(dict):
|
|
149
|
-
"""Dict that can get attribute by dot, and doesn't raise KeyError"""
|
|
150
|
-
|
|
151
|
-
def __getattr__(self, name):
|
|
152
|
-
try:
|
|
153
|
-
return self[name]
|
|
154
|
-
except KeyError:
|
|
155
|
-
return None
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
class AttrDictCursor(aiomysql.DictCursor):
|
|
159
|
-
dict_type = AttrDict
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
class Mysql(MeteBase):
|
|
163
|
-
def __init__(self, pool=None):
|
|
164
|
-
# self.pool: Optional[MYPool] = pool
|
|
165
|
-
self.driver = "mysql"
|
|
166
|
-
|
|
167
|
-
def __call__(self, *args: Any, **kwds: Any) -> Any:
|
|
168
|
-
self.__init__(*args, **kwds)
|
|
169
|
-
return self
|
|
170
|
-
|
|
171
|
-
async def query(self, sql, params=None, query_type: QueryTypes = QueryTypes.ONE, size: int = 10):
|
|
172
|
-
sql = sql.replace("?", "%s")
|
|
173
|
-
if not self.pool:
|
|
174
|
-
raise ValueError("must be create_engine first.")
|
|
175
|
-
if self.pool._closing or self.pool._closed:
|
|
176
|
-
return None
|
|
177
|
-
async with self.pool.acquire() as conn:
|
|
178
|
-
if conn.echo:
|
|
179
|
-
info("sql: %s , %s", sql, params)
|
|
180
|
-
|
|
181
|
-
async with conn.cursor() as cursor:
|
|
182
|
-
# current_cursor = getattr(cursor, 'lastrowid', None)
|
|
183
|
-
if (
|
|
184
|
-
query_type == QueryTypes.CREATEBATCH
|
|
185
|
-
or query_type == QueryTypes.UPDATEBATCH
|
|
186
|
-
):
|
|
187
|
-
await cursor.executemany(sql, params)
|
|
188
|
-
else:
|
|
189
|
-
await cursor.execute(sql, params)
|
|
190
|
-
|
|
191
|
-
await conn.commit()
|
|
192
|
-
|
|
193
|
-
if query_type == QueryTypes.ALL:
|
|
194
|
-
return await cursor.fetchall()
|
|
195
|
-
elif query_type == QueryTypes.ONE:
|
|
196
|
-
return await cursor.fetchone()
|
|
197
|
-
elif query_type == QueryTypes.MANY:
|
|
198
|
-
return await cursor.fetchmany(size)
|
|
199
|
-
elif query_type == QueryTypes.COUNT:
|
|
200
|
-
rows = await cursor.fetchone()
|
|
201
|
-
count = 0
|
|
202
|
-
if rows is None:
|
|
203
|
-
return count
|
|
204
|
-
for row in rows:
|
|
205
|
-
count = rows[row]
|
|
206
|
-
return count
|
|
207
|
-
elif query_type == QueryTypes.GROUP_COUNT:
|
|
208
|
-
result = await cursor.fetchall()
|
|
209
|
-
return 0 if not result else len(result)
|
|
210
|
-
elif query_type == QueryTypes.CREATE:
|
|
211
|
-
result = cursor.rowcount > 0
|
|
212
|
-
id = cursor.lastrowid
|
|
213
|
-
return result, id
|
|
214
|
-
elif query_type == QueryTypes.CREATEBATCH:
|
|
215
|
-
count = cursor.rowcount
|
|
216
|
-
id = cursor.lastrowid
|
|
217
|
-
return count, id
|
|
218
|
-
elif query_type == QueryTypes.UPDATE:
|
|
219
|
-
return cursor.rowcount > 0
|
|
220
|
-
elif query_type == QueryTypes.UPDATEBATCH:
|
|
221
|
-
return cursor.rowcount
|
|
222
|
-
elif query_type == QueryTypes.DELETE:
|
|
223
|
-
return cursor.rowcount > 0
|
|
224
|
-
|
|
225
|
-
self.pool.release(conn)
|
|
226
|
-
|
|
227
|
-
async def create_engine(self, loop=None, **kw):
|
|
228
|
-
try:
|
|
229
|
-
self.pool = await aiomysql.create_pool(
|
|
230
|
-
host=kw.get("host", "localhost"),
|
|
231
|
-
port=kw.get("port", 3306),
|
|
232
|
-
user=kw.get("username"),
|
|
233
|
-
password=str(kw.get("password", "")),
|
|
234
|
-
db=kw.get("db"),
|
|
235
|
-
echo=kw.get("echo", False),
|
|
236
|
-
charset=kw.get("charset", "utf8"),
|
|
237
|
-
autocommit=False, # kw.get("autocommit", False),
|
|
238
|
-
maxsize=kw.get("maxsize", 10),
|
|
239
|
-
minsize=kw.get("minsize", 5),
|
|
240
|
-
cursorclass=AttrDictCursor,
|
|
241
|
-
loop=loop,
|
|
242
|
-
)
|
|
243
|
-
info(f"Database connection successfuly for mysql/{kw.get('db')}.")
|
|
244
|
-
except Exception as e:
|
|
245
|
-
# print(traceback.format_exc())
|
|
246
|
-
error(f"Database connection failed,the instance : mysql/{kw.get('db')}")
|
|
247
|
-
return self
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
from cloudoll.orm import Mysql
|
|
5
|
-
|
|
6
|
-
# for config folder conf.{env}.yaml
|
|
7
|
-
|
|
8
|
-
database:
|
|
9
|
-
mysql:
|
|
10
|
-
type: mysql # important ,default mysql
|
|
11
|
-
host: 127.0.0.1
|
|
12
|
-
port: 3306
|
|
13
|
-
user: root
|
|
14
|
-
password: 123456
|
|
15
|
-
db: test
|
|
16
|
-
charset:utf8mb4
|
|
17
|
-
pool_size:5
|
|
18
|
-
|
|
19
|
-
# to get yaml config
|
|
20
|
-
config = ....(database.mysql)
|
|
21
|
-
|
|
22
|
-
pool = await Mysql().create_engine(**config)
|
|
23
|
-
result = await pool.query("sql")
|
|
24
|
-
"""
|
|
25
|
-
__author__ = "Qiu / smallerqiu@gmail.com"
|
|
26
|
-
|
|
27
|
-
from aiomysql import create_pool, DictCursor
|
|
28
|
-
|
|
29
|
-
# from aiomysql.cursors import Cursor
|
|
30
|
-
# from aiomysql.connection import Connection
|
|
31
|
-
from cloudoll.logging import error
|
|
32
|
-
|
|
33
|
-
# from inspect import isclass, isfunction
|
|
34
|
-
from cloudoll.orm.base import MeteBase
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
class AttrDict(dict):
|
|
38
|
-
"""Dict that can get attribute by dot, and doesn't raise KeyError"""
|
|
39
|
-
|
|
40
|
-
def __getattr__(self, name):
|
|
41
|
-
try:
|
|
42
|
-
return self[name]
|
|
43
|
-
except KeyError:
|
|
44
|
-
return None
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
class AttrDictCursor(DictCursor):
|
|
48
|
-
dict_type = AttrDict
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
class Mysql(MeteBase):
|
|
52
|
-
# def __init__(self):
|
|
53
|
-
# self.pool: Optional[Pool] = None
|
|
54
|
-
# self.cursor: Cursor = None
|
|
55
|
-
# self.conn: Connection = None
|
|
56
|
-
# self.__MODELS__ = []
|
|
57
|
-
|
|
58
|
-
# def __call__(cls, *args: Any, **kwds: Any) -> Any:
|
|
59
|
-
# cls.__init__(*args, **kwds)
|
|
60
|
-
# return cls()
|
|
61
|
-
|
|
62
|
-
# @classmethod
|
|
63
|
-
async def create_engine(self, loop=None, **kw):
|
|
64
|
-
try:
|
|
65
|
-
self.pool = await create_pool(
|
|
66
|
-
host=kw.get("host", "localhost"),
|
|
67
|
-
port=kw.get("port", 3306),
|
|
68
|
-
user=kw.get("user"),
|
|
69
|
-
password=str(kw.get("password", "")),
|
|
70
|
-
db=kw.get("db"),
|
|
71
|
-
echo=kw.get("echo", False),
|
|
72
|
-
charset=kw.get("charset", "utf8"),
|
|
73
|
-
autocommit=False, # kw.get("autocommit", False),
|
|
74
|
-
maxsize=kw.get("maxsize", 10),
|
|
75
|
-
minsize=kw.get("pool_size", 5),
|
|
76
|
-
cursorclass=AttrDictCursor,
|
|
77
|
-
connect_timeout=kw.get("connect_timeout", 10),
|
|
78
|
-
loop=loop,
|
|
79
|
-
)
|
|
80
|
-
except Exception as e:
|
|
81
|
-
error(f"Database connection failed,the instance : {kw.get('db')}")
|
|
82
|
-
return self
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
from cloudoll.orm.postgres import Postgres
|
|
5
|
-
|
|
6
|
-
# for config folder conf.{env}.yaml
|
|
7
|
-
|
|
8
|
-
database:
|
|
9
|
-
pg:
|
|
10
|
-
type: postgres # important ,default mysql
|
|
11
|
-
host: 127.0.0.1
|
|
12
|
-
port: 3306
|
|
13
|
-
user: root
|
|
14
|
-
password: 123456
|
|
15
|
-
db: test
|
|
16
|
-
echo: True
|
|
17
|
-
timeout: 60
|
|
18
|
-
pool_size:5
|
|
19
|
-
|
|
20
|
-
# to get yaml config
|
|
21
|
-
config = ....(database.postgres)
|
|
22
|
-
|
|
23
|
-
pool = await Postgres().create_engine(**config)
|
|
24
|
-
result = await pool.query("sql")
|
|
25
|
-
"""
|
|
26
|
-
__author__ = "Qiu / smallerqiu@gmail.com"
|
|
27
|
-
|
|
28
|
-
from aiopg import create_pool
|
|
29
|
-
from cloudoll.logging import error
|
|
30
|
-
from cloudoll.orm.base import MeteBase
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class Postgres(MeteBase):
|
|
34
|
-
# def __init__(self):
|
|
35
|
-
# self.pool: Optional[Pool] = None
|
|
36
|
-
# self.cursor: Optional[Cursor] = None
|
|
37
|
-
# self.conn: Optional[Connection] = None
|
|
38
|
-
# self.__MODELS__ = []
|
|
39
|
-
|
|
40
|
-
# def __call__(cls, *args: Any, **kwds: Any) -> Any:
|
|
41
|
-
# cls.__init__(*args, **kwds)
|
|
42
|
-
# return cls()
|
|
43
|
-
|
|
44
|
-
@classmethod
|
|
45
|
-
async def create_engine(cls, **kw):
|
|
46
|
-
try:
|
|
47
|
-
host = (kw.get("host", "localhost"),)
|
|
48
|
-
port = (kw.get("port", 5432),)
|
|
49
|
-
user = (kw.get("user"),)
|
|
50
|
-
password = (str(kw.get("password", "")),)
|
|
51
|
-
db = (kw.get("db"),)
|
|
52
|
-
dsn = f"dbname={db[0]} user={user[0]} password={password[0]} host={host[0]} port={port[0]}"
|
|
53
|
-
cls.pool = await create_pool(
|
|
54
|
-
dsn=dsn,
|
|
55
|
-
timeout=kw.get("timeout", 10.0),
|
|
56
|
-
enable_json=kw.get("enable_json", False),
|
|
57
|
-
enable_hstore=kw.get("enable_hstore", False),
|
|
58
|
-
enable_uuid=kw.get("enable_uuid", False),
|
|
59
|
-
pool_recycle=kw.get("pool_recycle", 10),
|
|
60
|
-
echo=kw.get("echo", False),
|
|
61
|
-
minsize=kw.get("pool_size", 5),
|
|
62
|
-
maxsize=kw.get("pool_size", 10),
|
|
63
|
-
)
|
|
64
|
-
except Exception as e:
|
|
65
|
-
# error(e)
|
|
66
|
-
error(f"Database connection failed,the instance : {kw.get('db')}")
|
|
67
|
-
|
|
68
|
-
return cls
|
|
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
|
|
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
|