pixelarraylib 1.1.6__tar.gz → 1.1.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.
- {pixelarraylib-1.1.6/pixelarraylib.egg-info → pixelarraylib-1.1.7}/PKG-INFO +6 -4
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/__init__.py +1 -1
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/scripts/__init__.py +1 -1
- pixelarraylib-1.1.7/pixelarraylib/system/cron_manager.py +535 -0
- pixelarraylib-1.1.7/pixelarraylib/utils/name_generator.py +144 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7/pixelarraylib.egg-info}/PKG-INFO +6 -4
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib.egg-info/SOURCES.txt +3 -2
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib.egg-info/requires.txt +6 -4
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pyproject.toml +7 -5
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/requirements.txt +3 -1
- pixelarraylib-1.1.6/pixelarraylib/net/request.py +0 -175
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/LICENSE +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/MANIFEST.in +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/README.md +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/__main__.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/aliyun/__init__.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/aliyun/acr.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/aliyun/aliyun_email.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/aliyun/billing.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/aliyun/content_scanner.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/aliyun/domain.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/aliyun/eci.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/aliyun/ecs.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/aliyun/eip.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/aliyun/fc.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/aliyun/oss.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/aliyun/sms.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/aliyun/sts.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/db_utils/mysql.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/db_utils/redis.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/decorators/__init__.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/decorators/decorators.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/gitlab/__init__.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/gitlab/code_analyzer.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/gitlab/pypi_package_manager.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/monitor/__init__.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/monitor/feishu.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/scripts/build_website.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/scripts/collect_code_to_txt.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/scripts/create_test_case_files.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/scripts/nginx_proxy_to_ecs.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/scripts/remove_empty_lines.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/scripts/tson_convert.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/system/__init__.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/system/common.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib/system/tson.py +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib.egg-info/dependency_links.txt +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib.egg-info/entry_points.txt +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/pixelarraylib.egg-info/top_level.txt +0 -0
- {pixelarraylib-1.1.6 → pixelarraylib-1.1.7}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pixelarraylib
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.7
|
|
4
4
|
Summary: PixelArray Python开发工具库 - 包含阿里云服务、数据库工具、装饰器、监控等功能
|
|
5
5
|
Author-email: Lu qi <qi.lu@pixelarrayai.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -29,6 +29,9 @@ Provides-Extra: system
|
|
|
29
29
|
Requires-Dist: cryptography; extra == "system"
|
|
30
30
|
Requires-Dist: paramiko; extra == "system"
|
|
31
31
|
Requires-Dist: requests; extra == "system"
|
|
32
|
+
Provides-Extra: utils
|
|
33
|
+
Requires-Dist: names; extra == "utils"
|
|
34
|
+
Requires-Dist: Faker; extra == "utils"
|
|
32
35
|
Provides-Extra: mysql
|
|
33
36
|
Requires-Dist: pymysql; extra == "mysql"
|
|
34
37
|
Requires-Dist: aiomysql; extra == "mysql"
|
|
@@ -36,9 +39,6 @@ Requires-Dist: requests; extra == "mysql"
|
|
|
36
39
|
Provides-Extra: redis
|
|
37
40
|
Requires-Dist: redis; extra == "redis"
|
|
38
41
|
Requires-Dist: requests; extra == "redis"
|
|
39
|
-
Provides-Extra: net
|
|
40
|
-
Requires-Dist: requests; extra == "net"
|
|
41
|
-
Requires-Dist: aiohttp; extra == "net"
|
|
42
42
|
Provides-Extra: gitlab
|
|
43
43
|
Requires-Dist: requests; extra == "gitlab"
|
|
44
44
|
Requires-Dist: aiohttp; extra == "gitlab"
|
|
@@ -161,6 +161,8 @@ Requires-Dist: requests; extra == "all"
|
|
|
161
161
|
Requires-Dist: aiohttp; extra == "all"
|
|
162
162
|
Requires-Dist: aiofiles; extra == "all"
|
|
163
163
|
Requires-Dist: pandas; extra == "all"
|
|
164
|
+
Requires-Dist: names; extra == "all"
|
|
165
|
+
Requires-Dist: Faker; extra == "all"
|
|
164
166
|
Dynamic: license-file
|
|
165
167
|
|
|
166
168
|
# PixelArrayLib - PixelArray Python开发工具库
|
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import tempfile
|
|
3
|
+
import os
|
|
4
|
+
import re
|
|
5
|
+
import traceback
|
|
6
|
+
from typing import List, Dict, Optional
|
|
7
|
+
from pixelarraylib.system.common import execute_command
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CronManager:
|
|
11
|
+
def __init__(self):
|
|
12
|
+
self.cron_file = self._load_crontab()
|
|
13
|
+
|
|
14
|
+
def _load_crontab(self) -> str:
|
|
15
|
+
"""
|
|
16
|
+
description:
|
|
17
|
+
加载crontab文件
|
|
18
|
+
return:
|
|
19
|
+
crontab_file(str): crontab文件内容
|
|
20
|
+
"""
|
|
21
|
+
output, success = execute_command(["crontab", "-l"])
|
|
22
|
+
if success:
|
|
23
|
+
return output
|
|
24
|
+
else:
|
|
25
|
+
return ""
|
|
26
|
+
|
|
27
|
+
def _save_crontab(self) -> bool:
|
|
28
|
+
"""
|
|
29
|
+
description:
|
|
30
|
+
保存crontab文件
|
|
31
|
+
return:
|
|
32
|
+
success(bool): 是否成功
|
|
33
|
+
"""
|
|
34
|
+
with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
|
|
35
|
+
content = self.cron_file
|
|
36
|
+
if content and not content.endswith("\n"):
|
|
37
|
+
content += "\n"
|
|
38
|
+
f.write(content)
|
|
39
|
+
temp_file = f.name
|
|
40
|
+
|
|
41
|
+
_, success = execute_command(["crontab", temp_file])
|
|
42
|
+
os.remove(temp_file)
|
|
43
|
+
return success
|
|
44
|
+
|
|
45
|
+
def _extract_job_name_from_comment(self, line: str) -> str:
|
|
46
|
+
"""
|
|
47
|
+
description:
|
|
48
|
+
从cron行中提取任务名称(如果有注释)
|
|
49
|
+
parameters:
|
|
50
|
+
line(str): cron行
|
|
51
|
+
return:
|
|
52
|
+
job_name(str): 任务名称
|
|
53
|
+
"""
|
|
54
|
+
match = re.search(r"# JOB_NAME:\s*([^\s]+)", line)
|
|
55
|
+
if match:
|
|
56
|
+
return match.group(1)
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
def _extract_status_from_comment(self, line: str) -> str:
|
|
60
|
+
"""
|
|
61
|
+
description:
|
|
62
|
+
从cron行中提取状态(STATUS:OPEN 或 STATUS:CLOSE)
|
|
63
|
+
parameters:
|
|
64
|
+
line(str): cron行
|
|
65
|
+
return:
|
|
66
|
+
status(str): 状态,OPEN 或 CLOSE,默认为 OPEN
|
|
67
|
+
"""
|
|
68
|
+
match = re.search(r"STATUS:\s*(\w+)", line)
|
|
69
|
+
if match:
|
|
70
|
+
return match.group(1).upper()
|
|
71
|
+
return "OPEN"
|
|
72
|
+
|
|
73
|
+
def _generate_job_name_from_command(self, command: str) -> str:
|
|
74
|
+
"""
|
|
75
|
+
description:
|
|
76
|
+
从命令中生成任务名称
|
|
77
|
+
parameters:
|
|
78
|
+
command(str): 任务命令
|
|
79
|
+
return:
|
|
80
|
+
job_name(str): 生成的任务名称
|
|
81
|
+
"""
|
|
82
|
+
# 尝试从命令中提取模块名
|
|
83
|
+
if "python -m" in command:
|
|
84
|
+
# 提取模块名
|
|
85
|
+
match = re.search(r"python -m\s+([^\s]+)", command)
|
|
86
|
+
if match:
|
|
87
|
+
module_name = match.group(1)
|
|
88
|
+
# 将模块名转换为任务名
|
|
89
|
+
return module_name.replace(".", "_").replace("/", "_")
|
|
90
|
+
|
|
91
|
+
# 如果无法提取模块名,使用命令的hash值
|
|
92
|
+
import hashlib
|
|
93
|
+
|
|
94
|
+
return f"task_{hashlib.md5(command.encode()).hexdigest()[:8]}"
|
|
95
|
+
|
|
96
|
+
def _validate_schedule(self, schedule: str) -> bool:
|
|
97
|
+
"""
|
|
98
|
+
description:
|
|
99
|
+
验证cron时间格式
|
|
100
|
+
parameters:
|
|
101
|
+
schedule(str): 任务时间安排
|
|
102
|
+
return:
|
|
103
|
+
valid(bool): 是否有效
|
|
104
|
+
"""
|
|
105
|
+
parts = schedule.strip().split()
|
|
106
|
+
if len(parts) != 5:
|
|
107
|
+
return False
|
|
108
|
+
|
|
109
|
+
ranges = [
|
|
110
|
+
(0, 59), # 分钟
|
|
111
|
+
(0, 23), # 小时
|
|
112
|
+
(1, 31), # 日期
|
|
113
|
+
(1, 12), # 月份
|
|
114
|
+
(0, 6), # 星期 (0=周日)
|
|
115
|
+
]
|
|
116
|
+
|
|
117
|
+
try:
|
|
118
|
+
for i, part in enumerate(parts):
|
|
119
|
+
if part == "*":
|
|
120
|
+
continue
|
|
121
|
+
|
|
122
|
+
min_val, max_val = ranges[i]
|
|
123
|
+
|
|
124
|
+
if "," in part:
|
|
125
|
+
for val in part.split(","):
|
|
126
|
+
if "-" in val:
|
|
127
|
+
range_parts = val.split("-")
|
|
128
|
+
if len(range_parts) == 2:
|
|
129
|
+
start = int(range_parts[0].strip())
|
|
130
|
+
end = int(range_parts[1].strip())
|
|
131
|
+
if not (
|
|
132
|
+
min_val <= start <= max_val
|
|
133
|
+
and min_val <= end <= max_val
|
|
134
|
+
and start <= end
|
|
135
|
+
):
|
|
136
|
+
return False
|
|
137
|
+
else:
|
|
138
|
+
return False
|
|
139
|
+
else:
|
|
140
|
+
val_int = int(val.strip())
|
|
141
|
+
if not (min_val <= val_int <= max_val):
|
|
142
|
+
return False
|
|
143
|
+
elif "-" in part:
|
|
144
|
+
if "/" in part:
|
|
145
|
+
range_part, step_part = part.split("/")
|
|
146
|
+
step = int(step_part.strip())
|
|
147
|
+
if step <= 0:
|
|
148
|
+
return False
|
|
149
|
+
|
|
150
|
+
if "-" in range_part:
|
|
151
|
+
start, end = range_part.split("-")
|
|
152
|
+
start = int(start.strip())
|
|
153
|
+
end = int(end.strip())
|
|
154
|
+
if not (
|
|
155
|
+
min_val <= start <= max_val
|
|
156
|
+
and min_val <= end <= max_val
|
|
157
|
+
and start <= end
|
|
158
|
+
):
|
|
159
|
+
return False
|
|
160
|
+
else:
|
|
161
|
+
base = int(range_part.strip())
|
|
162
|
+
if not (min_val <= base <= max_val):
|
|
163
|
+
return False
|
|
164
|
+
else:
|
|
165
|
+
start, end = part.split("-")
|
|
166
|
+
start = int(start.strip())
|
|
167
|
+
end = int(end.strip())
|
|
168
|
+
if not (
|
|
169
|
+
min_val <= start <= max_val
|
|
170
|
+
and min_val <= end <= max_val
|
|
171
|
+
and start <= end
|
|
172
|
+
):
|
|
173
|
+
return False
|
|
174
|
+
elif "/" in part:
|
|
175
|
+
base, step = part.split("/")
|
|
176
|
+
step = int(step.strip())
|
|
177
|
+
if step <= 0:
|
|
178
|
+
return False
|
|
179
|
+
|
|
180
|
+
if base == "*":
|
|
181
|
+
continue
|
|
182
|
+
base_int = int(base.strip())
|
|
183
|
+
if not (min_val <= base_int <= max_val):
|
|
184
|
+
return False
|
|
185
|
+
else:
|
|
186
|
+
val = int(part)
|
|
187
|
+
if not (min_val <= val <= max_val):
|
|
188
|
+
return False
|
|
189
|
+
except (ValueError, IndexError):
|
|
190
|
+
return False
|
|
191
|
+
|
|
192
|
+
return True
|
|
193
|
+
|
|
194
|
+
def list_cron_jobs(self) -> List[Dict[str, str]]:
|
|
195
|
+
"""
|
|
196
|
+
description:
|
|
197
|
+
获取所有cron任务
|
|
198
|
+
return:
|
|
199
|
+
jobs(list[dict]): 所有cron任务
|
|
200
|
+
"""
|
|
201
|
+
jobs = []
|
|
202
|
+
if not self.cron_file:
|
|
203
|
+
return jobs
|
|
204
|
+
|
|
205
|
+
lines = self.cron_file.strip().split("\n")
|
|
206
|
+
for line in lines:
|
|
207
|
+
original_line = line
|
|
208
|
+
line = line.strip()
|
|
209
|
+
|
|
210
|
+
# 检查是否是被注释的cron任务行(以#开头但包含JOB_NAME)
|
|
211
|
+
is_commented = line.startswith("#")
|
|
212
|
+
if is_commented:
|
|
213
|
+
# 移除开头的#,继续处理
|
|
214
|
+
line = line[1:].strip()
|
|
215
|
+
|
|
216
|
+
# 检查是否包含JOB_NAME注释(无论是启用还是禁用状态)
|
|
217
|
+
job_name = self._extract_job_name_from_comment(original_line)
|
|
218
|
+
if not job_name:
|
|
219
|
+
# 如果不是我们的任务行(没有JOB_NAME),跳过
|
|
220
|
+
if is_commented or not line or line.startswith("#"):
|
|
221
|
+
continue
|
|
222
|
+
# 对于没有JOB_NAME的普通cron行,尝试从命令生成任务名
|
|
223
|
+
parts = line.split(None, 5)
|
|
224
|
+
if len(parts) >= 6:
|
|
225
|
+
schedule = " ".join(parts[:5])
|
|
226
|
+
command = parts[5]
|
|
227
|
+
job_name = self._generate_job_name_from_command(command)
|
|
228
|
+
status = "OPEN" if not is_commented else "CLOSE"
|
|
229
|
+
jobs.append(
|
|
230
|
+
{
|
|
231
|
+
"job_name": job_name,
|
|
232
|
+
"schedule": schedule,
|
|
233
|
+
"command": command,
|
|
234
|
+
"raw_line": original_line,
|
|
235
|
+
"enabled": not is_commented,
|
|
236
|
+
}
|
|
237
|
+
)
|
|
238
|
+
continue
|
|
239
|
+
|
|
240
|
+
# 提取状态
|
|
241
|
+
status = self._extract_status_from_comment(original_line)
|
|
242
|
+
enabled = not is_commented and status == "OPEN"
|
|
243
|
+
|
|
244
|
+
# 解析cron行
|
|
245
|
+
parts = line.split(None, 5)
|
|
246
|
+
if len(parts) >= 6:
|
|
247
|
+
schedule = " ".join(parts[:5])
|
|
248
|
+
command_with_comment = parts[5]
|
|
249
|
+
command = command_with_comment
|
|
250
|
+
|
|
251
|
+
# 从命令中移除注释部分
|
|
252
|
+
# 移除 JOB_NAME 注释
|
|
253
|
+
if f" # JOB_NAME: {job_name}" in command:
|
|
254
|
+
command = command.replace(f" # JOB_NAME: {job_name}", "")
|
|
255
|
+
# 移除 STATUS 注释
|
|
256
|
+
status_pattern = r"\s*STATUS:\s*\w+"
|
|
257
|
+
command = re.sub(status_pattern, "", command).strip()
|
|
258
|
+
|
|
259
|
+
jobs.append(
|
|
260
|
+
{
|
|
261
|
+
"job_name": job_name,
|
|
262
|
+
"schedule": schedule,
|
|
263
|
+
"command": command,
|
|
264
|
+
"raw_line": original_line,
|
|
265
|
+
"enabled": enabled,
|
|
266
|
+
}
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
return jobs
|
|
270
|
+
|
|
271
|
+
def get_job_by_name(self, job_name: str) -> Optional[Dict[str, str]]:
|
|
272
|
+
"""
|
|
273
|
+
description:
|
|
274
|
+
根据名称获取任务
|
|
275
|
+
parameters:
|
|
276
|
+
job_name(str): 任务名称
|
|
277
|
+
return:
|
|
278
|
+
job(dict): 任务
|
|
279
|
+
"""
|
|
280
|
+
jobs = self.list_cron_jobs()
|
|
281
|
+
for job in jobs:
|
|
282
|
+
if job["job_name"] == job_name:
|
|
283
|
+
return job
|
|
284
|
+
return None
|
|
285
|
+
|
|
286
|
+
def add_cron_job(
|
|
287
|
+
self, job_name: str, command: str, schedule: str, enabled: bool = True
|
|
288
|
+
) -> bool:
|
|
289
|
+
"""
|
|
290
|
+
description:
|
|
291
|
+
添加cron任务
|
|
292
|
+
parameters:
|
|
293
|
+
job_name(str): 任务名称
|
|
294
|
+
command(str): 任务命令
|
|
295
|
+
schedule(str): 任务时间安排
|
|
296
|
+
enabled(bool): 是否启用,默认为True
|
|
297
|
+
return:
|
|
298
|
+
success(bool): 是否成功
|
|
299
|
+
"""
|
|
300
|
+
if self.job_exists(job_name):
|
|
301
|
+
raise ValueError(f"任务 '{job_name}' 已存在")
|
|
302
|
+
if not self._validate_schedule(schedule):
|
|
303
|
+
raise ValueError(f"无效的cron时间格式: {schedule}")
|
|
304
|
+
|
|
305
|
+
status = "OPEN" if enabled else "CLOSE"
|
|
306
|
+
new_line = f"{schedule} {command} # JOB_NAME: {job_name} STATUS:{status}"
|
|
307
|
+
|
|
308
|
+
# 如果禁用,在行首添加#
|
|
309
|
+
if not enabled:
|
|
310
|
+
new_line = f"# {new_line}"
|
|
311
|
+
|
|
312
|
+
if self.cron_file:
|
|
313
|
+
self.cron_file += f"\n{new_line}"
|
|
314
|
+
else:
|
|
315
|
+
self.cron_file = new_line
|
|
316
|
+
|
|
317
|
+
success = self._save_crontab()
|
|
318
|
+
return success
|
|
319
|
+
|
|
320
|
+
def remove_cron_job(self, job_name: str):
|
|
321
|
+
"""
|
|
322
|
+
description:
|
|
323
|
+
删除cron任务
|
|
324
|
+
parameters:
|
|
325
|
+
job_name(str): 任务名称
|
|
326
|
+
return:
|
|
327
|
+
success(bool): 是否成功
|
|
328
|
+
"""
|
|
329
|
+
if not self.job_exists(job_name):
|
|
330
|
+
raise ValueError(f"任务 '{job_name}' 不存在")
|
|
331
|
+
|
|
332
|
+
lines = self.cron_file.strip().split("\n")
|
|
333
|
+
self.cron_file = "\n".join(
|
|
334
|
+
[
|
|
335
|
+
line
|
|
336
|
+
for line in lines
|
|
337
|
+
if not (line.strip() and f"# JOB_NAME: {job_name}" in line)
|
|
338
|
+
]
|
|
339
|
+
)
|
|
340
|
+
success = self._save_crontab()
|
|
341
|
+
return success
|
|
342
|
+
|
|
343
|
+
def enable_cron_job(self, job_name: str) -> bool:
|
|
344
|
+
"""
|
|
345
|
+
description:
|
|
346
|
+
启用cron任务(移除行首的#注释)
|
|
347
|
+
parameters:
|
|
348
|
+
job_name(str): 任务名称
|
|
349
|
+
return:
|
|
350
|
+
success(bool): 是否成功
|
|
351
|
+
"""
|
|
352
|
+
if not self.job_exists(job_name):
|
|
353
|
+
raise ValueError(f"任务 '{job_name}' 不存在")
|
|
354
|
+
|
|
355
|
+
lines = self.cron_file.strip().split("\n")
|
|
356
|
+
updated_lines = []
|
|
357
|
+
|
|
358
|
+
for line in lines:
|
|
359
|
+
if f"# JOB_NAME: {job_name}" in line:
|
|
360
|
+
# 移除行首的#(如果存在)
|
|
361
|
+
if line.strip().startswith("#"):
|
|
362
|
+
line = line.strip()[1:].strip()
|
|
363
|
+
|
|
364
|
+
# 更新STATUS为OPEN
|
|
365
|
+
line = re.sub(r"STATUS:\s*\w+", "STATUS:OPEN", line)
|
|
366
|
+
updated_lines.append(line)
|
|
367
|
+
else:
|
|
368
|
+
updated_lines.append(line)
|
|
369
|
+
|
|
370
|
+
self.cron_file = "\n".join(updated_lines)
|
|
371
|
+
success = self._save_crontab()
|
|
372
|
+
return success
|
|
373
|
+
|
|
374
|
+
def disable_cron_job(self, job_name: str) -> bool:
|
|
375
|
+
"""
|
|
376
|
+
description:
|
|
377
|
+
禁用cron任务(在行首添加#注释)
|
|
378
|
+
parameters:
|
|
379
|
+
job_name(str): 任务名称
|
|
380
|
+
return:
|
|
381
|
+
success(bool): 是否成功
|
|
382
|
+
"""
|
|
383
|
+
if not self.job_exists(job_name):
|
|
384
|
+
raise ValueError(f"任务 '{job_name}' 不存在")
|
|
385
|
+
|
|
386
|
+
lines = self.cron_file.strip().split("\n")
|
|
387
|
+
updated_lines = []
|
|
388
|
+
|
|
389
|
+
for line in lines:
|
|
390
|
+
if f"# JOB_NAME: {job_name}" in line:
|
|
391
|
+
# 如果还没有被注释,添加#
|
|
392
|
+
if not line.strip().startswith("#"):
|
|
393
|
+
line = f"# {line.strip()}"
|
|
394
|
+
|
|
395
|
+
# 更新STATUS为CLOSE
|
|
396
|
+
line = re.sub(r"STATUS:\s*\w+", "STATUS:CLOSE", line)
|
|
397
|
+
# 如果原来没有STATUS,添加STATUS:CLOSE
|
|
398
|
+
if "STATUS:" not in line:
|
|
399
|
+
line = line.rstrip() + " STATUS:CLOSE"
|
|
400
|
+
updated_lines.append(line)
|
|
401
|
+
else:
|
|
402
|
+
updated_lines.append(line)
|
|
403
|
+
|
|
404
|
+
self.cron_file = "\n".join(updated_lines)
|
|
405
|
+
success = self._save_crontab()
|
|
406
|
+
return success
|
|
407
|
+
|
|
408
|
+
def trigger_cron_job(self, job_name: str) -> tuple[str, bool]:
|
|
409
|
+
"""
|
|
410
|
+
description:
|
|
411
|
+
手动触发cron任务
|
|
412
|
+
parameters:
|
|
413
|
+
job_name(str): 任务名称
|
|
414
|
+
return:
|
|
415
|
+
output(str): 命令执行输出
|
|
416
|
+
success(bool): 是否成功
|
|
417
|
+
"""
|
|
418
|
+
job = self.get_job_by_name(job_name)
|
|
419
|
+
if not job:
|
|
420
|
+
raise ValueError(f"任务 '{job_name}' 不存在")
|
|
421
|
+
|
|
422
|
+
# 使用shell执行命令,确保环境变量和路径正确
|
|
423
|
+
command = job["command"]
|
|
424
|
+
print(f"执行cron任务命令: {command}")
|
|
425
|
+
|
|
426
|
+
try:
|
|
427
|
+
# 使用subprocess直接执行命令,设置工作目录和环境变量
|
|
428
|
+
# 从命令中提取工作目录,如果命令以cd开头
|
|
429
|
+
working_dir = None
|
|
430
|
+
if command.startswith("cd "):
|
|
431
|
+
# 提取cd后的目录
|
|
432
|
+
parts = command.split("&&", 1)
|
|
433
|
+
if len(parts) > 1:
|
|
434
|
+
cd_part = parts[0].strip()
|
|
435
|
+
if cd_part.startswith("cd "):
|
|
436
|
+
working_dir = cd_part[3:].strip()
|
|
437
|
+
# 更新命令,移除cd部分
|
|
438
|
+
command = parts[1].strip()
|
|
439
|
+
|
|
440
|
+
result = subprocess.run(
|
|
441
|
+
command,
|
|
442
|
+
shell=True,
|
|
443
|
+
capture_output=True,
|
|
444
|
+
text=True,
|
|
445
|
+
cwd=working_dir or os.getcwd(), # 使用提取的工作目录或当前目录
|
|
446
|
+
env={
|
|
447
|
+
**os.environ, # 继承当前环境变量
|
|
448
|
+
"PYTHONPATH": working_dir or os.getcwd(), # 设置Python路径
|
|
449
|
+
},
|
|
450
|
+
timeout=300, # 5分钟超时
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
success = result.returncode == 0
|
|
454
|
+
output = result.stdout + result.stderr if result.stderr else result.stdout
|
|
455
|
+
|
|
456
|
+
print(f"命令执行结果: success={success}, returncode={result.returncode}")
|
|
457
|
+
print(f"输出: {output}")
|
|
458
|
+
|
|
459
|
+
return output, success
|
|
460
|
+
|
|
461
|
+
except subprocess.TimeoutExpired:
|
|
462
|
+
error_msg = f"任务执行超时: {command}"
|
|
463
|
+
print(error_msg)
|
|
464
|
+
return error_msg, False
|
|
465
|
+
except Exception as e:
|
|
466
|
+
error_msg = f"执行任务时发生错误: {str(e)}"
|
|
467
|
+
print(error_msg)
|
|
468
|
+
return error_msg, False
|
|
469
|
+
|
|
470
|
+
def job_exists(self, job_name: str) -> bool:
|
|
471
|
+
"""
|
|
472
|
+
description:
|
|
473
|
+
检查任务是否存在
|
|
474
|
+
parameters:
|
|
475
|
+
job_name(str): 任务名称
|
|
476
|
+
return:
|
|
477
|
+
exists(bool): 是否存在
|
|
478
|
+
"""
|
|
479
|
+
return self.get_job_by_name(job_name) is not None
|
|
480
|
+
|
|
481
|
+
def change_cron_job_schedule(self, job_name: str, schedule: str) -> bool:
|
|
482
|
+
"""
|
|
483
|
+
description:
|
|
484
|
+
修改cron任务的时间安排
|
|
485
|
+
parameters:
|
|
486
|
+
job_name(str): 任务名称
|
|
487
|
+
schedule(str): 任务时间安排
|
|
488
|
+
return:
|
|
489
|
+
success(bool): 是否成功
|
|
490
|
+
"""
|
|
491
|
+
if not self.job_exists(job_name):
|
|
492
|
+
raise ValueError(f"任务 '{job_name}' 不存在")
|
|
493
|
+
|
|
494
|
+
if not self._validate_schedule(schedule):
|
|
495
|
+
raise ValueError(f"无效的cron时间格式: {schedule}")
|
|
496
|
+
|
|
497
|
+
lines = self.cron_file.strip().split("\n")
|
|
498
|
+
updated_lines = []
|
|
499
|
+
|
|
500
|
+
for line in lines:
|
|
501
|
+
if f"# JOB_NAME: {job_name}" in line:
|
|
502
|
+
# 检查是否被注释
|
|
503
|
+
is_commented = line.strip().startswith("#")
|
|
504
|
+
if is_commented:
|
|
505
|
+
line = line.strip()[1:].strip()
|
|
506
|
+
|
|
507
|
+
parts = line.split(None, 5)
|
|
508
|
+
if len(parts) >= 6:
|
|
509
|
+
command_with_comment = parts[5]
|
|
510
|
+
command = command_with_comment
|
|
511
|
+
# 移除旧的注释
|
|
512
|
+
if f" # JOB_NAME: {job_name}" in command:
|
|
513
|
+
command = command.replace(f" # JOB_NAME: {job_name}", "")
|
|
514
|
+
# 移除旧的STATUS
|
|
515
|
+
command = re.sub(r"\s*STATUS:\s*\w+", "", command).strip()
|
|
516
|
+
|
|
517
|
+
# 提取当前状态
|
|
518
|
+
status = self._extract_status_from_comment(line)
|
|
519
|
+
new_line = (
|
|
520
|
+
f"{schedule} {command} # JOB_NAME: {job_name} STATUS:{status}"
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
# 如果原来是被注释的,保持注释状态
|
|
524
|
+
if is_commented:
|
|
525
|
+
new_line = f"# {new_line}"
|
|
526
|
+
|
|
527
|
+
updated_lines.append(new_line)
|
|
528
|
+
else:
|
|
529
|
+
updated_lines.append(line)
|
|
530
|
+
else:
|
|
531
|
+
updated_lines.append(line)
|
|
532
|
+
|
|
533
|
+
self.cron_file = "\n".join(updated_lines)
|
|
534
|
+
success = self._save_crontab()
|
|
535
|
+
return success
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
from typing import Optional, Dict
|
|
3
|
+
import names
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class NameGenerator:
|
|
7
|
+
"""姓名生成器,支持英文和中文姓名生成,支持多种生成方式"""
|
|
8
|
+
|
|
9
|
+
def __init__(self, use_api: bool = False, language: str = "en"):
|
|
10
|
+
"""
|
|
11
|
+
description:
|
|
12
|
+
初始化姓名生成器,设置生成方式(API或本地库)和语言
|
|
13
|
+
parameters:
|
|
14
|
+
use_api(bool): 是否使用API方式,True表示使用randomuser.me API,False表示使用本地库
|
|
15
|
+
language(str): 语言设置,"en"表示英文,"zh"表示中文,默认为"en"
|
|
16
|
+
return:
|
|
17
|
+
无
|
|
18
|
+
"""
|
|
19
|
+
self.use_api = use_api
|
|
20
|
+
self.api_url = "https://randomuser.me/api/"
|
|
21
|
+
self.language = language
|
|
22
|
+
self._faker_zh = None
|
|
23
|
+
|
|
24
|
+
def _generate_random_name_api(self) -> Optional[Dict[str, str]]:
|
|
25
|
+
"""
|
|
26
|
+
description:
|
|
27
|
+
使用 randomuser.me API 获取随机姓名(支持英文和中文)
|
|
28
|
+
注意:API不支持中文名,当language="zh"时,如果API返回非中文名则返回None,由调用方降级到本地生成
|
|
29
|
+
parameters:
|
|
30
|
+
无
|
|
31
|
+
return:
|
|
32
|
+
name(Optional[Dict[str, str]]): 包含first_name、last_name、full_name的字典,如果API调用失败或返回非中文名则返回None
|
|
33
|
+
"""
|
|
34
|
+
try:
|
|
35
|
+
# randomuser.me API 不支持中文名生成,当需要中文名时直接返回None,降级到本地生成
|
|
36
|
+
if self.language == "zh":
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
params = {
|
|
40
|
+
"nat": "us,gb,au,ca", # 限制为英语国家
|
|
41
|
+
"inc": "name", # 只获取姓名信息
|
|
42
|
+
}
|
|
43
|
+
response = requests.get(self.api_url, params=params, timeout=5)
|
|
44
|
+
response.raise_for_status()
|
|
45
|
+
data = response.json()
|
|
46
|
+
|
|
47
|
+
if "results" in data and len(data["results"]) > 0:
|
|
48
|
+
user = data["results"][0]
|
|
49
|
+
first_name = user["name"]["first"]
|
|
50
|
+
last_name = user["name"]["last"]
|
|
51
|
+
|
|
52
|
+
# 英文名格式:名字 姓氏(有空格)
|
|
53
|
+
full_name = f"{first_name} {last_name}"
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
"first_name": first_name,
|
|
57
|
+
"last_name": last_name,
|
|
58
|
+
"full_name": full_name,
|
|
59
|
+
}
|
|
60
|
+
return None
|
|
61
|
+
except requests.exceptions.RequestException:
|
|
62
|
+
return None
|
|
63
|
+
except (KeyError, IndexError):
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
def _get_faker_zh(self):
|
|
67
|
+
"""
|
|
68
|
+
description:
|
|
69
|
+
获取或创建中文 Faker 实例(延迟导入)
|
|
70
|
+
return:
|
|
71
|
+
Faker: 中文 Faker 实例
|
|
72
|
+
"""
|
|
73
|
+
if self._faker_zh is None:
|
|
74
|
+
from faker import Faker
|
|
75
|
+
self._faker_zh = Faker("zh_CN")
|
|
76
|
+
return self._faker_zh
|
|
77
|
+
|
|
78
|
+
def _generate_random_name_local_chinese(self) -> Dict[str, str]:
|
|
79
|
+
"""
|
|
80
|
+
description:
|
|
81
|
+
使用本地方式生成随机中文姓名(使用 Faker 库)
|
|
82
|
+
parameters:
|
|
83
|
+
无
|
|
84
|
+
return:
|
|
85
|
+
name(Dict[str, str]): 包含first_name、last_name、full_name的字典
|
|
86
|
+
"""
|
|
87
|
+
# 使用 Faker 库生成中文名
|
|
88
|
+
# Faker 的 last_name() 返回姓氏,first_name() 返回名字
|
|
89
|
+
faker = self._get_faker_zh()
|
|
90
|
+
last_name = faker.last_name()
|
|
91
|
+
first_name = faker.first_name()
|
|
92
|
+
|
|
93
|
+
# 中文名格式:姓氏+名字(无空格)
|
|
94
|
+
full_name = f"{last_name}{first_name}"
|
|
95
|
+
return {
|
|
96
|
+
"first_name": first_name,
|
|
97
|
+
"last_name": last_name,
|
|
98
|
+
"full_name": full_name,
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
def _generate_random_name_local_english(self) -> Dict[str, str]:
|
|
102
|
+
"""
|
|
103
|
+
description:
|
|
104
|
+
使用本地方式生成随机英文姓名
|
|
105
|
+
parameters:
|
|
106
|
+
无
|
|
107
|
+
return:
|
|
108
|
+
name(Dict[str, str]): 包含first_name、last_name、full_name的字典
|
|
109
|
+
"""
|
|
110
|
+
first_name = names.get_first_name()
|
|
111
|
+
last_name = names.get_last_name()
|
|
112
|
+
# 英文名格式:名字 姓氏(有空格)
|
|
113
|
+
full_name = f"{first_name} {last_name}"
|
|
114
|
+
return {
|
|
115
|
+
"first_name": first_name,
|
|
116
|
+
"last_name": last_name,
|
|
117
|
+
"full_name": full_name,
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
def _generate_random_name_local(self) -> Dict[str, str]:
|
|
121
|
+
"""
|
|
122
|
+
description:
|
|
123
|
+
使用本地方式生成随机姓名(根据语言设置选择生成方式)
|
|
124
|
+
parameters:
|
|
125
|
+
无
|
|
126
|
+
return:
|
|
127
|
+
name(Dict[str, str]): 包含first_name、last_name、full_name的字典
|
|
128
|
+
"""
|
|
129
|
+
if self.language == "zh":
|
|
130
|
+
return self._generate_random_name_local_chinese()
|
|
131
|
+
else:
|
|
132
|
+
return self._generate_random_name_local_english()
|
|
133
|
+
|
|
134
|
+
def generate_random_name(self) -> Dict[str, str]:
|
|
135
|
+
"""
|
|
136
|
+
description:
|
|
137
|
+
生成随机姓名(支持英文和中文),根据初始化时的设置选择API方式或本地方式,API失败时自动降级到本地方式
|
|
138
|
+
parameters:
|
|
139
|
+
无
|
|
140
|
+
return:
|
|
141
|
+
name(Dict[str, str]): 包含first_name、last_name、full_name的字典
|
|
142
|
+
"""
|
|
143
|
+
name = self._generate_random_name_api() if self.use_api else None
|
|
144
|
+
return name or self._generate_random_name_local()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pixelarraylib
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.7
|
|
4
4
|
Summary: PixelArray Python开发工具库 - 包含阿里云服务、数据库工具、装饰器、监控等功能
|
|
5
5
|
Author-email: Lu qi <qi.lu@pixelarrayai.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -29,6 +29,9 @@ Provides-Extra: system
|
|
|
29
29
|
Requires-Dist: cryptography; extra == "system"
|
|
30
30
|
Requires-Dist: paramiko; extra == "system"
|
|
31
31
|
Requires-Dist: requests; extra == "system"
|
|
32
|
+
Provides-Extra: utils
|
|
33
|
+
Requires-Dist: names; extra == "utils"
|
|
34
|
+
Requires-Dist: Faker; extra == "utils"
|
|
32
35
|
Provides-Extra: mysql
|
|
33
36
|
Requires-Dist: pymysql; extra == "mysql"
|
|
34
37
|
Requires-Dist: aiomysql; extra == "mysql"
|
|
@@ -36,9 +39,6 @@ Requires-Dist: requests; extra == "mysql"
|
|
|
36
39
|
Provides-Extra: redis
|
|
37
40
|
Requires-Dist: redis; extra == "redis"
|
|
38
41
|
Requires-Dist: requests; extra == "redis"
|
|
39
|
-
Provides-Extra: net
|
|
40
|
-
Requires-Dist: requests; extra == "net"
|
|
41
|
-
Requires-Dist: aiohttp; extra == "net"
|
|
42
42
|
Provides-Extra: gitlab
|
|
43
43
|
Requires-Dist: requests; extra == "gitlab"
|
|
44
44
|
Requires-Dist: aiohttp; extra == "gitlab"
|
|
@@ -161,6 +161,8 @@ Requires-Dist: requests; extra == "all"
|
|
|
161
161
|
Requires-Dist: aiohttp; extra == "all"
|
|
162
162
|
Requires-Dist: aiofiles; extra == "all"
|
|
163
163
|
Requires-Dist: pandas; extra == "all"
|
|
164
|
+
Requires-Dist: names; extra == "all"
|
|
165
|
+
Requires-Dist: Faker; extra == "all"
|
|
164
166
|
Dynamic: license-file
|
|
165
167
|
|
|
166
168
|
# PixelArrayLib - PixelArray Python开发工具库
|
|
@@ -33,7 +33,6 @@ pixelarraylib/gitlab/code_analyzer.py
|
|
|
33
33
|
pixelarraylib/gitlab/pypi_package_manager.py
|
|
34
34
|
pixelarraylib/monitor/__init__.py
|
|
35
35
|
pixelarraylib/monitor/feishu.py
|
|
36
|
-
pixelarraylib/net/request.py
|
|
37
36
|
pixelarraylib/scripts/__init__.py
|
|
38
37
|
pixelarraylib/scripts/build_website.py
|
|
39
38
|
pixelarraylib/scripts/collect_code_to_txt.py
|
|
@@ -43,4 +42,6 @@ pixelarraylib/scripts/remove_empty_lines.py
|
|
|
43
42
|
pixelarraylib/scripts/tson_convert.py
|
|
44
43
|
pixelarraylib/system/__init__.py
|
|
45
44
|
pixelarraylib/system/common.py
|
|
46
|
-
pixelarraylib/system/
|
|
45
|
+
pixelarraylib/system/cron_manager.py
|
|
46
|
+
pixelarraylib/system/tson.py
|
|
47
|
+
pixelarraylib/utils/name_generator.py
|
|
@@ -130,6 +130,8 @@ requests
|
|
|
130
130
|
aiohttp
|
|
131
131
|
aiofiles
|
|
132
132
|
pandas
|
|
133
|
+
names
|
|
134
|
+
Faker
|
|
133
135
|
|
|
134
136
|
[gitlab]
|
|
135
137
|
requests
|
|
@@ -144,10 +146,6 @@ pymysql
|
|
|
144
146
|
aiomysql
|
|
145
147
|
requests
|
|
146
148
|
|
|
147
|
-
[net]
|
|
148
|
-
requests
|
|
149
|
-
aiohttp
|
|
150
|
-
|
|
151
149
|
[redis]
|
|
152
150
|
redis
|
|
153
151
|
requests
|
|
@@ -156,3 +154,7 @@ requests
|
|
|
156
154
|
cryptography
|
|
157
155
|
paramiko
|
|
158
156
|
requests
|
|
157
|
+
|
|
158
|
+
[utils]
|
|
159
|
+
names
|
|
160
|
+
Faker
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pixelarraylib"
|
|
7
|
-
version = "1.1.
|
|
7
|
+
version = "1.1.7"
|
|
8
8
|
authors = [
|
|
9
9
|
{name = "Lu qi", email = "qi.lu@pixelarrayai.com"},
|
|
10
10
|
]
|
|
@@ -41,6 +41,10 @@ system = [
|
|
|
41
41
|
"paramiko",
|
|
42
42
|
"requests",
|
|
43
43
|
]
|
|
44
|
+
utils = [
|
|
45
|
+
"names",
|
|
46
|
+
"Faker",
|
|
47
|
+
]
|
|
44
48
|
mysql = [
|
|
45
49
|
"pymysql",
|
|
46
50
|
"aiomysql",
|
|
@@ -50,10 +54,6 @@ redis = [
|
|
|
50
54
|
"redis",
|
|
51
55
|
"requests",
|
|
52
56
|
]
|
|
53
|
-
net = [
|
|
54
|
-
"requests",
|
|
55
|
-
"aiohttp",
|
|
56
|
-
]
|
|
57
57
|
gitlab = [
|
|
58
58
|
"requests",
|
|
59
59
|
"aiohttp",
|
|
@@ -194,6 +194,8 @@ all = [
|
|
|
194
194
|
"aiohttp",
|
|
195
195
|
"aiofiles",
|
|
196
196
|
"pandas",
|
|
197
|
+
"names",
|
|
198
|
+
"Faker",
|
|
197
199
|
]
|
|
198
200
|
|
|
199
201
|
[project.scripts]
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import requests
|
|
2
|
-
from typing import Generator
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class Request:
|
|
6
|
-
def __init__(self, trust_env: bool = False):
|
|
7
|
-
"""
|
|
8
|
-
description:
|
|
9
|
-
初始化请求客户端
|
|
10
|
-
parameters:
|
|
11
|
-
trust_env(bool): 是否信任环境变量中的代理设置,默认为False
|
|
12
|
-
"""
|
|
13
|
-
self.session = requests.Session()
|
|
14
|
-
self.session.trust_env = trust_env
|
|
15
|
-
|
|
16
|
-
def _post(
|
|
17
|
-
self,
|
|
18
|
-
url: str,
|
|
19
|
-
data: dict = {},
|
|
20
|
-
headers: dict = {"Content-Type": "application/json"},
|
|
21
|
-
timeout: int = 0,
|
|
22
|
-
):
|
|
23
|
-
"""
|
|
24
|
-
description:
|
|
25
|
-
内部POST请求方法
|
|
26
|
-
parameters:
|
|
27
|
-
url(str): 请求URL
|
|
28
|
-
data(dict): 请求数据
|
|
29
|
-
headers(dict): 请求头
|
|
30
|
-
timeout(int): 请求超时时间(秒),0表示不设置超时
|
|
31
|
-
return:
|
|
32
|
-
response(requests.Response): 响应对象
|
|
33
|
-
"""
|
|
34
|
-
kwargs = {
|
|
35
|
-
"url": url,
|
|
36
|
-
"json": data,
|
|
37
|
-
"headers": headers,
|
|
38
|
-
}
|
|
39
|
-
if timeout:
|
|
40
|
-
kwargs["timeout"] = timeout
|
|
41
|
-
response = self.session.post(**kwargs)
|
|
42
|
-
return response
|
|
43
|
-
|
|
44
|
-
def _get(self, url: str, params: dict = {}, headers: dict = {}, timeout: int = 0):
|
|
45
|
-
"""
|
|
46
|
-
description:
|
|
47
|
-
内部GET请求方法
|
|
48
|
-
parameters:
|
|
49
|
-
url(str): 请求URL
|
|
50
|
-
params(dict): 请求参数
|
|
51
|
-
headers(dict): 请求头
|
|
52
|
-
timeout(int): 请求超时时间(秒),0表示不设置超时
|
|
53
|
-
return:
|
|
54
|
-
response(requests.Response): 响应对象
|
|
55
|
-
"""
|
|
56
|
-
kwargs = {
|
|
57
|
-
"url": url,
|
|
58
|
-
"params": params,
|
|
59
|
-
"headers": headers,
|
|
60
|
-
}
|
|
61
|
-
if timeout:
|
|
62
|
-
kwargs["timeout"] = timeout
|
|
63
|
-
response = self.session.get(**kwargs)
|
|
64
|
-
return response
|
|
65
|
-
|
|
66
|
-
def post(
|
|
67
|
-
self,
|
|
68
|
-
url: str,
|
|
69
|
-
data: dict = {},
|
|
70
|
-
headers: dict = {"Content-Type": "application/json"},
|
|
71
|
-
timeout: int = 0,
|
|
72
|
-
) -> tuple[dict, bool]:
|
|
73
|
-
"""
|
|
74
|
-
description:
|
|
75
|
-
发送 POST 请求
|
|
76
|
-
parameters:
|
|
77
|
-
url(str): 请求 URL
|
|
78
|
-
data(dict): 请求数据
|
|
79
|
-
headers(dict): 请求头
|
|
80
|
-
timeout(int): 请求超时时间
|
|
81
|
-
return:
|
|
82
|
-
response(dict): 响应数据
|
|
83
|
-
flag(bool): 请求是否成功
|
|
84
|
-
"""
|
|
85
|
-
response = self._post(url, data, headers, timeout)
|
|
86
|
-
|
|
87
|
-
if response.status_code == 200:
|
|
88
|
-
return response.json(), True
|
|
89
|
-
else:
|
|
90
|
-
print(response.status_code, response.text)
|
|
91
|
-
return {
|
|
92
|
-
"status_code": response.status_code,
|
|
93
|
-
"message": response.text,
|
|
94
|
-
}, False
|
|
95
|
-
|
|
96
|
-
def get(
|
|
97
|
-
self,
|
|
98
|
-
url: str,
|
|
99
|
-
params: dict = {},
|
|
100
|
-
headers: dict = {},
|
|
101
|
-
timeout: int = 0,
|
|
102
|
-
) -> tuple[dict, bool]:
|
|
103
|
-
"""
|
|
104
|
-
description:
|
|
105
|
-
发送 GET 请求
|
|
106
|
-
parameters:
|
|
107
|
-
url(str): 请求 URL
|
|
108
|
-
params(dict): 请求参数
|
|
109
|
-
headers(dict): 请求头
|
|
110
|
-
timeout(int): 请求超时时间
|
|
111
|
-
return:
|
|
112
|
-
response(dict): 响应数据
|
|
113
|
-
flag(bool): 请求是否成功
|
|
114
|
-
"""
|
|
115
|
-
response = self._get(url, params, headers, timeout)
|
|
116
|
-
|
|
117
|
-
if response.status_code == 200:
|
|
118
|
-
return response.json(), True
|
|
119
|
-
else:
|
|
120
|
-
print(response.status_code, response.text)
|
|
121
|
-
return {
|
|
122
|
-
"status_code": response.status_code,
|
|
123
|
-
"message": response.text,
|
|
124
|
-
}, False
|
|
125
|
-
|
|
126
|
-
def post_stream(
|
|
127
|
-
self,
|
|
128
|
-
url: str,
|
|
129
|
-
data: dict = {},
|
|
130
|
-
headers: dict = {"Content-Type": "application/json"},
|
|
131
|
-
timeout: int = 0,
|
|
132
|
-
) -> Generator[str, None, None]:
|
|
133
|
-
"""
|
|
134
|
-
description:
|
|
135
|
-
发送 POST 流式请求
|
|
136
|
-
parameters:
|
|
137
|
-
url(str): 请求 URL
|
|
138
|
-
data(dict): 请求数据
|
|
139
|
-
headers(dict): 请求头
|
|
140
|
-
timeout(int): 请求超时时间
|
|
141
|
-
return:
|
|
142
|
-
response(generator): 响应数据生成器
|
|
143
|
-
"""
|
|
144
|
-
response = self._post(url, data, headers, timeout)
|
|
145
|
-
if response.status_code == 200:
|
|
146
|
-
return response.iter_lines()
|
|
147
|
-
|
|
148
|
-
def get_stream(
|
|
149
|
-
self,
|
|
150
|
-
url: str,
|
|
151
|
-
params: dict = {},
|
|
152
|
-
headers: dict = {},
|
|
153
|
-
timeout: int = 0,
|
|
154
|
-
) -> Generator[str, None, None]:
|
|
155
|
-
"""
|
|
156
|
-
description:
|
|
157
|
-
发送 GET 流式请求
|
|
158
|
-
parameters:
|
|
159
|
-
url(str): 请求 URL
|
|
160
|
-
params(dict): 请求参数
|
|
161
|
-
headers(dict): 请求头
|
|
162
|
-
timeout(int): 请求超时时间
|
|
163
|
-
return:
|
|
164
|
-
response(generator): 响应数据生成器
|
|
165
|
-
"""
|
|
166
|
-
response = self._get(url, params, headers, timeout)
|
|
167
|
-
if response.status_code == 200:
|
|
168
|
-
return response.iter_lines()
|
|
169
|
-
|
|
170
|
-
def __del__(self):
|
|
171
|
-
"""
|
|
172
|
-
description:
|
|
173
|
-
析构函数,关闭会话连接
|
|
174
|
-
"""
|
|
175
|
-
self.session.close()
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|