cloudoll 2.2.23__tar.gz → 2.2.26__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-2.2.23 → cloudoll-2.2.26}/PKG-INFO +14 -1
- cloudoll-2.2.26/cloudoll/__init__.py +1 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/orm/__init__.py +108 -18
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/orm/base.py +8 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/orm/field.py +62 -35
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/orm/model.py +69 -12
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/robot/feishu.py +2 -1
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/utils/m2d.py +2 -1
- cloudoll-2.2.26/cloudoll/utils/snowflake.py +86 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll.egg-info/PKG-INFO +14 -1
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll.egg-info/SOURCES.txt +1 -0
- cloudoll-2.2.23/cloudoll/__init__.py +0 -1
- {cloudoll-2.2.23 → cloudoll-2.2.26}/LICENSE +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/README.md +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/__main__.py +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/cli.py +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/error/__init__.py +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/error/errors.py +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/logging/__init__.py +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/mail/__init__.py +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/mail/smtp.py +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/orm/err.py +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/orm/mysql.py +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/orm/parse.py +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/orm/postgres.py +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/robot/__init__.py +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/robot/dingtalk.py +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/robot/slack.py +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/utils/__init__.py +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/utils/common.py +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/utils/patch_loop.py +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/utils/watch.py +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/web/__init__.py +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/web/exception.py +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/web/html.py +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/web/http.py +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/web/jwt.py +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll/web/settings.py +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll.egg-info/dependency_links.txt +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll.egg-info/entry_points.txt +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll.egg-info/requires.txt +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/cloudoll.egg-info/top_level.txt +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/setup.cfg +0 -0
- {cloudoll-2.2.23 → cloudoll-2.2.26}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cloudoll
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.26
|
|
4
4
|
Summary: 辅助快速创建可分布的微服务。
|
|
5
5
|
Home-page: https://gitee.com/chuchur/cloudoll-py
|
|
6
6
|
Author: chuchur
|
|
@@ -15,6 +15,19 @@ Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
|
15
15
|
Requires-Python: >=3.6.0
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
|
+
Requires-Dist: watchfiles
|
|
19
|
+
Requires-Dist: curl_cffi
|
|
20
|
+
Requires-Dist: colorlog
|
|
21
|
+
Requires-Dist: aiomysql
|
|
22
|
+
Requires-Dist: aiopg
|
|
23
|
+
Requires-Dist: aiohttp
|
|
24
|
+
Requires-Dist: redis[hiredis]
|
|
25
|
+
Requires-Dist: aiomcache
|
|
26
|
+
Requires-Dist: jinja2
|
|
27
|
+
Requires-Dist: pyjwt
|
|
28
|
+
Requires-Dist: aiohttp_session[secure]
|
|
29
|
+
Requires-Dist: PyYAML
|
|
30
|
+
Requires-Dist: click
|
|
18
31
|
|
|
19
32
|
|
|
20
33
|
# cloudoll 云端玩具
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.2.26"
|
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
from redis import asyncio as aioredis
|
|
2
2
|
from .parse import parse_coon
|
|
3
|
-
import aiopg, aiomysql
|
|
4
3
|
from .base import MeteBase, QueryTypes
|
|
5
4
|
from typing import Any
|
|
6
5
|
from ..logging import print_info, print_error
|
|
7
6
|
import traceback
|
|
7
|
+
import aiomysql
|
|
8
8
|
from aiomysql import Pool as MYPool
|
|
9
|
+
|
|
10
|
+
import aiopg as pg
|
|
9
11
|
from aiopg.pool import Pool as PGPool
|
|
10
12
|
|
|
13
|
+
# import asyncpg as pg
|
|
14
|
+
# from asyncpg.pool import Pool as PGPool
|
|
15
|
+
|
|
16
|
+
__ALL__ = "create_engine"
|
|
17
|
+
|
|
11
18
|
|
|
12
19
|
async def create_engine(**kw):
|
|
13
20
|
url = kw.get("url")
|
|
@@ -48,23 +55,85 @@ class Postgres(MeteBase):
|
|
|
48
55
|
def __call__(self, *args: Any, **kwds: Any) -> Any:
|
|
49
56
|
self.__init__(*args, **kwds)
|
|
50
57
|
|
|
58
|
+
# async def query(self, sql, params=None, query_type: QueryTypes = 2, size: int = 10):
|
|
59
|
+
# sql = sql.replace("?", "%s").replace("`", "")
|
|
60
|
+
# print_info("sql", sql, params)
|
|
61
|
+
# if not self.pool:
|
|
62
|
+
# raise ValueError("must be create_engine first.")
|
|
63
|
+
# if self.pool._closing or self.pool._closed:
|
|
64
|
+
# return None
|
|
65
|
+
|
|
66
|
+
# # async with self.pool.acquire() as conn:
|
|
67
|
+
# async with self.pool.acquire() as conn:
|
|
68
|
+
# # current_cursor = getattr(cursor, 'lastrowid', None)
|
|
69
|
+
# if (
|
|
70
|
+
# query_type == QueryTypes.CREATEBATCH
|
|
71
|
+
# or query_type == QueryTypes.UPDATEBATCH
|
|
72
|
+
# ):
|
|
73
|
+
# result = await conn.executemany(sql, params) # aiopg don't support executemany
|
|
74
|
+
# else:
|
|
75
|
+
# result = await conn.execute(sql, params)
|
|
76
|
+
|
|
77
|
+
# # result = None
|
|
78
|
+
|
|
79
|
+
# return result
|
|
80
|
+
|
|
81
|
+
# if query_type == QueryTypes.ALL:
|
|
82
|
+
# columns = [desc[0] for desc in cursor.description]
|
|
83
|
+
# rows = await cursor.fetchall()
|
|
84
|
+
# result = [dict(zip(columns, row)) for row in rows]
|
|
85
|
+
# return result
|
|
86
|
+
# elif query_type == QueryTypes.ONE:
|
|
87
|
+
# columns = [desc[0] for desc in cursor.description]
|
|
88
|
+
# row = await cursor.fetchone()
|
|
89
|
+
# # print("rows_reuslt", rows)
|
|
90
|
+
# result = dict(zip(columns, row)) if row else {}
|
|
91
|
+
# return result
|
|
92
|
+
# elif query_type == QueryTypes.MANY:
|
|
93
|
+
# columns = [desc[0] for desc in cursor.description]
|
|
94
|
+
# rows = await cursor.fetchmany(size)
|
|
95
|
+
# result = [dict(zip(columns, row)) for row in rows]
|
|
96
|
+
# return result
|
|
97
|
+
# elif query_type == QueryTypes.COUNT:
|
|
98
|
+
# rs = await cursor.fetchone()
|
|
99
|
+
# return rs[0] if row else 0
|
|
100
|
+
# elif query_type == QueryTypes.CREATE:
|
|
101
|
+
# result = cursor.rowcount > 0
|
|
102
|
+
# id = cursor.lastrowid
|
|
103
|
+
# return result, id
|
|
104
|
+
# elif query_type == QueryTypes.CREATEBATCH:
|
|
105
|
+
# count = cursor.rowcount
|
|
106
|
+
# id = cursor.lastrowid
|
|
107
|
+
# return count, id
|
|
108
|
+
# elif query_type == QueryTypes.UPDATE:
|
|
109
|
+
# return cursor.rowcount > 0
|
|
110
|
+
# elif query_type == QueryTypes.UPDATEBATCH:
|
|
111
|
+
# return cursor.rowcount
|
|
112
|
+
# elif query_type == QueryTypes.DELETE:
|
|
113
|
+
# return cursor.rowcount > 0
|
|
114
|
+
|
|
115
|
+
# self.pool.release(cursor)
|
|
116
|
+
|
|
51
117
|
async def query(self, sql, params=None, query_type: QueryTypes = 2, size: int = 10):
|
|
52
|
-
sql = sql.replace("?", "%s").replace(
|
|
118
|
+
sql = sql.replace("?", "%s").replace('`', '"')
|
|
53
119
|
print_info("sql", sql, params)
|
|
54
120
|
if not self.pool:
|
|
55
121
|
raise ValueError("must be create_engine first.")
|
|
56
122
|
if self.pool._closing or self.pool._closed:
|
|
57
123
|
return None
|
|
124
|
+
|
|
58
125
|
async with self.pool.acquire() as conn:
|
|
59
126
|
async with conn.cursor() as cursor:
|
|
60
127
|
# current_cursor = getattr(cursor, 'lastrowid', None)
|
|
61
|
-
|
|
128
|
+
if (
|
|
129
|
+
query_type == QueryTypes.CREATEBATCH
|
|
130
|
+
or query_type == QueryTypes.UPDATEBATCH
|
|
131
|
+
):
|
|
132
|
+
# aiopg don't support executemany
|
|
133
|
+
await cursor.executemany(sql, params)
|
|
134
|
+
else:
|
|
135
|
+
await cursor.execute(sql, params)
|
|
62
136
|
|
|
63
|
-
# if query_type.value > 4:
|
|
64
|
-
# if self.driver == "mysql":
|
|
65
|
-
# print(columns)
|
|
66
|
-
# await conn.commit()
|
|
67
|
-
# elif self.driver == "postgres":
|
|
68
137
|
result = None
|
|
69
138
|
|
|
70
139
|
if query_type == QueryTypes.ALL:
|
|
@@ -90,12 +159,18 @@ class Postgres(MeteBase):
|
|
|
90
159
|
result = cursor.rowcount > 0
|
|
91
160
|
id = cursor.lastrowid
|
|
92
161
|
return result, id
|
|
162
|
+
elif query_type == QueryTypes.CREATEBATCH:
|
|
163
|
+
count = cursor.rowcount
|
|
164
|
+
id = cursor.lastrowid
|
|
165
|
+
return count, id
|
|
93
166
|
elif query_type == QueryTypes.UPDATE:
|
|
94
167
|
return cursor.rowcount > 0
|
|
168
|
+
elif query_type == QueryTypes.UPDATEBATCH:
|
|
169
|
+
return cursor.rowcount
|
|
95
170
|
elif query_type == QueryTypes.DELETE:
|
|
96
171
|
return cursor.rowcount > 0
|
|
97
172
|
|
|
98
|
-
self.pool.release(
|
|
173
|
+
self.pool.release(cursor)
|
|
99
174
|
|
|
100
175
|
async def create_engine(self, **kw):
|
|
101
176
|
try:
|
|
@@ -104,17 +179,20 @@ class Postgres(MeteBase):
|
|
|
104
179
|
user = (kw.get("username"),)
|
|
105
180
|
password = (str(kw.get("password", "")),)
|
|
106
181
|
db = (kw.get("db"),)
|
|
107
|
-
dsn = f"dbname={db[0]} user={user[0]} password={password[0]} host={host[0]} port={port[0]}"
|
|
108
|
-
|
|
182
|
+
dsn = f"dbname={db[0]} user={user[0]} password={password[0]} host={host[0]} port={port[0]}" # aiopg
|
|
183
|
+
# dsn = f"postgres://{user[0]}:{password[0]}@{host[0]}:{port[0]}/{db[0]}" #asyncpg
|
|
184
|
+
self.pool = await pg.create_pool(
|
|
109
185
|
dsn=dsn,
|
|
110
186
|
timeout=kw.get("timeout"),
|
|
111
|
-
echo=kw.get("echo", False),
|
|
112
|
-
|
|
113
|
-
|
|
187
|
+
echo=kw.get("echo", False), # aiopg
|
|
188
|
+
# max_size=kw.get("maxsize", 10), #asyncpg
|
|
189
|
+
# min_size=kw.get("minsize", 5), #asyncpg
|
|
190
|
+
maxsize=kw.get("maxsize", 10), # aiopg
|
|
191
|
+
minsize=kw.get("minsize", 5), # aiopg
|
|
114
192
|
)
|
|
115
193
|
print_info(f"Database connection successfuly for postgres/{kw.get('db')}")
|
|
116
194
|
except Exception as e:
|
|
117
|
-
|
|
195
|
+
print_error(e)
|
|
118
196
|
# print(traceback.format_exc())
|
|
119
197
|
print_error(
|
|
120
198
|
f"Database connection failed,the instance : postgres/{kw.get('db')}"
|
|
@@ -148,7 +226,7 @@ class Mysql(MeteBase):
|
|
|
148
226
|
|
|
149
227
|
async def query(self, sql, params=None, query_type: QueryTypes = 2, size: int = 10):
|
|
150
228
|
sql = sql.replace("?", "%s")
|
|
151
|
-
print_info("sql", sql, params)
|
|
229
|
+
# print_info("sql", sql, params)
|
|
152
230
|
if not self.pool:
|
|
153
231
|
raise ValueError("must be create_engine first.")
|
|
154
232
|
if self.pool._closing or self.pool._closed:
|
|
@@ -156,7 +234,13 @@ class Mysql(MeteBase):
|
|
|
156
234
|
async with self.pool.acquire() as conn:
|
|
157
235
|
async with conn.cursor() as cursor:
|
|
158
236
|
# current_cursor = getattr(cursor, 'lastrowid', None)
|
|
159
|
-
|
|
237
|
+
if (
|
|
238
|
+
query_type == QueryTypes.CREATEBATCH
|
|
239
|
+
or query_type == QueryTypes.UPDATEBATCH
|
|
240
|
+
):
|
|
241
|
+
await cursor.executemany(sql, params)
|
|
242
|
+
else:
|
|
243
|
+
await cursor.execute(sql, params)
|
|
160
244
|
|
|
161
245
|
# if query_type.value > 4:
|
|
162
246
|
# if self.driver == "mysql":
|
|
@@ -180,8 +264,14 @@ class Mysql(MeteBase):
|
|
|
180
264
|
result = cursor.rowcount > 0
|
|
181
265
|
id = cursor.lastrowid
|
|
182
266
|
return result, id
|
|
267
|
+
elif query_type == QueryTypes.CREATEBATCH:
|
|
268
|
+
count = cursor.rowcount
|
|
269
|
+
id = cursor.lastrowid
|
|
270
|
+
return count, id
|
|
183
271
|
elif query_type == QueryTypes.UPDATE:
|
|
184
272
|
return cursor.rowcount > 0
|
|
273
|
+
elif query_type == QueryTypes.UPDATEBATCH:
|
|
274
|
+
return cursor.rowcount
|
|
185
275
|
elif query_type == QueryTypes.DELETE:
|
|
186
276
|
return cursor.rowcount > 0
|
|
187
277
|
|
|
@@ -199,7 +289,7 @@ class Mysql(MeteBase):
|
|
|
199
289
|
charset=kw.get("charset", "utf8"),
|
|
200
290
|
autocommit=False, # kw.get("autocommit", False),
|
|
201
291
|
maxsize=kw.get("maxsize", 10),
|
|
202
|
-
minsize=kw.get("
|
|
292
|
+
minsize=kw.get("minsize", 5),
|
|
203
293
|
cursorclass=AttrDictCursor,
|
|
204
294
|
loop=loop,
|
|
205
295
|
)
|
|
@@ -9,6 +9,8 @@ class QueryTypes(Enum):
|
|
|
9
9
|
CREATE = 5
|
|
10
10
|
UPDATE = 6
|
|
11
11
|
DELETE = 7
|
|
12
|
+
CREATEBATCH = 8
|
|
13
|
+
UPDATEBATCH = 9
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
class MeteBase:
|
|
@@ -60,9 +62,15 @@ class MeteBase:
|
|
|
60
62
|
|
|
61
63
|
async def update(self, sql, params):
|
|
62
64
|
return await self.query(sql, params, QueryTypes.UPDATE)
|
|
65
|
+
|
|
66
|
+
async def update_batch(self, sql, params):
|
|
67
|
+
return await self.query(sql, params, QueryTypes.UPDATEBATCH)
|
|
63
68
|
|
|
64
69
|
async def delete(self, sql, params):
|
|
65
70
|
return await self.query(sql, params, QueryTypes.DELETE)
|
|
66
71
|
|
|
67
72
|
async def create(self, sql, params):
|
|
68
73
|
return await self.query(sql, params, QueryTypes.CREATE)
|
|
74
|
+
|
|
75
|
+
async def create_batch(self, sql, params):
|
|
76
|
+
return await self.query(sql, params, QueryTypes.CREATEBATCH)
|
|
@@ -35,13 +35,18 @@ OP = objdict(
|
|
|
35
35
|
IS_THIS_WEEK="THIS_WEEK",
|
|
36
36
|
IS_THIS_MONTH="THIS_MONTH",
|
|
37
37
|
IS_THIS_YEAR="THIS_YEAR",
|
|
38
|
+
LASTED_HOURS="LASTED_HOURS",
|
|
39
|
+
LASTED_MINUTES="LASTED_MINUTES",
|
|
40
|
+
LASTED_SECONDS="LASTED_SECONDS",
|
|
41
|
+
LASTED_YEARS="LASTED_YEARS",
|
|
42
|
+
LASTED_MONTHS="LASTED_MONTHS",
|
|
43
|
+
LASTED_DAYS="LASTED_DAYS",
|
|
44
|
+
BEFORE_YEARS="BEFORE_YEARS",
|
|
45
|
+
BEFORE_MONTHS="BEFORE_MONTHS",
|
|
38
46
|
BEFORE_DAYS="BEFORE_DAYS",
|
|
39
47
|
BEFORE_HOURS="BEFORE_HOURS",
|
|
40
48
|
BEFORE_MINUTES="BEFORE_MINUTES",
|
|
41
|
-
|
|
42
|
-
AFTER_YEARS="AFTER_YEARS",
|
|
43
|
-
AFTER_MONTHS="AFTER_MONTHS",
|
|
44
|
-
BEFORE_MONTHS="BEFORE_MONTHS",
|
|
49
|
+
BEFORE_SECONDS="BEFORE_SECONDS",
|
|
45
50
|
DATE_FORMAT="DATE_FORMAT",
|
|
46
51
|
JSON_CONTAINS_OBJECT="JSON_CONTAINS_OBJECT",
|
|
47
52
|
JSON_CONTAINS_ARRAY="JSON_CONTAINS_ARRAY",
|
|
@@ -138,6 +143,30 @@ class FieldBase(object):
|
|
|
138
143
|
def is_this_year(self):
|
|
139
144
|
return Function(self, OP.IS_THIS_YEAR)
|
|
140
145
|
|
|
146
|
+
def lasted_hours(self, args):
|
|
147
|
+
return Function(self, OP.LASTED_HOURS, args)
|
|
148
|
+
|
|
149
|
+
def lasted_minutes(self, args):
|
|
150
|
+
return Function(self, OP.LASTED_MINUTES, args)
|
|
151
|
+
|
|
152
|
+
def lasted_seconds(self, args):
|
|
153
|
+
return Function(self, OP.LASTED_SECONDS, args)
|
|
154
|
+
|
|
155
|
+
def lasted_years(self, args):
|
|
156
|
+
return Function(self, OP.LASTED_YEARS, args)
|
|
157
|
+
|
|
158
|
+
def lasted_months(self, args):
|
|
159
|
+
return Function(self, OP.LASTED_MONTHS, args)
|
|
160
|
+
|
|
161
|
+
def lasted_days(self, args):
|
|
162
|
+
return Function(self, OP.LASTED_DAYS, args)
|
|
163
|
+
|
|
164
|
+
def before_months(self, args):
|
|
165
|
+
return Function(self, OP.BEFORE_MONTHS, args)
|
|
166
|
+
|
|
167
|
+
def before_years(self, args):
|
|
168
|
+
return Function(self, OP.BEFORE_YEARS, args)
|
|
169
|
+
|
|
141
170
|
def before_days(self, args):
|
|
142
171
|
return Function(self, OP.BEFORE_DAYS, args)
|
|
143
172
|
|
|
@@ -147,17 +176,8 @@ class FieldBase(object):
|
|
|
147
176
|
def before_minutes(self, args):
|
|
148
177
|
return Function(self, OP.BEFORE_MINUTES, args)
|
|
149
178
|
|
|
150
|
-
def
|
|
151
|
-
return Function(self, OP.
|
|
152
|
-
|
|
153
|
-
def before_months(self, args):
|
|
154
|
-
return Function(self, OP.BEFORE_MONTHS, args)
|
|
155
|
-
|
|
156
|
-
def after_months(self, args):
|
|
157
|
-
return Function(self, OP.AFTER_MONTHS, args)
|
|
158
|
-
|
|
159
|
-
def after_years(self, args):
|
|
160
|
-
return Function(self, OP.AFTER_YEARS, args)
|
|
179
|
+
def before_seconds(self, args):
|
|
180
|
+
return Function(self, OP.BEFORE_SECOBDS, args)
|
|
161
181
|
|
|
162
182
|
def json_contains_object(self, key, value):
|
|
163
183
|
return Function(self, OP.JSON_CONTAINS_OBJECT, (key, value))
|
|
@@ -238,20 +258,33 @@ class Function(FieldBase):
|
|
|
238
258
|
return f"MONTH({col_name}) = MONTH(CURDATE())", None
|
|
239
259
|
elif op == OP.IS_THIS_YEAR:
|
|
240
260
|
return f"YEAR({col_name}) = YEAR(CURDATE())", None
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
elif op == OP.BEFORE_MINUTES:
|
|
261
|
+
|
|
262
|
+
elif op == OP.LASTED_SECONDS:
|
|
263
|
+
return f"{col_name} >= NOW() - INTERVAL {self.rpt} SECOND", None
|
|
264
|
+
elif op == OP.LASTED_MINUTES:
|
|
246
265
|
return f"{col_name} >= NOW() - INTERVAL {self.rpt} MINUTE", None
|
|
266
|
+
elif op == OP.LASTED_HOURS:
|
|
267
|
+
return f"{col_name} >= NOW() - INTERVAL {self.rpt} HOUR", None
|
|
268
|
+
elif op == OP.LASTED_YEARS:
|
|
269
|
+
return f"{col_name} >= NOW() - INTERVAL {self.rpt} YEAR", None
|
|
270
|
+
elif op == OP.LASTED_MONTHS:
|
|
271
|
+
return f"{col_name} >= NOW() - INTERVAL {self.rpt} MONTH", None
|
|
272
|
+
elif op == OP.LASTED_DAYS:
|
|
273
|
+
return f"DATE({col_name}) >= CURDATE() - INTERVAL {self.rpt} DAY", None
|
|
274
|
+
|
|
247
275
|
elif op == OP.BEFORE_YEARS:
|
|
248
|
-
return f"{col_name}
|
|
276
|
+
return f"{col_name} < NOW() - INTERVAL {self.rpt} YEAR", None
|
|
249
277
|
elif op == OP.BEFORE_MONTHS:
|
|
250
|
-
return f"{col_name}
|
|
251
|
-
elif op == OP.
|
|
252
|
-
return f"{col_name} <
|
|
253
|
-
elif op == OP.
|
|
254
|
-
return f"{col_name} <
|
|
278
|
+
return f"{col_name} < NOW() - INTERVAL {self.rpt} MONTH", None
|
|
279
|
+
elif op == OP.BEFORE_DAYS:
|
|
280
|
+
return f"{col_name} < NOW() - INTERVAL {self.rpt} DAY", None
|
|
281
|
+
elif op == OP.BEFORE_HOURS:
|
|
282
|
+
return f"{col_name} < NOW() - INTERVAL {self.rpt} HOUR", None
|
|
283
|
+
elif op == OP.BEFORE_MINUTES:
|
|
284
|
+
return f"{col_name} < NOW() - INTERVAL {self.rpt} MINUTE", None
|
|
285
|
+
elif op == OP.BEFORE_SECONDS:
|
|
286
|
+
return f"{col_name} < NOW() - INTERVAL {self.rpt} SECOND", None
|
|
287
|
+
|
|
255
288
|
elif op == OP.JSON_CONTAINS_OBJECT:
|
|
256
289
|
_k, _v = self.rpt
|
|
257
290
|
key = _k.full_name if isinstance(_k, Field) else f"'{_k}'"
|
|
@@ -301,33 +334,28 @@ class Expression(FieldBase):
|
|
|
301
334
|
q.append(_q)
|
|
302
335
|
if _p:
|
|
303
336
|
p = p + _p
|
|
304
|
-
# return l.sql()
|
|
305
337
|
|
|
306
338
|
q.append(f" {self.op} ")
|
|
307
339
|
|
|
308
340
|
if isinstance(r, Field):
|
|
309
341
|
if r._value is not None:
|
|
310
|
-
# _value = r._value if r._value is not None else r.full_name
|
|
311
342
|
q.append("?")
|
|
312
343
|
p.append(r._value)
|
|
313
344
|
else:
|
|
314
345
|
q.append(r.full_name)
|
|
315
|
-
# q.append(str(r._value) if r._value is not None else r.full_name)
|
|
316
346
|
elif isinstance(r, FieldBase):
|
|
317
347
|
_q, _p = r.sql()
|
|
318
348
|
q.append(_q)
|
|
319
|
-
|
|
349
|
+
if _p:
|
|
350
|
+
p = p + _p
|
|
320
351
|
elif self.op == OP.AS:
|
|
321
|
-
# is_fun or (isinstance(r, str) and (self.op == OP.AND or self.op == OP.OR)):
|
|
322
352
|
q.append(r)
|
|
323
353
|
elif r is not None:
|
|
324
354
|
q.append("?")
|
|
325
355
|
p.append(r)
|
|
326
356
|
if not is_fun:
|
|
327
357
|
q.append(")")
|
|
328
|
-
# q.append(")")
|
|
329
358
|
q = [str(num) for num in q]
|
|
330
|
-
# print("".join(q))
|
|
331
359
|
return "".join(q), p
|
|
332
360
|
|
|
333
361
|
# def __str__(self):
|
|
@@ -338,7 +366,6 @@ class Expression(FieldBase):
|
|
|
338
366
|
# return str(eval(f"{cal}{self.rhs}"))
|
|
339
367
|
|
|
340
368
|
def __repr__(self):
|
|
341
|
-
# return self.__value
|
|
342
369
|
cal = f"{self.lhs.value}{self.op}"
|
|
343
370
|
if isinstance(self.rhs, FieldBase):
|
|
344
371
|
return str(eval(f"{cal}{self.rhs.value}"))
|
|
@@ -354,7 +381,7 @@ class Field(FieldBase):
|
|
|
354
381
|
primary_key=False, # 主键
|
|
355
382
|
charset=None, # 编码
|
|
356
383
|
max_length=None, # 长度
|
|
357
|
-
scale_length=None,
|
|
384
|
+
scale_length=None, # 精度
|
|
358
385
|
auto_increment=False, # 自增
|
|
359
386
|
NOT_NULL=False, # 非空
|
|
360
387
|
created_generated=False, # 创建时for datetime
|
|
@@ -7,6 +7,8 @@ import re
|
|
|
7
7
|
import datetime
|
|
8
8
|
from ..utils.common import Object
|
|
9
9
|
|
|
10
|
+
__ALL__ = ("models", "Model")
|
|
11
|
+
|
|
10
12
|
|
|
11
13
|
class ModelMetaclass(type):
|
|
12
14
|
# def __init__(self, **kw):
|
|
@@ -197,9 +199,7 @@ class Model(metaclass=ModelMetaclass):
|
|
|
197
199
|
if isinstance(f, str):
|
|
198
200
|
by.append(f)
|
|
199
201
|
else:
|
|
200
|
-
by.append(
|
|
201
|
-
f"{f.lhs.name if self.__ispg else f.lhs.full_name} {f.op}"
|
|
202
|
-
)
|
|
202
|
+
by.append(f"{f.lhs.name if self.__ispg else f.lhs.full_name} {f.op}")
|
|
203
203
|
if self.__order_by__ is not None:
|
|
204
204
|
by = self.__order_by__ + by
|
|
205
205
|
self.__order_by__ = by
|
|
@@ -214,9 +214,8 @@ class Model(metaclass=ModelMetaclass):
|
|
|
214
214
|
self.__group_by__ = by
|
|
215
215
|
return self
|
|
216
216
|
|
|
217
|
-
def
|
|
217
|
+
def _format_data(self, action, args):
|
|
218
218
|
data = dict()
|
|
219
|
-
md = None
|
|
220
219
|
if args is None or not args:
|
|
221
220
|
for k in self.__fields__:
|
|
222
221
|
f = getattr(self, k)
|
|
@@ -226,15 +225,19 @@ class Model(metaclass=ModelMetaclass):
|
|
|
226
225
|
data = args
|
|
227
226
|
else:
|
|
228
227
|
for item in args:
|
|
229
|
-
md = item
|
|
230
228
|
if isinstance(item, Model):
|
|
231
229
|
for k in item.__dict__:
|
|
232
|
-
if (action ==
|
|
230
|
+
if (action == "u" and item[k].value) or action == "i":
|
|
233
231
|
data[k] = item[k]
|
|
234
232
|
|
|
235
233
|
else: # for object
|
|
236
234
|
for k, v in item.items():
|
|
237
235
|
data[k] = v
|
|
236
|
+
|
|
237
|
+
return data
|
|
238
|
+
|
|
239
|
+
def _get_update_key_args(self, action, args):
|
|
240
|
+
data = self._format_data(action, args)
|
|
238
241
|
keys = []
|
|
239
242
|
params = []
|
|
240
243
|
for k, v in data.items():
|
|
@@ -251,7 +254,48 @@ class Model(metaclass=ModelMetaclass):
|
|
|
251
254
|
elif v is not None: # fix sql format %s
|
|
252
255
|
keys.append("`%s`=?" % k)
|
|
253
256
|
params.append(v)
|
|
254
|
-
return ",".join(keys), params
|
|
257
|
+
return ",".join(keys), params
|
|
258
|
+
|
|
259
|
+
def _get_insert_key_args(self, action, args):
|
|
260
|
+
data = self._format_data(action, args)
|
|
261
|
+
keys = []
|
|
262
|
+
params = []
|
|
263
|
+
for k, v in data.items():
|
|
264
|
+
if isinstance(v, Field):
|
|
265
|
+
value = v.value
|
|
266
|
+
if value is None:
|
|
267
|
+
if v.created_generated == True or v.update_generated == True:
|
|
268
|
+
value = datetime.datetime.now()
|
|
269
|
+
else:
|
|
270
|
+
value = v.default
|
|
271
|
+
if value is not None:
|
|
272
|
+
keys.append(k)
|
|
273
|
+
params.append(value)
|
|
274
|
+
elif v is not None: # fix sql format %s
|
|
275
|
+
keys.append(k)
|
|
276
|
+
params.append(v)
|
|
277
|
+
return keys, params
|
|
278
|
+
|
|
279
|
+
def _get_batch_keys_values(self, items: list):
|
|
280
|
+
keys = []
|
|
281
|
+
values = []
|
|
282
|
+
item = items[0]
|
|
283
|
+
if isinstance(item, Model):
|
|
284
|
+
keys = item.__dict__.keys()
|
|
285
|
+
elif isinstance(item, dict):
|
|
286
|
+
keys = item.keys()
|
|
287
|
+
|
|
288
|
+
for item in items:
|
|
289
|
+
value = []
|
|
290
|
+
if isinstance(item, Model):
|
|
291
|
+
for k in item.__dict__:
|
|
292
|
+
value.append(item[k].value)
|
|
293
|
+
elif isinstance(item, dict): # for object
|
|
294
|
+
for k, v in item.items():
|
|
295
|
+
value.append(v)
|
|
296
|
+
value = tuple(key for key in value)
|
|
297
|
+
values.append(value)
|
|
298
|
+
return keys, values
|
|
255
299
|
|
|
256
300
|
def _merge_params(self, p):
|
|
257
301
|
if p is not None:
|
|
@@ -343,12 +387,13 @@ class Model(metaclass=ModelMetaclass):
|
|
|
343
387
|
# where = self.__where__
|
|
344
388
|
where = self._literal("WHERE", self.__where__)
|
|
345
389
|
|
|
346
|
-
keys, params
|
|
390
|
+
keys, params = self._get_update_key_args("u", args or kw)
|
|
347
391
|
|
|
348
392
|
sql = f"update `{table}` set {keys} {where}"
|
|
349
393
|
|
|
350
394
|
if where is not None and where != "":
|
|
351
|
-
|
|
395
|
+
if self.__params__:
|
|
396
|
+
params += self.__params__
|
|
352
397
|
else:
|
|
353
398
|
pk, pkv = self._get_primary()
|
|
354
399
|
if pkv is None:
|
|
@@ -389,11 +434,23 @@ class Model(metaclass=ModelMetaclass):
|
|
|
389
434
|
|
|
390
435
|
async def insert(self, *args, **kw):
|
|
391
436
|
table = self.__table__
|
|
392
|
-
keys, params
|
|
393
|
-
|
|
437
|
+
keys, params = self._get_insert_key_args("i", args or kw)
|
|
438
|
+
escape_keys = [f'`{k}`' for k in keys]
|
|
439
|
+
sql = f"insert into `{table}` ({','.join(escape_keys)}) values ({','.join(['?' for k in keys])})"
|
|
394
440
|
self._reset()
|
|
441
|
+
params = tuple(key for key in params)
|
|
395
442
|
return await self.__pool__.create(sql, params)
|
|
396
443
|
|
|
444
|
+
async def insert_batch(self, items: list):
|
|
445
|
+
table = self.__table__
|
|
446
|
+
if len(items) == 0:
|
|
447
|
+
return 0
|
|
448
|
+
keys, params = self._get_batch_keys_values(items)
|
|
449
|
+
escape_keys = [f'`{k}`' for k in keys]
|
|
450
|
+
sql = f"insert into `{table}` ({','.join(escape_keys)}) values ({','.join(['?' for k in keys])})"
|
|
451
|
+
self._reset()
|
|
452
|
+
return await self.__pool__.create_batch(sql, params)
|
|
453
|
+
|
|
397
454
|
async def count(self):
|
|
398
455
|
__where__ = copy.copy(self.__where__)
|
|
399
456
|
__join__ = copy.copy(self.__join__)
|
|
@@ -5,7 +5,8 @@ __author__ = "chuchur/chuchur.com"
|
|
|
5
5
|
|
|
6
6
|
import hashlib, hmac, time, base64
|
|
7
7
|
from curl_cffi import requests as http
|
|
8
|
-
from ..logging import print_error,print_info
|
|
8
|
+
from ..logging import print_error, print_info
|
|
9
|
+
|
|
9
10
|
|
|
10
11
|
class Client(object):
|
|
11
12
|
def __init__(self, **kw):
|
|
@@ -253,7 +253,8 @@ def get_col_sql(field):
|
|
|
253
253
|
if field.primary_key:
|
|
254
254
|
sql += " PRIMARY KEY"
|
|
255
255
|
if field.auto_increment:
|
|
256
|
-
|
|
256
|
+
# todo: postgres SERIAL
|
|
257
|
+
sql += " AUTO_INCREMENT"
|
|
257
258
|
if field.NOT_NULL:
|
|
258
259
|
sql += " NOT NULL"
|
|
259
260
|
if field.default:
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
|
|
2
|
+
import time
|
|
3
|
+
import threading
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
雪花算法(全局唯一id)
|
|
8
|
+
|
|
9
|
+
# 最早是Twitter公司在其内部用于分布式环境下生成唯一ID
|
|
10
|
+
|
|
11
|
+
# Twitter's Snowflake algorithm implementation which is used to generate distributed IDs.
|
|
12
|
+
|
|
13
|
+
# https://github.com/twitter-archive/snowflake/blob/snowflake-2010/src/main/scala/com/twitter/service/snowflake/IdWorker.scala
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class IdWorker:
|
|
18
|
+
def __init__(self, worker_id, datacenter_id, sequence=0):
|
|
19
|
+
# 位移值
|
|
20
|
+
self.worker_id_bits = 5
|
|
21
|
+
self.datacenter_id_bits = 5
|
|
22
|
+
self.sequence_bits = 12
|
|
23
|
+
|
|
24
|
+
self.max_worker_id = -1 ^ (-1 << self.worker_id_bits)
|
|
25
|
+
self.max_datacenter_id = -1 ^ (-1 << self.datacenter_id_bits)
|
|
26
|
+
|
|
27
|
+
# 参数校验
|
|
28
|
+
if worker_id > self.max_worker_id or worker_id < 0:
|
|
29
|
+
raise ValueError(f'worker_id must be between 0 and {self.max_worker_id}')
|
|
30
|
+
if datacenter_id > self.max_datacenter_id or datacenter_id < 0:
|
|
31
|
+
raise ValueError(f'datacenter_id must be between 0 and {self.max_datacenter_id}')
|
|
32
|
+
|
|
33
|
+
self.worker_id = worker_id
|
|
34
|
+
self.datacenter_id = datacenter_id
|
|
35
|
+
self.sequence = sequence
|
|
36
|
+
self.lock = threading.Lock()
|
|
37
|
+
|
|
38
|
+
# Twitter Epoch (2010-11-04 01:42:54 UTC)
|
|
39
|
+
self.twepoch = 1288834974657
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
self.worker_id_shift = self.sequence_bits
|
|
45
|
+
self.datacenter_id_shift = self.sequence_bits + self.worker_id_bits
|
|
46
|
+
self.timestamp_left_shift = self.sequence_bits + self.worker_id_bits + self.datacenter_id_bits
|
|
47
|
+
self.sequence_mask = -1 ^ (-1 << self.sequence_bits)
|
|
48
|
+
|
|
49
|
+
self.last_timestamp = -1
|
|
50
|
+
|
|
51
|
+
def _time_gen(self):
|
|
52
|
+
return int(time.time() * 1000)
|
|
53
|
+
|
|
54
|
+
def _til_next_millis(self, last_timestamp):
|
|
55
|
+
timestamp = self._time_gen()
|
|
56
|
+
while timestamp <= last_timestamp:
|
|
57
|
+
timestamp = self._time_gen()
|
|
58
|
+
return timestamp
|
|
59
|
+
|
|
60
|
+
def next_id(self):
|
|
61
|
+
with self.lock:
|
|
62
|
+
timestamp = self._time_gen()
|
|
63
|
+
|
|
64
|
+
if timestamp < self.last_timestamp:
|
|
65
|
+
raise Exception(f'Clock moved backwards. Refusing to generate id for {self.last_timestamp - timestamp} milliseconds')
|
|
66
|
+
|
|
67
|
+
if self.last_timestamp == timestamp:
|
|
68
|
+
self.sequence = (self.sequence + 1) & self.sequence_mask
|
|
69
|
+
if self.sequence == 0:
|
|
70
|
+
timestamp = self._til_next_millis(self.last_timestamp)
|
|
71
|
+
else:
|
|
72
|
+
self.sequence = 0
|
|
73
|
+
|
|
74
|
+
self.last_timestamp = timestamp
|
|
75
|
+
|
|
76
|
+
id = ((timestamp - self.twepoch) << self.timestamp_left_shift) | \
|
|
77
|
+
(self.datacenter_id << self.datacenter_id_shift) | \
|
|
78
|
+
(self.worker_id << self.worker_id_shift) | \
|
|
79
|
+
self.sequence
|
|
80
|
+
|
|
81
|
+
return id
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# 示例使用
|
|
85
|
+
worker = IdWorker(worker_id=1, datacenter_id=1)
|
|
86
|
+
print(worker.next_id())
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cloudoll
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.26
|
|
4
4
|
Summary: 辅助快速创建可分布的微服务。
|
|
5
5
|
Home-page: https://gitee.com/chuchur/cloudoll-py
|
|
6
6
|
Author: chuchur
|
|
@@ -15,6 +15,19 @@ Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
|
15
15
|
Requires-Python: >=3.6.0
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
|
+
Requires-Dist: watchfiles
|
|
19
|
+
Requires-Dist: curl_cffi
|
|
20
|
+
Requires-Dist: colorlog
|
|
21
|
+
Requires-Dist: aiomysql
|
|
22
|
+
Requires-Dist: aiopg
|
|
23
|
+
Requires-Dist: aiohttp
|
|
24
|
+
Requires-Dist: redis[hiredis]
|
|
25
|
+
Requires-Dist: aiomcache
|
|
26
|
+
Requires-Dist: jinja2
|
|
27
|
+
Requires-Dist: pyjwt
|
|
28
|
+
Requires-Dist: aiohttp_session[secure]
|
|
29
|
+
Requires-Dist: PyYAML
|
|
30
|
+
Requires-Dist: click
|
|
18
31
|
|
|
19
32
|
|
|
20
33
|
# cloudoll 云端玩具
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.2.23"
|
|
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
|