ezKit 1.10.3__py3-none-any.whl → 1.10.5__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- ezKit/database.py +57 -0
- ezKit/utils.py +35 -36
- {ezKit-1.10.3.dist-info → ezKit-1.10.5.dist-info}/METADATA +1 -1
- {ezKit-1.10.3.dist-info → ezKit-1.10.5.dist-info}/RECORD +7 -7
- {ezKit-1.10.3.dist-info → ezKit-1.10.5.dist-info}/LICENSE +0 -0
- {ezKit-1.10.3.dist-info → ezKit-1.10.5.dist-info}/WHEEL +0 -0
- {ezKit-1.10.3.dist-info → ezKit-1.10.5.dist-info}/top_level.txt +0 -0
ezKit/database.py
CHANGED
@@ -6,8 +6,10 @@
|
|
6
6
|
# PostgreSQL 14 Data Types
|
7
7
|
# https://www.postgresql.org/docs/14/datatype.html
|
8
8
|
import csv
|
9
|
+
import json
|
9
10
|
from typing import Any
|
10
11
|
|
12
|
+
import pandas as pd
|
11
13
|
from loguru import logger
|
12
14
|
from sqlalchemy import CursorResult, Index, create_engine, text
|
13
15
|
from sqlalchemy.orm import DeclarativeBase
|
@@ -30,10 +32,14 @@ class Database():
|
|
30
32
|
else:
|
31
33
|
pass
|
32
34
|
|
35
|
+
# ----------------------------------------------------------------------------------------------
|
36
|
+
|
33
37
|
def initializer(self):
|
34
38
|
"""ensure the parent proc's database connections are not touched in the new connection pool"""
|
35
39
|
self.engine.dispose(close=False)
|
36
40
|
|
41
|
+
# ----------------------------------------------------------------------------------------------
|
42
|
+
|
37
43
|
def connect_test(self) -> bool:
|
38
44
|
info = "Database connect test"
|
39
45
|
try:
|
@@ -46,6 +52,8 @@ class Database():
|
|
46
52
|
logger.exception(e)
|
47
53
|
return False
|
48
54
|
|
55
|
+
# ----------------------------------------------------------------------------------------------
|
56
|
+
|
49
57
|
def metadata_init(self, base: DeclarativeBase, **kwargs) -> bool:
|
50
58
|
# https://stackoverflow.com/questions/19175311/how-to-create-only-one-table-with-sqlalchemy
|
51
59
|
info = "Database init table"
|
@@ -60,6 +68,8 @@ class Database():
|
|
60
68
|
logger.exception(e)
|
61
69
|
return False
|
62
70
|
|
71
|
+
# ----------------------------------------------------------------------------------------------
|
72
|
+
|
63
73
|
def create_index(self, index_name, table_field) -> bool:
|
64
74
|
# 创建索引
|
65
75
|
# https://stackoverflow.com/a/41254430
|
@@ -82,6 +92,8 @@ class Database():
|
|
82
92
|
logger.error(e)
|
83
93
|
return False
|
84
94
|
|
95
|
+
# ----------------------------------------------------------------------------------------------
|
96
|
+
|
85
97
|
# 私有函数, 保存 execute 的结果到 CSV 文件
|
86
98
|
def _result_save(self, file, data) -> bool:
|
87
99
|
try:
|
@@ -93,6 +105,8 @@ class Database():
|
|
93
105
|
logger.exception(e)
|
94
106
|
return False
|
95
107
|
|
108
|
+
# ----------------------------------------------------------------------------------------------
|
109
|
+
|
96
110
|
def execute(
|
97
111
|
self,
|
98
112
|
sql: str | None = None,
|
@@ -204,3 +218,46 @@ class Database():
|
|
204
218
|
logger.error(f'{info} [failure]')
|
205
219
|
logger.exception(e)
|
206
220
|
return False
|
221
|
+
|
222
|
+
# ----------------------------------------------------------------------------------------------
|
223
|
+
|
224
|
+
def read_data_with_pandas(self, result_type: str = "df", **kwargs) -> pd.DataFrame | dict | list | None:
|
225
|
+
"""读取表中所有数据"""
|
226
|
+
|
227
|
+
# 使用 pd.read_sql_table 的参数
|
228
|
+
# read_data_with_pandas(result_type="df", table_name="ashare")
|
229
|
+
|
230
|
+
info = f"读取 {kwargs.get('table_name', None)} 表中所有数据"
|
231
|
+
|
232
|
+
try:
|
233
|
+
|
234
|
+
logger.info(f"{info} ......")
|
235
|
+
|
236
|
+
# 从 kwargs 中删除 con 键
|
237
|
+
kwargs.pop('con', None)
|
238
|
+
|
239
|
+
# 读取数据
|
240
|
+
data: pd.DataFrame = pd.read_sql_table(con=self.engine, **kwargs)
|
241
|
+
|
242
|
+
if data.empty:
|
243
|
+
logger.error(f"{info} [失败]")
|
244
|
+
return None
|
245
|
+
|
246
|
+
logger.success(f"{info} [成功]")
|
247
|
+
|
248
|
+
if utils.isTrue(result_type, str) and result_type == "json":
|
249
|
+
return json.loads(data.to_json(orient='records'))
|
250
|
+
|
251
|
+
if utils.isTrue(result_type, str) and result_type == "dict":
|
252
|
+
return data.to_dict()
|
253
|
+
|
254
|
+
if utils.isTrue(result_type, str) and result_type == "list":
|
255
|
+
# https://stackoverflow.com/a/26716774
|
256
|
+
return data.to_dict('list')
|
257
|
+
|
258
|
+
return data
|
259
|
+
|
260
|
+
except Exception as e:
|
261
|
+
logger.error(f"{info} [失败]")
|
262
|
+
logger.exception(e)
|
263
|
+
return None
|
ezKit/utils.py
CHANGED
@@ -13,7 +13,7 @@ from multiprocessing import Pool
|
|
13
13
|
from multiprocessing.pool import ThreadPool
|
14
14
|
from pathlib import Path
|
15
15
|
from shutil import rmtree
|
16
|
-
from typing import Any, Callable, List, Union
|
16
|
+
from typing import Any, Callable, List, Optional, Union
|
17
17
|
from urllib.parse import ParseResult, urlparse
|
18
18
|
from uuid import uuid4
|
19
19
|
|
@@ -1001,46 +1001,45 @@ def datetime_object(
|
|
1001
1001
|
# --------------------------------------------------------------------------------------------------
|
1002
1002
|
|
1003
1003
|
|
1004
|
-
# run_cmd = bash('echo ok', universal_newlines=True, stdout=PIPE)
|
1005
|
-
#
|
1006
|
-
# if run_cmd != None:
|
1007
|
-
# returncode = run_cmd.returncode
|
1008
|
-
# outputs = run_cmd.stdout.splitlines()
|
1009
|
-
# print(returncode, type(returncode))
|
1010
|
-
# print(outputs, type(outputs))
|
1011
|
-
#
|
1012
|
-
# # echo 'echo ok' > /tmp/ok.sh
|
1013
|
-
# run_script = bash('/tmp/ok.sh', file=True, universal_newlines=True, stdout=PIPE)
|
1014
|
-
#
|
1015
|
-
# if run_script != None:
|
1016
|
-
# returncode = run_script.returncode
|
1017
|
-
# outputs = run_script.stdout.splitlines()
|
1018
|
-
# print(returncode, type(returncode))
|
1019
|
-
# print(outputs, type(outputs))
|
1020
|
-
|
1021
|
-
|
1022
1004
|
def shell(
|
1023
1005
|
command: str,
|
1024
1006
|
isfile: bool = False,
|
1025
|
-
sh_shell: str =
|
1026
|
-
sh_option: str
|
1007
|
+
sh_shell: str = "/bin/bash",
|
1008
|
+
sh_option: Optional[str] = None,
|
1027
1009
|
**kwargs
|
1028
|
-
) -> subprocess.CompletedProcess
|
1029
|
-
"""
|
1010
|
+
) -> Optional[subprocess.CompletedProcess]:
|
1011
|
+
"""执行 Shell 命令 或 脚本"""
|
1012
|
+
|
1013
|
+
# :param command: 需要执行的命令
|
1014
|
+
# :param isfile: 是否将命令视为文件路径
|
1015
|
+
# :param sh_shell: 使用的 Shell 程序路径
|
1016
|
+
# :param sh_option: Shell 执行选项,例如 '-c'
|
1017
|
+
# :param kwargs: 其他传递给 subprocess.run 的参数
|
1018
|
+
# :return: 返回 subprocess.CompletedProcess 对象,失败时返回 None
|
1019
|
+
|
1030
1020
|
try:
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1021
|
+
|
1022
|
+
# 校验 Shell 程序路径
|
1023
|
+
if not check_file_type(sh_shell, "file"):
|
1024
|
+
logger.error(f"Invalid shell path: {sh_shell}")
|
1025
|
+
return None
|
1026
|
+
|
1027
|
+
# 校验 command 和 sh_shell 的类型
|
1028
|
+
if not (isTrue(sh_shell, str) and isTrue(command, str)):
|
1029
|
+
logger.error("Invalid shell or command input.")
|
1030
|
+
return None
|
1031
|
+
|
1032
|
+
# 构造命令
|
1033
|
+
if isfile:
|
1034
|
+
args = [sh_shell, command] if sh_option is None else [sh_shell, sh_option, command]
|
1035
|
+
else:
|
1036
|
+
sh_option = sh_option or "-c"
|
1037
|
+
args = [sh_shell, sh_option, command]
|
1038
|
+
|
1039
|
+
logger.info(f"Executing command: {args}")
|
1040
|
+
|
1041
|
+
return subprocess.run(args, **kwargs, check=False)
|
1042
|
+
|
1044
1043
|
except Exception as e:
|
1045
1044
|
logger.exception(e)
|
1046
1045
|
return None
|
@@ -2,17 +2,17 @@ ezKit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
ezKit/bottle.py,sha256=usKK1wVaZw4_D-4VwMYmOIc8jtz4TrpM30nck59HMFw,180178
|
3
3
|
ezKit/bottle_extensions.py,sha256=3reEQVZuHklXTl6r7F8kiBFFPb0RaAGc3mYJJnrMDjQ,1129
|
4
4
|
ezKit/cipher.py,sha256=0T_StbjiNI4zgrjVgcfU-ffKgu1waBA9UDudAnqFcNM,2896
|
5
|
-
ezKit/database.py,sha256=
|
5
|
+
ezKit/database.py,sha256=b_4gR_bvyC3IhP6TbmeKdVrif1DNG_NqO3YIyy-XqQE,8891
|
6
6
|
ezKit/http.py,sha256=i3Kn5AMAMicDMcDjxKKZU7zqEKTU88Ec9_LwCuBJy-0,1801
|
7
7
|
ezKit/mongo.py,sha256=dOm_1wXEPp_e8Ml5Qq78M7FDNrQUAZaThzVIiiLJJwk,2393
|
8
8
|
ezKit/qywx.py,sha256=X_H4fzP-iEqeDEbumr7D1bXi6dxczaxfO8iyutzy02s,7171
|
9
9
|
ezKit/redis.py,sha256=g2_V4jvq0djRc20jLZkgeAeF_bYrq-Rbl_kHcCUPZcA,1965
|
10
10
|
ezKit/sendemail.py,sha256=tRXCsJm_RfTJ9xEWe_lTQ5kOs2JxHGPXvq0oWA7prq0,7263
|
11
11
|
ezKit/token.py,sha256=HKREyZj_T2S8-aFoFIrBXTaCKExQq4zE66OHXhGHqQg,1750
|
12
|
-
ezKit/utils.py,sha256=
|
12
|
+
ezKit/utils.py,sha256=ILLaptYUSalERbCOhmoq4w0ZNZgj9yurpHEZwjRMZ8w,42387
|
13
13
|
ezKit/xftp.py,sha256=XyIdr_2rxRVLqPofG6fIYWhAMVsFwTyp46dg5P9FLW4,7774
|
14
|
-
ezKit-1.10.
|
15
|
-
ezKit-1.10.
|
16
|
-
ezKit-1.10.
|
17
|
-
ezKit-1.10.
|
18
|
-
ezKit-1.10.
|
14
|
+
ezKit-1.10.5.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
15
|
+
ezKit-1.10.5.dist-info/METADATA,sha256=onqUqJ4_5WVpBoLpSOis-Z6uYbmM3GCMPPrAfTvi_yw,191
|
16
|
+
ezKit-1.10.5.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
17
|
+
ezKit-1.10.5.dist-info/top_level.txt,sha256=aYLB_1WODsqNTsTFWcKP-BN0KCTKcV-HZJ4zlHkCFw8,6
|
18
|
+
ezKit-1.10.5.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|