funcguard 0.1.6__tar.gz → 0.1.8__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.
Potentially problematic release.
This version of funcguard might be problematic. Click here for more details.
- {funcguard-0.1.6 → funcguard-0.1.8}/PKG-INFO +85 -5
- {funcguard-0.1.6 → funcguard-0.1.8}/README.md +84 -4
- {funcguard-0.1.6 → funcguard-0.1.8}/funcguard/__init__.py +5 -2
- {funcguard-0.1.6 → funcguard-0.1.8}/funcguard/printer.py +13 -0
- funcguard-0.1.6/funcguard/tools.py → funcguard-0.1.8/funcguard/time_utils.py +71 -75
- funcguard-0.1.8/funcguard/tools.py +68 -0
- {funcguard-0.1.6 → funcguard-0.1.8}/funcguard.egg-info/PKG-INFO +85 -5
- {funcguard-0.1.6 → funcguard-0.1.8}/funcguard.egg-info/SOURCES.txt +2 -4
- {funcguard-0.1.6 → funcguard-0.1.8}/setup.py +1 -1
- funcguard-0.1.6/tests/run_test.py +0 -40
- funcguard-0.1.6/tests/test_core.py +0 -109
- funcguard-0.1.6/tests/test_tools.py +0 -165
- {funcguard-0.1.6 → funcguard-0.1.8}/LICENSE +0 -0
- {funcguard-0.1.6 → funcguard-0.1.8}/funcguard/core.py +0 -0
- {funcguard-0.1.6 → funcguard-0.1.8}/funcguard.egg-info/dependency_links.txt +0 -0
- {funcguard-0.1.6 → funcguard-0.1.8}/funcguard.egg-info/not-zip-safe +0 -0
- {funcguard-0.1.6 → funcguard-0.1.8}/funcguard.egg-info/requires.txt +0 -0
- {funcguard-0.1.6 → funcguard-0.1.8}/funcguard.egg-info/top_level.txt +0 -0
- {funcguard-0.1.6 → funcguard-0.1.8}/setup.cfg +0 -0
- {funcguard-0.1.6 → funcguard-0.1.8}/tests/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: funcguard
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.8
|
|
4
4
|
Summary: FuncGuard是一个Python库,提供函数执行超时控制、重试机制、HTTP请求封装和格式化打印工具。
|
|
5
5
|
Home-page: https://github.com/tinycen/funcguard
|
|
6
6
|
Author: tinycen
|
|
@@ -23,6 +23,7 @@ FuncGuard是一个Python库,提供了函数执行超时控制和重试机制
|
|
|
23
23
|
- HTTP请求封装(支持自动重试)
|
|
24
24
|
- 格式化打印工具(分隔线和块打印)
|
|
25
25
|
- 时间日志记录和耗时统计
|
|
26
|
+
- 函数执行时间监控和警告
|
|
26
27
|
|
|
27
28
|
## 安装/升级
|
|
28
29
|
|
|
@@ -110,10 +111,14 @@ print(response)
|
|
|
110
111
|
|
|
111
112
|
### 格式化打印
|
|
112
113
|
|
|
113
|
-
使用`print_line`和`
|
|
114
|
+
使用`print_line`、`print_block`和`print_title`函数进行格式化打印,便于查看和调试:
|
|
114
115
|
|
|
115
116
|
```python
|
|
116
|
-
from funcguard import print_line, print_block
|
|
117
|
+
from funcguard import print_line, print_block, print_title
|
|
118
|
+
|
|
119
|
+
# 打印带等号的标题
|
|
120
|
+
print_title("初始化分类器") # 输出:=== 初始化分类器 ===
|
|
121
|
+
print_title("训练完成", separator_char="*", padding_length=2) # 输出:** 训练完成 **
|
|
117
122
|
|
|
118
123
|
# 打印分隔线
|
|
119
124
|
print_line() # 默认使用40个'-'字符
|
|
@@ -174,12 +179,49 @@ for i in range(1, 101):
|
|
|
174
179
|
|
|
175
180
|
```
|
|
176
181
|
|
|
182
|
+
### 执行时间监控
|
|
183
|
+
|
|
184
|
+
使用`monitor_execution_time`函数监控函数执行时间:
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
from funcguard import monitor_execution_time
|
|
188
|
+
|
|
189
|
+
def some_function():
|
|
190
|
+
# 模拟一个耗时操作
|
|
191
|
+
import time
|
|
192
|
+
time.sleep(2)
|
|
193
|
+
return "操作完成"
|
|
194
|
+
|
|
195
|
+
# 模式1:总是打印执行时间
|
|
196
|
+
result = monitor_execution_time(
|
|
197
|
+
func=some_function,
|
|
198
|
+
print_mode=1
|
|
199
|
+
)
|
|
200
|
+
print(f"结果: {result}")
|
|
201
|
+
|
|
202
|
+
# 模式2:仅在超过阈值时打印警告
|
|
203
|
+
result = monitor_execution_time(
|
|
204
|
+
func=some_function,
|
|
205
|
+
warning_threshold=1.5, # 设置1.5秒的警告阈值
|
|
206
|
+
print_mode=2
|
|
207
|
+
)
|
|
208
|
+
print(f"结果: {result}")
|
|
209
|
+
|
|
210
|
+
# 模式0:不打印任何信息,仅返回结果和执行时间
|
|
211
|
+
result, duration = monitor_execution_time(
|
|
212
|
+
func=some_function,
|
|
213
|
+
print_mode=0
|
|
214
|
+
)
|
|
215
|
+
print(f"结果: {result}, 耗时: {duration}秒")
|
|
216
|
+
```
|
|
217
|
+
|
|
177
218
|
时间日志功能特点:
|
|
178
219
|
- 自动显示北京时间(UTC+8)
|
|
179
220
|
- 支持进度显示和预计完成时间计算
|
|
180
221
|
- 提供中英文双语统计信息
|
|
181
222
|
- 可显示总耗时、平均耗时等详细统计
|
|
182
223
|
- 支持i从0或从1开始的计数方式
|
|
224
|
+
- 支持函数执行时间监控和警告
|
|
183
225
|
|
|
184
226
|
## API文档
|
|
185
227
|
|
|
@@ -222,6 +264,8 @@ for i in range(1, 101):
|
|
|
222
264
|
- **返回值**: 根据return_type参数返回不同格式的响应数据
|
|
223
265
|
- **异常**: 当请求失败且重试次数用尽后,抛出相应的异常
|
|
224
266
|
|
|
267
|
+
### funcguard.time_utils
|
|
268
|
+
|
|
225
269
|
#### time_log(message, i=0, max_num=0, s_time=None, start_from=0)
|
|
226
270
|
|
|
227
271
|
- **参数**:
|
|
@@ -233,15 +277,42 @@ for i in range(1, 101):
|
|
|
233
277
|
- **返回值**: 无
|
|
234
278
|
- **功能**: 打印带时间戳的日志信息,支持进度显示和预计完成时间计算
|
|
235
279
|
|
|
236
|
-
#### time_diff(s_time=None, max_num=0, language="cn")
|
|
280
|
+
#### time_diff(s_time=None, max_num=0, language="cn", return_duration=1)
|
|
237
281
|
|
|
238
282
|
- **参数**:
|
|
239
283
|
- `s_time`: 开始时间,默认为None
|
|
240
284
|
- `max_num`: 任务数量,默认为0
|
|
241
285
|
- `language`: 语言选择("cn"中文,其他为英文),默认为"cn"
|
|
242
|
-
-
|
|
286
|
+
- `return_duration`: 返回模式,默认为1:
|
|
287
|
+
- 0 - 仅返回 total_seconds,不打印信息
|
|
288
|
+
- 1 - 仅打印信息,不返回 total_seconds
|
|
289
|
+
- 2 - 打印信息,并返回 total_seconds
|
|
290
|
+
- **返回值**:
|
|
291
|
+
- 如果s_time为None则返回当前时间
|
|
292
|
+
- 如果return_duration为0或2则返回持续时间(秒)
|
|
293
|
+
- 否则返回None
|
|
243
294
|
- **功能**: 计算并打印任务执行时间统计信息,支持中英文双语输出
|
|
244
295
|
|
|
296
|
+
#### monitor_execution_time(warning_threshold=None, print_mode=2, func=None, *args, **kwargs)
|
|
297
|
+
|
|
298
|
+
- **参数**:
|
|
299
|
+
- `warning_threshold`: 警告阈值(秒),如果执行耗时超过此值则打印警告,默认为None
|
|
300
|
+
- `print_mode`: 打印模式,支持三种模式:
|
|
301
|
+
- 0 - 仅返回total_seconds,不打印任何信息
|
|
302
|
+
- 1 - 总是打印执行时间
|
|
303
|
+
- 2 - 仅在超时打印警告信息(默认)
|
|
304
|
+
- `func`: 要监控的函数
|
|
305
|
+
- `args`: 函数的位置参数
|
|
306
|
+
- `kwargs`: 函数的关键字参数
|
|
307
|
+
- **返回值**:
|
|
308
|
+
- print_mode == 0: 元组 (result, total_seconds) - 函数的执行结果和执行时间(秒)
|
|
309
|
+
- print_mode == 1: 函数的执行结果
|
|
310
|
+
- print_mode == 2: 函数的执行结果
|
|
311
|
+
- **功能**: 监控函数执行时间,并返回函数的执行结果和执行时间
|
|
312
|
+
- **注意**: 该方法内部使用 time_diff 函数,根据 print_mode 自动设置 return_duration 参数
|
|
313
|
+
- print_mode 为 0 或 2 时,设置 return_duration=0( time_diff 仅返回total_seconds,不打印信息)
|
|
314
|
+
- print_mode 为 1 时,设置 return_duration=2( time_diff 打印信息,并返回total_seconds)
|
|
315
|
+
|
|
245
316
|
### funcguard.printer
|
|
246
317
|
|
|
247
318
|
#### print_line(separator_char: str = "-", separator_length: int = 40) -> None
|
|
@@ -252,6 +323,15 @@ for i in range(1, 101):
|
|
|
252
323
|
- **返回值**: 无
|
|
253
324
|
- **功能**: 打印分隔线,用于分隔不同的打印块
|
|
254
325
|
|
|
326
|
+
#### print_title(title: str, separator_char: str = "=", padding_length: int = 3) -> None
|
|
327
|
+
|
|
328
|
+
- **参数**:
|
|
329
|
+
- `title`: 标题内容
|
|
330
|
+
- `separator_char`: 分隔符字符,默认为'='
|
|
331
|
+
- `padding_length`: 标题两侧的分隔符数量,默认为3
|
|
332
|
+
- **返回值**: 无
|
|
333
|
+
- **功能**: 打印带分隔符的标题,格式如:=== 初始化分类器 ===
|
|
334
|
+
|
|
255
335
|
#### print_block(title: str, content: Any, separator_char: str = "-", separator_length: int = 40) -> None
|
|
256
336
|
|
|
257
337
|
- **参数**:
|
|
@@ -9,6 +9,7 @@ FuncGuard是一个Python库,提供了函数执行超时控制和重试机制
|
|
|
9
9
|
- HTTP请求封装(支持自动重试)
|
|
10
10
|
- 格式化打印工具(分隔线和块打印)
|
|
11
11
|
- 时间日志记录和耗时统计
|
|
12
|
+
- 函数执行时间监控和警告
|
|
12
13
|
|
|
13
14
|
## 安装/升级
|
|
14
15
|
|
|
@@ -96,10 +97,14 @@ print(response)
|
|
|
96
97
|
|
|
97
98
|
### 格式化打印
|
|
98
99
|
|
|
99
|
-
使用`print_line`和`
|
|
100
|
+
使用`print_line`、`print_block`和`print_title`函数进行格式化打印,便于查看和调试:
|
|
100
101
|
|
|
101
102
|
```python
|
|
102
|
-
from funcguard import print_line, print_block
|
|
103
|
+
from funcguard import print_line, print_block, print_title
|
|
104
|
+
|
|
105
|
+
# 打印带等号的标题
|
|
106
|
+
print_title("初始化分类器") # 输出:=== 初始化分类器 ===
|
|
107
|
+
print_title("训练完成", separator_char="*", padding_length=2) # 输出:** 训练完成 **
|
|
103
108
|
|
|
104
109
|
# 打印分隔线
|
|
105
110
|
print_line() # 默认使用40个'-'字符
|
|
@@ -160,12 +165,49 @@ for i in range(1, 101):
|
|
|
160
165
|
|
|
161
166
|
```
|
|
162
167
|
|
|
168
|
+
### 执行时间监控
|
|
169
|
+
|
|
170
|
+
使用`monitor_execution_time`函数监控函数执行时间:
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
from funcguard import monitor_execution_time
|
|
174
|
+
|
|
175
|
+
def some_function():
|
|
176
|
+
# 模拟一个耗时操作
|
|
177
|
+
import time
|
|
178
|
+
time.sleep(2)
|
|
179
|
+
return "操作完成"
|
|
180
|
+
|
|
181
|
+
# 模式1:总是打印执行时间
|
|
182
|
+
result = monitor_execution_time(
|
|
183
|
+
func=some_function,
|
|
184
|
+
print_mode=1
|
|
185
|
+
)
|
|
186
|
+
print(f"结果: {result}")
|
|
187
|
+
|
|
188
|
+
# 模式2:仅在超过阈值时打印警告
|
|
189
|
+
result = monitor_execution_time(
|
|
190
|
+
func=some_function,
|
|
191
|
+
warning_threshold=1.5, # 设置1.5秒的警告阈值
|
|
192
|
+
print_mode=2
|
|
193
|
+
)
|
|
194
|
+
print(f"结果: {result}")
|
|
195
|
+
|
|
196
|
+
# 模式0:不打印任何信息,仅返回结果和执行时间
|
|
197
|
+
result, duration = monitor_execution_time(
|
|
198
|
+
func=some_function,
|
|
199
|
+
print_mode=0
|
|
200
|
+
)
|
|
201
|
+
print(f"结果: {result}, 耗时: {duration}秒")
|
|
202
|
+
```
|
|
203
|
+
|
|
163
204
|
时间日志功能特点:
|
|
164
205
|
- 自动显示北京时间(UTC+8)
|
|
165
206
|
- 支持进度显示和预计完成时间计算
|
|
166
207
|
- 提供中英文双语统计信息
|
|
167
208
|
- 可显示总耗时、平均耗时等详细统计
|
|
168
209
|
- 支持i从0或从1开始的计数方式
|
|
210
|
+
- 支持函数执行时间监控和警告
|
|
169
211
|
|
|
170
212
|
## API文档
|
|
171
213
|
|
|
@@ -208,6 +250,8 @@ for i in range(1, 101):
|
|
|
208
250
|
- **返回值**: 根据return_type参数返回不同格式的响应数据
|
|
209
251
|
- **异常**: 当请求失败且重试次数用尽后,抛出相应的异常
|
|
210
252
|
|
|
253
|
+
### funcguard.time_utils
|
|
254
|
+
|
|
211
255
|
#### time_log(message, i=0, max_num=0, s_time=None, start_from=0)
|
|
212
256
|
|
|
213
257
|
- **参数**:
|
|
@@ -219,15 +263,42 @@ for i in range(1, 101):
|
|
|
219
263
|
- **返回值**: 无
|
|
220
264
|
- **功能**: 打印带时间戳的日志信息,支持进度显示和预计完成时间计算
|
|
221
265
|
|
|
222
|
-
#### time_diff(s_time=None, max_num=0, language="cn")
|
|
266
|
+
#### time_diff(s_time=None, max_num=0, language="cn", return_duration=1)
|
|
223
267
|
|
|
224
268
|
- **参数**:
|
|
225
269
|
- `s_time`: 开始时间,默认为None
|
|
226
270
|
- `max_num`: 任务数量,默认为0
|
|
227
271
|
- `language`: 语言选择("cn"中文,其他为英文),默认为"cn"
|
|
228
|
-
-
|
|
272
|
+
- `return_duration`: 返回模式,默认为1:
|
|
273
|
+
- 0 - 仅返回 total_seconds,不打印信息
|
|
274
|
+
- 1 - 仅打印信息,不返回 total_seconds
|
|
275
|
+
- 2 - 打印信息,并返回 total_seconds
|
|
276
|
+
- **返回值**:
|
|
277
|
+
- 如果s_time为None则返回当前时间
|
|
278
|
+
- 如果return_duration为0或2则返回持续时间(秒)
|
|
279
|
+
- 否则返回None
|
|
229
280
|
- **功能**: 计算并打印任务执行时间统计信息,支持中英文双语输出
|
|
230
281
|
|
|
282
|
+
#### monitor_execution_time(warning_threshold=None, print_mode=2, func=None, *args, **kwargs)
|
|
283
|
+
|
|
284
|
+
- **参数**:
|
|
285
|
+
- `warning_threshold`: 警告阈值(秒),如果执行耗时超过此值则打印警告,默认为None
|
|
286
|
+
- `print_mode`: 打印模式,支持三种模式:
|
|
287
|
+
- 0 - 仅返回total_seconds,不打印任何信息
|
|
288
|
+
- 1 - 总是打印执行时间
|
|
289
|
+
- 2 - 仅在超时打印警告信息(默认)
|
|
290
|
+
- `func`: 要监控的函数
|
|
291
|
+
- `args`: 函数的位置参数
|
|
292
|
+
- `kwargs`: 函数的关键字参数
|
|
293
|
+
- **返回值**:
|
|
294
|
+
- print_mode == 0: 元组 (result, total_seconds) - 函数的执行结果和执行时间(秒)
|
|
295
|
+
- print_mode == 1: 函数的执行结果
|
|
296
|
+
- print_mode == 2: 函数的执行结果
|
|
297
|
+
- **功能**: 监控函数执行时间,并返回函数的执行结果和执行时间
|
|
298
|
+
- **注意**: 该方法内部使用 time_diff 函数,根据 print_mode 自动设置 return_duration 参数
|
|
299
|
+
- print_mode 为 0 或 2 时,设置 return_duration=0( time_diff 仅返回total_seconds,不打印信息)
|
|
300
|
+
- print_mode 为 1 时,设置 return_duration=2( time_diff 打印信息,并返回total_seconds)
|
|
301
|
+
|
|
231
302
|
### funcguard.printer
|
|
232
303
|
|
|
233
304
|
#### print_line(separator_char: str = "-", separator_length: int = 40) -> None
|
|
@@ -238,6 +309,15 @@ for i in range(1, 101):
|
|
|
238
309
|
- **返回值**: 无
|
|
239
310
|
- **功能**: 打印分隔线,用于分隔不同的打印块
|
|
240
311
|
|
|
312
|
+
#### print_title(title: str, separator_char: str = "=", padding_length: int = 3) -> None
|
|
313
|
+
|
|
314
|
+
- **参数**:
|
|
315
|
+
- `title`: 标题内容
|
|
316
|
+
- `separator_char`: 分隔符字符,默认为'='
|
|
317
|
+
- `padding_length`: 标题两侧的分隔符数量,默认为3
|
|
318
|
+
- **返回值**: 无
|
|
319
|
+
- **功能**: 打印带分隔符的标题,格式如:=== 初始化分类器 ===
|
|
320
|
+
|
|
241
321
|
#### print_block(title: str, content: Any, separator_char: str = "-", separator_length: int = 40) -> None
|
|
242
322
|
|
|
243
323
|
- **参数**:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from .core import timeout_handler, retry_function
|
|
2
|
-
from .tools import send_request
|
|
3
|
-
from .
|
|
2
|
+
from .tools import send_request
|
|
3
|
+
from .time_utils import time_log, time_diff, monitor_execution_time
|
|
4
|
+
from .printer import print_block, print_line, print_title
|
|
4
5
|
|
|
5
6
|
__author__ = "ruocen"
|
|
6
7
|
|
|
@@ -11,6 +12,8 @@ __all__ = [
|
|
|
11
12
|
"send_request",
|
|
12
13
|
"time_log",
|
|
13
14
|
"time_diff",
|
|
15
|
+
"monitor_execution_time",
|
|
14
16
|
"print_block",
|
|
15
17
|
"print_line",
|
|
18
|
+
"print_title",
|
|
16
19
|
]
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
from typing import Any
|
|
2
2
|
|
|
3
|
+
# 打印带等号的标题(如:=== 初始化分类器 ===)
|
|
4
|
+
def print_title(title: str, separator_char: str = "=", padding_length: int = 3) -> None:
|
|
5
|
+
"""
|
|
6
|
+
打印带分隔符的标题,格式如:=== 初始化分类器 ===
|
|
7
|
+
|
|
8
|
+
:param title: 标题内容
|
|
9
|
+
:param separator_char: 分隔符字符,默认为'='
|
|
10
|
+
:param padding_length: 标题两侧的分隔符数量,默认为3
|
|
11
|
+
"""
|
|
12
|
+
separator = separator_char * padding_length
|
|
13
|
+
print(f"{separator} {title} {separator}")
|
|
14
|
+
|
|
15
|
+
|
|
3
16
|
# 打印分隔线
|
|
4
17
|
def print_line(separator_char: str = "-", separator_length: int = 40) -> None:
|
|
5
18
|
"""
|
|
@@ -1,70 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
"""
|
|
2
|
+
时间工具模块,提供时间计算、日志记录和执行时间监控功能
|
|
3
|
+
"""
|
|
3
4
|
from datetime import datetime, timezone, timedelta
|
|
4
|
-
from typing import Optional,
|
|
5
|
-
from .core import retry_function
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
# 发起请求
|
|
9
|
-
def send_request(
|
|
10
|
-
method: str,
|
|
11
|
-
url: str,
|
|
12
|
-
headers: Dict[str, str],
|
|
13
|
-
data: Optional[Any] = None,
|
|
14
|
-
return_type: str = "json",
|
|
15
|
-
timeout: int = 60,
|
|
16
|
-
auto_retry: Optional[Dict[str, Any]] = None,
|
|
17
|
-
) -> Union[Dict, str, requests.Response]:
|
|
18
|
-
"""
|
|
19
|
-
发送HTTP请求的通用函数
|
|
20
|
-
|
|
21
|
-
:param method: HTTP方法(GET, POST等)
|
|
22
|
-
:param url: 请求URL
|
|
23
|
-
:param headers: 请求头
|
|
24
|
-
:param data: 请求数据
|
|
25
|
-
:param return_type: 返回类型(json, text, response)
|
|
26
|
-
:param timeout: 请求超时时间
|
|
27
|
-
:param auto_retry: 自动重试配置,格式为:
|
|
28
|
-
{"task_name": "任务名称", "max_retries": 最大重试次数, "execute_timeout": 执行超时时间}
|
|
29
|
-
:return: 请求结果
|
|
30
|
-
"""
|
|
31
|
-
if data is None:
|
|
32
|
-
payload = {}
|
|
33
|
-
else:
|
|
34
|
-
if (isinstance(data, dict) or isinstance(data, list)) and data != {}:
|
|
35
|
-
payload = json.dumps(data, ensure_ascii=False)
|
|
36
|
-
else:
|
|
37
|
-
payload = data
|
|
38
|
-
if auto_retry is None:
|
|
39
|
-
response = requests.request(
|
|
40
|
-
method, url, headers=headers, data=payload, timeout=timeout
|
|
41
|
-
)
|
|
42
|
-
else:
|
|
43
|
-
max_retries = auto_retry.get("max_retries", 5)
|
|
44
|
-
execute_timeout = auto_retry.get("execute_timeout", 90)
|
|
45
|
-
task_name = auto_retry.get("task_name", "")
|
|
46
|
-
response = retry_function(
|
|
47
|
-
requests.request,
|
|
48
|
-
max_retries,
|
|
49
|
-
execute_timeout,
|
|
50
|
-
task_name,
|
|
51
|
-
method,
|
|
52
|
-
url,
|
|
53
|
-
headers=headers,
|
|
54
|
-
data=payload,
|
|
55
|
-
timeout=timeout,
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
if response is None:
|
|
59
|
-
raise ValueError("请求返回的响应为None")
|
|
60
|
-
|
|
61
|
-
if return_type == "json":
|
|
62
|
-
result = response.json()
|
|
63
|
-
elif return_type == "response":
|
|
64
|
-
return response
|
|
65
|
-
else:
|
|
66
|
-
result = response.text
|
|
67
|
-
return result
|
|
5
|
+
from typing import Optional, Union
|
|
68
6
|
|
|
69
7
|
|
|
70
8
|
# 打印时间
|
|
@@ -103,14 +41,19 @@ def time_log(message, i = 0, max_num = 0, s_time = None, start_from = 0 ) :
|
|
|
103
41
|
|
|
104
42
|
|
|
105
43
|
# 计算持续时间
|
|
106
|
-
def time_diff(s_time = None, max_num = 0, language = "cn") :
|
|
44
|
+
def time_diff(s_time = None, max_num = 0, language = "cn", return_duration = 1) :
|
|
107
45
|
"""
|
|
108
46
|
计算并打印任务执行时间统计信息
|
|
109
47
|
|
|
110
48
|
:param s_time: 开始时间
|
|
111
49
|
:param max_num: 任务数量
|
|
112
50
|
:param language: 语言选择("cn"中文,其他为英文)
|
|
113
|
-
:
|
|
51
|
+
:param return_duration:
|
|
52
|
+
返回模式,默认为1,
|
|
53
|
+
0,仅返回 total_seconds,不打印信息
|
|
54
|
+
1,仅打印信息,不返回 total_seconds
|
|
55
|
+
2,print 信息,并返回 total_seconds
|
|
56
|
+
:return: 如果s_time为None则返回当前时间
|
|
114
57
|
"""
|
|
115
58
|
# 获取当前时间并转换为北京时间
|
|
116
59
|
now = datetime.now( timezone( timedelta( hours = 8 ) ) )
|
|
@@ -120,18 +63,22 @@ def time_diff(s_time = None, max_num = 0, language = "cn") :
|
|
|
120
63
|
|
|
121
64
|
e_time = now
|
|
122
65
|
duration = e_time - s_time
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
66
|
+
total_seconds = int(duration.total_seconds())
|
|
67
|
+
if return_duration == 0:
|
|
68
|
+
return total_seconds
|
|
69
|
+
|
|
70
|
+
hours = total_seconds // 3600
|
|
71
|
+
duration_minutes = (total_seconds % 3600) // 60
|
|
72
|
+
seconds = total_seconds % 60
|
|
126
73
|
result = f"{hours:02d}:{duration_minutes:02d}:{seconds:02d}"
|
|
127
74
|
|
|
128
75
|
# 将时间差转化为分钟
|
|
129
76
|
minutes = round( duration.total_seconds() / 60 )
|
|
130
77
|
if max_num == 0 :
|
|
131
78
|
if language == "cn" :
|
|
132
|
-
print( "总耗时:{}".format(
|
|
79
|
+
print( "总耗时:{:02d} : {:02d} : {:02d}".format( hours, duration_minutes, seconds ) )
|
|
133
80
|
else :
|
|
134
|
-
print( "Total time: {}".format(
|
|
81
|
+
print( "Total time: {:02d} : {:02d} : {:02d}".format( hours, duration_minutes, seconds ) )
|
|
135
82
|
else :
|
|
136
83
|
eve_minutes = round( minutes / max_num, 3 )
|
|
137
84
|
if language == "cn" :
|
|
@@ -143,6 +90,55 @@ def time_diff(s_time = None, max_num = 0, language = "cn") :
|
|
|
143
90
|
e_time.strftime( "%Y-%m-%d %H:%M" ) ) )
|
|
144
91
|
print( "Total time: {},Total minutes: {},Number: {},Average time: {} minutes".format( result, minutes,
|
|
145
92
|
max_num,
|
|
146
|
-
|
|
93
|
+
eve_minutes ) )
|
|
94
|
+
if return_duration == 2:
|
|
95
|
+
return total_seconds
|
|
96
|
+
return
|
|
97
|
+
|
|
147
98
|
|
|
148
|
-
|
|
99
|
+
# 监控程序的执行时间
|
|
100
|
+
def monitor_execution_time(warning_threshold=None, print_mode=2, func=None, *args, **kwargs):
|
|
101
|
+
"""
|
|
102
|
+
监控函数执行时间,并返回函数的执行结果和执行时间
|
|
103
|
+
|
|
104
|
+
:param warning_threshold: 警告阈值(秒),如果执行耗时超过此值则打印警告
|
|
105
|
+
:param print_mode: 打印模式,支持三种模式:
|
|
106
|
+
0 - 仅返回total_seconds,不打印任何信息
|
|
107
|
+
1 - 总是打印执行时间
|
|
108
|
+
2 - 仅在超时打印警告信息(默认)
|
|
109
|
+
:param func: 要监控的函数
|
|
110
|
+
:param args: 函数的位置参数
|
|
111
|
+
:param kwargs: 函数的关键字参数
|
|
112
|
+
:return:
|
|
113
|
+
print_mode == 0: 函数的执行结果, total_seconds
|
|
114
|
+
print_mode == 1: 函数的执行结果
|
|
115
|
+
print_mode == 2: 函数的执行结果
|
|
116
|
+
"""
|
|
117
|
+
s_time = datetime.now( timezone( timedelta( hours = 8 ) ) )
|
|
118
|
+
|
|
119
|
+
if func is None:
|
|
120
|
+
raise ValueError("func is None, func must be a function")
|
|
121
|
+
|
|
122
|
+
# 执行函数并获取结果
|
|
123
|
+
result = func(*args, **kwargs)
|
|
124
|
+
|
|
125
|
+
# 计算执行时间
|
|
126
|
+
if print_mode in [ 0, 2 ] : # print_mode:0 和 2 需要 time_diff 内部不执行 print 但返回 total_seconds 信息
|
|
127
|
+
return_duration = 0
|
|
128
|
+
|
|
129
|
+
elif print_mode == 1 :
|
|
130
|
+
return_duration = 2
|
|
131
|
+
|
|
132
|
+
else:
|
|
133
|
+
raise ValueError("print_mode must be 0, 1 or 2")
|
|
134
|
+
|
|
135
|
+
total_seconds = time_diff(s_time, return_duration=return_duration) # pyright: ignore[reportGeneralTypeIssues]
|
|
136
|
+
|
|
137
|
+
# 根据打印模式决定是否打印耗时信息
|
|
138
|
+
if print_mode == 2 and warning_threshold is not None and total_seconds > warning_threshold:
|
|
139
|
+
print(f"警告: 函数 {func.__name__} 执行耗时 {total_seconds:.2f}秒,超过阈值 {warning_threshold}秒")
|
|
140
|
+
|
|
141
|
+
if print_mode == 0:
|
|
142
|
+
return result, total_seconds
|
|
143
|
+
|
|
144
|
+
return result
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import requests
|
|
3
|
+
from datetime import datetime, timezone, timedelta
|
|
4
|
+
from typing import Optional, Dict, Any, Union
|
|
5
|
+
from .core import retry_function
|
|
6
|
+
from .time_utils import time_log, time_diff, monitor_execution_time
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# 发起请求
|
|
10
|
+
def send_request(
|
|
11
|
+
method: str,
|
|
12
|
+
url: str,
|
|
13
|
+
headers: Dict[str, str],
|
|
14
|
+
data: Optional[Any] = None,
|
|
15
|
+
return_type: str = "json",
|
|
16
|
+
timeout: int = 60,
|
|
17
|
+
auto_retry: Optional[Dict[str, Any]] = None,
|
|
18
|
+
) -> Union[Dict, str, requests.Response]:
|
|
19
|
+
"""
|
|
20
|
+
发送HTTP请求的通用函数
|
|
21
|
+
|
|
22
|
+
:param method: HTTP方法(GET, POST等)
|
|
23
|
+
:param url: 请求URL
|
|
24
|
+
:param headers: 请求头
|
|
25
|
+
:param data: 请求数据
|
|
26
|
+
:param return_type: 返回类型(json, text, response)
|
|
27
|
+
:param timeout: 请求超时时间
|
|
28
|
+
:param auto_retry: 自动重试配置,格式为:
|
|
29
|
+
{"task_name": "任务名称", "max_retries": 最大重试次数, "execute_timeout": 执行超时时间}
|
|
30
|
+
:return: 请求结果
|
|
31
|
+
"""
|
|
32
|
+
if data is None:
|
|
33
|
+
payload = {}
|
|
34
|
+
else:
|
|
35
|
+
if (isinstance(data, dict) or isinstance(data, list)) and data != {}:
|
|
36
|
+
payload = json.dumps(data, ensure_ascii=False)
|
|
37
|
+
else:
|
|
38
|
+
payload = data
|
|
39
|
+
if auto_retry is None:
|
|
40
|
+
response = requests.request(
|
|
41
|
+
method, url, headers=headers, data=payload, timeout=timeout
|
|
42
|
+
)
|
|
43
|
+
else:
|
|
44
|
+
max_retries = auto_retry.get("max_retries", 5)
|
|
45
|
+
execute_timeout = auto_retry.get("execute_timeout", 90)
|
|
46
|
+
task_name = auto_retry.get("task_name", "")
|
|
47
|
+
response = retry_function(
|
|
48
|
+
requests.request,
|
|
49
|
+
max_retries,
|
|
50
|
+
execute_timeout,
|
|
51
|
+
task_name,
|
|
52
|
+
method,
|
|
53
|
+
url,
|
|
54
|
+
headers=headers,
|
|
55
|
+
data=payload,
|
|
56
|
+
timeout=timeout,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
if response is None:
|
|
60
|
+
raise ValueError("请求返回的响应为None")
|
|
61
|
+
|
|
62
|
+
if return_type == "json":
|
|
63
|
+
result = response.json()
|
|
64
|
+
elif return_type == "response":
|
|
65
|
+
return response
|
|
66
|
+
else:
|
|
67
|
+
result = response.text
|
|
68
|
+
return result
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: funcguard
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.8
|
|
4
4
|
Summary: FuncGuard是一个Python库,提供函数执行超时控制、重试机制、HTTP请求封装和格式化打印工具。
|
|
5
5
|
Home-page: https://github.com/tinycen/funcguard
|
|
6
6
|
Author: tinycen
|
|
@@ -23,6 +23,7 @@ FuncGuard是一个Python库,提供了函数执行超时控制和重试机制
|
|
|
23
23
|
- HTTP请求封装(支持自动重试)
|
|
24
24
|
- 格式化打印工具(分隔线和块打印)
|
|
25
25
|
- 时间日志记录和耗时统计
|
|
26
|
+
- 函数执行时间监控和警告
|
|
26
27
|
|
|
27
28
|
## 安装/升级
|
|
28
29
|
|
|
@@ -110,10 +111,14 @@ print(response)
|
|
|
110
111
|
|
|
111
112
|
### 格式化打印
|
|
112
113
|
|
|
113
|
-
使用`print_line`和`
|
|
114
|
+
使用`print_line`、`print_block`和`print_title`函数进行格式化打印,便于查看和调试:
|
|
114
115
|
|
|
115
116
|
```python
|
|
116
|
-
from funcguard import print_line, print_block
|
|
117
|
+
from funcguard import print_line, print_block, print_title
|
|
118
|
+
|
|
119
|
+
# 打印带等号的标题
|
|
120
|
+
print_title("初始化分类器") # 输出:=== 初始化分类器 ===
|
|
121
|
+
print_title("训练完成", separator_char="*", padding_length=2) # 输出:** 训练完成 **
|
|
117
122
|
|
|
118
123
|
# 打印分隔线
|
|
119
124
|
print_line() # 默认使用40个'-'字符
|
|
@@ -174,12 +179,49 @@ for i in range(1, 101):
|
|
|
174
179
|
|
|
175
180
|
```
|
|
176
181
|
|
|
182
|
+
### 执行时间监控
|
|
183
|
+
|
|
184
|
+
使用`monitor_execution_time`函数监控函数执行时间:
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
from funcguard import monitor_execution_time
|
|
188
|
+
|
|
189
|
+
def some_function():
|
|
190
|
+
# 模拟一个耗时操作
|
|
191
|
+
import time
|
|
192
|
+
time.sleep(2)
|
|
193
|
+
return "操作完成"
|
|
194
|
+
|
|
195
|
+
# 模式1:总是打印执行时间
|
|
196
|
+
result = monitor_execution_time(
|
|
197
|
+
func=some_function,
|
|
198
|
+
print_mode=1
|
|
199
|
+
)
|
|
200
|
+
print(f"结果: {result}")
|
|
201
|
+
|
|
202
|
+
# 模式2:仅在超过阈值时打印警告
|
|
203
|
+
result = monitor_execution_time(
|
|
204
|
+
func=some_function,
|
|
205
|
+
warning_threshold=1.5, # 设置1.5秒的警告阈值
|
|
206
|
+
print_mode=2
|
|
207
|
+
)
|
|
208
|
+
print(f"结果: {result}")
|
|
209
|
+
|
|
210
|
+
# 模式0:不打印任何信息,仅返回结果和执行时间
|
|
211
|
+
result, duration = monitor_execution_time(
|
|
212
|
+
func=some_function,
|
|
213
|
+
print_mode=0
|
|
214
|
+
)
|
|
215
|
+
print(f"结果: {result}, 耗时: {duration}秒")
|
|
216
|
+
```
|
|
217
|
+
|
|
177
218
|
时间日志功能特点:
|
|
178
219
|
- 自动显示北京时间(UTC+8)
|
|
179
220
|
- 支持进度显示和预计完成时间计算
|
|
180
221
|
- 提供中英文双语统计信息
|
|
181
222
|
- 可显示总耗时、平均耗时等详细统计
|
|
182
223
|
- 支持i从0或从1开始的计数方式
|
|
224
|
+
- 支持函数执行时间监控和警告
|
|
183
225
|
|
|
184
226
|
## API文档
|
|
185
227
|
|
|
@@ -222,6 +264,8 @@ for i in range(1, 101):
|
|
|
222
264
|
- **返回值**: 根据return_type参数返回不同格式的响应数据
|
|
223
265
|
- **异常**: 当请求失败且重试次数用尽后,抛出相应的异常
|
|
224
266
|
|
|
267
|
+
### funcguard.time_utils
|
|
268
|
+
|
|
225
269
|
#### time_log(message, i=0, max_num=0, s_time=None, start_from=0)
|
|
226
270
|
|
|
227
271
|
- **参数**:
|
|
@@ -233,15 +277,42 @@ for i in range(1, 101):
|
|
|
233
277
|
- **返回值**: 无
|
|
234
278
|
- **功能**: 打印带时间戳的日志信息,支持进度显示和预计完成时间计算
|
|
235
279
|
|
|
236
|
-
#### time_diff(s_time=None, max_num=0, language="cn")
|
|
280
|
+
#### time_diff(s_time=None, max_num=0, language="cn", return_duration=1)
|
|
237
281
|
|
|
238
282
|
- **参数**:
|
|
239
283
|
- `s_time`: 开始时间,默认为None
|
|
240
284
|
- `max_num`: 任务数量,默认为0
|
|
241
285
|
- `language`: 语言选择("cn"中文,其他为英文),默认为"cn"
|
|
242
|
-
-
|
|
286
|
+
- `return_duration`: 返回模式,默认为1:
|
|
287
|
+
- 0 - 仅返回 total_seconds,不打印信息
|
|
288
|
+
- 1 - 仅打印信息,不返回 total_seconds
|
|
289
|
+
- 2 - 打印信息,并返回 total_seconds
|
|
290
|
+
- **返回值**:
|
|
291
|
+
- 如果s_time为None则返回当前时间
|
|
292
|
+
- 如果return_duration为0或2则返回持续时间(秒)
|
|
293
|
+
- 否则返回None
|
|
243
294
|
- **功能**: 计算并打印任务执行时间统计信息,支持中英文双语输出
|
|
244
295
|
|
|
296
|
+
#### monitor_execution_time(warning_threshold=None, print_mode=2, func=None, *args, **kwargs)
|
|
297
|
+
|
|
298
|
+
- **参数**:
|
|
299
|
+
- `warning_threshold`: 警告阈值(秒),如果执行耗时超过此值则打印警告,默认为None
|
|
300
|
+
- `print_mode`: 打印模式,支持三种模式:
|
|
301
|
+
- 0 - 仅返回total_seconds,不打印任何信息
|
|
302
|
+
- 1 - 总是打印执行时间
|
|
303
|
+
- 2 - 仅在超时打印警告信息(默认)
|
|
304
|
+
- `func`: 要监控的函数
|
|
305
|
+
- `args`: 函数的位置参数
|
|
306
|
+
- `kwargs`: 函数的关键字参数
|
|
307
|
+
- **返回值**:
|
|
308
|
+
- print_mode == 0: 元组 (result, total_seconds) - 函数的执行结果和执行时间(秒)
|
|
309
|
+
- print_mode == 1: 函数的执行结果
|
|
310
|
+
- print_mode == 2: 函数的执行结果
|
|
311
|
+
- **功能**: 监控函数执行时间,并返回函数的执行结果和执行时间
|
|
312
|
+
- **注意**: 该方法内部使用 time_diff 函数,根据 print_mode 自动设置 return_duration 参数
|
|
313
|
+
- print_mode 为 0 或 2 时,设置 return_duration=0( time_diff 仅返回total_seconds,不打印信息)
|
|
314
|
+
- print_mode 为 1 时,设置 return_duration=2( time_diff 打印信息,并返回total_seconds)
|
|
315
|
+
|
|
245
316
|
### funcguard.printer
|
|
246
317
|
|
|
247
318
|
#### print_line(separator_char: str = "-", separator_length: int = 40) -> None
|
|
@@ -252,6 +323,15 @@ for i in range(1, 101):
|
|
|
252
323
|
- **返回值**: 无
|
|
253
324
|
- **功能**: 打印分隔线,用于分隔不同的打印块
|
|
254
325
|
|
|
326
|
+
#### print_title(title: str, separator_char: str = "=", padding_length: int = 3) -> None
|
|
327
|
+
|
|
328
|
+
- **参数**:
|
|
329
|
+
- `title`: 标题内容
|
|
330
|
+
- `separator_char`: 分隔符字符,默认为'='
|
|
331
|
+
- `padding_length`: 标题两侧的分隔符数量,默认为3
|
|
332
|
+
- **返回值**: 无
|
|
333
|
+
- **功能**: 打印带分隔符的标题,格式如:=== 初始化分类器 ===
|
|
334
|
+
|
|
255
335
|
#### print_block(title: str, content: Any, separator_char: str = "-", separator_length: int = 40) -> None
|
|
256
336
|
|
|
257
337
|
- **参数**:
|
|
@@ -4,6 +4,7 @@ setup.py
|
|
|
4
4
|
funcguard/__init__.py
|
|
5
5
|
funcguard/core.py
|
|
6
6
|
funcguard/printer.py
|
|
7
|
+
funcguard/time_utils.py
|
|
7
8
|
funcguard/tools.py
|
|
8
9
|
funcguard.egg-info/PKG-INFO
|
|
9
10
|
funcguard.egg-info/SOURCES.txt
|
|
@@ -11,7 +12,4 @@ funcguard.egg-info/dependency_links.txt
|
|
|
11
12
|
funcguard.egg-info/not-zip-safe
|
|
12
13
|
funcguard.egg-info/requires.txt
|
|
13
14
|
funcguard.egg-info/top_level.txt
|
|
14
|
-
tests/__init__.py
|
|
15
|
-
tests/run_test.py
|
|
16
|
-
tests/test_core.py
|
|
17
|
-
tests/test_tools.py
|
|
15
|
+
tests/__init__.py
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
测试运行脚本
|
|
4
|
-
运行所有测试用例
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import unittest
|
|
8
|
-
import sys
|
|
9
|
-
import os
|
|
10
|
-
|
|
11
|
-
# 添加项目根目录到Python路径
|
|
12
|
-
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
13
|
-
sys.path.insert(0, project_root)
|
|
14
|
-
|
|
15
|
-
# 导入测试模块
|
|
16
|
-
from tests.test_core import TestTimeoutHandler, TestRetryFunction
|
|
17
|
-
from tests.test_tools import TestSendRequest
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def run_all_tests():
|
|
21
|
-
"""运行所有测试"""
|
|
22
|
-
# 创建测试套件
|
|
23
|
-
loader = unittest.TestLoader()
|
|
24
|
-
suite = unittest.TestSuite()
|
|
25
|
-
|
|
26
|
-
# 添加测试类
|
|
27
|
-
suite.addTests(loader.loadTestsFromTestCase(TestTimeoutHandler))
|
|
28
|
-
suite.addTests(loader.loadTestsFromTestCase(TestRetryFunction))
|
|
29
|
-
suite.addTests(loader.loadTestsFromTestCase(TestSendRequest))
|
|
30
|
-
|
|
31
|
-
# 运行测试
|
|
32
|
-
runner = unittest.TextTestRunner(verbosity=2)
|
|
33
|
-
result = runner.run(suite)
|
|
34
|
-
|
|
35
|
-
return result.wasSuccessful()
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if __name__ == '__main__':
|
|
39
|
-
success = run_all_tests()
|
|
40
|
-
sys.exit(0 if success else 1)
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import time
|
|
2
|
-
import unittest
|
|
3
|
-
from unittest.mock import patch
|
|
4
|
-
|
|
5
|
-
from funcguard.core import timeout_handler, retry_function
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class TestTimeoutHandler(unittest.TestCase):
|
|
9
|
-
"""测试timeout_handler函数"""
|
|
10
|
-
|
|
11
|
-
def test_normal_execution(self):
|
|
12
|
-
"""测试正常执行的函数"""
|
|
13
|
-
def quick_function():
|
|
14
|
-
time.sleep(0.1)
|
|
15
|
-
return "success"
|
|
16
|
-
|
|
17
|
-
result = timeout_handler(quick_function, execution_timeout=2)
|
|
18
|
-
self.assertEqual(result, "success")
|
|
19
|
-
|
|
20
|
-
def test_timeout_execution(self):
|
|
21
|
-
"""测试超时的函数"""
|
|
22
|
-
def slow_function():
|
|
23
|
-
time.sleep(2)
|
|
24
|
-
return "should not reach here"
|
|
25
|
-
|
|
26
|
-
from funcguard.core import FuncguardTimeoutError
|
|
27
|
-
with self.assertRaises(FuncguardTimeoutError) as context:
|
|
28
|
-
timeout_handler(slow_function, execution_timeout=1)
|
|
29
|
-
|
|
30
|
-
self.assertIn("执行时间超过 1 秒", str(context.exception))
|
|
31
|
-
|
|
32
|
-
def test_function_with_args(self):
|
|
33
|
-
"""测试带参数的函数"""
|
|
34
|
-
def add_numbers(a, b):
|
|
35
|
-
return a + b
|
|
36
|
-
|
|
37
|
-
result = timeout_handler(add_numbers, args=(3, 4), execution_timeout=2)
|
|
38
|
-
self.assertEqual(result, 7)
|
|
39
|
-
|
|
40
|
-
def test_function_with_kwargs(self):
|
|
41
|
-
"""测试带关键字参数的函数"""
|
|
42
|
-
def greet(name, greeting="Hello"):
|
|
43
|
-
return f"{greeting}, {name}!"
|
|
44
|
-
|
|
45
|
-
result = timeout_handler(greet, kwargs={"name": "World", "greeting": "Hi"}, execution_timeout=2)
|
|
46
|
-
self.assertEqual(result, "Hi, World!")
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
class TestRetryFunction(unittest.TestCase):
|
|
50
|
-
"""测试retry_function函数"""
|
|
51
|
-
|
|
52
|
-
def test_successful_first_try(self):
|
|
53
|
-
"""测试第一次就成功的函数"""
|
|
54
|
-
def always_success():
|
|
55
|
-
return "success"
|
|
56
|
-
|
|
57
|
-
result = retry_function(always_success, max_retries=2, task_name="test")
|
|
58
|
-
self.assertEqual(result, "success")
|
|
59
|
-
|
|
60
|
-
def test_retry_until_success(self):
|
|
61
|
-
"""测试重试后成功的函数"""
|
|
62
|
-
attempts = []
|
|
63
|
-
|
|
64
|
-
def sometimes_fail():
|
|
65
|
-
attempts.append(len(attempts))
|
|
66
|
-
if len(attempts) < 2:
|
|
67
|
-
raise ValueError("Not yet")
|
|
68
|
-
return "finally success"
|
|
69
|
-
|
|
70
|
-
result = retry_function(sometimes_fail, max_retries=2, task_name="test")
|
|
71
|
-
self.assertEqual(result, "finally success")
|
|
72
|
-
self.assertEqual(len(attempts), 2)
|
|
73
|
-
|
|
74
|
-
def test_exhaust_all_retries(self):
|
|
75
|
-
"""测试耗尽所有重试次数"""
|
|
76
|
-
def always_fail():
|
|
77
|
-
raise RuntimeError("Always fails")
|
|
78
|
-
|
|
79
|
-
with self.assertRaises(RuntimeError) as context:
|
|
80
|
-
retry_function(always_fail, max_retries=2, task_name="test")
|
|
81
|
-
|
|
82
|
-
self.assertEqual(str(context.exception), "Always fails")
|
|
83
|
-
|
|
84
|
-
def test_timeout_in_retry(self):
|
|
85
|
-
"""测试重试中的超时处理"""
|
|
86
|
-
def timeout_function():
|
|
87
|
-
time.sleep(2)
|
|
88
|
-
return "should timeout"
|
|
89
|
-
|
|
90
|
-
# retry_function会在重试耗尽后抛出最后的异常
|
|
91
|
-
from funcguard.core import FuncguardTimeoutError
|
|
92
|
-
with self.assertRaises(FuncguardTimeoutError):
|
|
93
|
-
retry_function(timeout_function, max_retries=1, execute_timeout=1, task_name="test")
|
|
94
|
-
|
|
95
|
-
@patch('time.sleep')
|
|
96
|
-
def test_retry_with_custom_delay(self, mock_sleep):
|
|
97
|
-
"""测试重试延迟"""
|
|
98
|
-
def always_fail():
|
|
99
|
-
raise ValueError("test")
|
|
100
|
-
|
|
101
|
-
with self.assertRaises(ValueError):
|
|
102
|
-
retry_function(always_fail, max_retries=2, task_name="test")
|
|
103
|
-
|
|
104
|
-
# 验证sleep被调用了正确的次数
|
|
105
|
-
self.assertEqual(mock_sleep.call_count, 1)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if __name__ == '__main__':
|
|
109
|
-
unittest.main()
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import unittest
|
|
3
|
-
from unittest.mock import patch, MagicMock
|
|
4
|
-
from typing import Any, Optional, Dict
|
|
5
|
-
import requests
|
|
6
|
-
|
|
7
|
-
from funcguard.tools import send_request
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class MockResponse:
|
|
11
|
-
"""模拟requests响应"""
|
|
12
|
-
|
|
13
|
-
json_data: Dict[str, Any]
|
|
14
|
-
text: str
|
|
15
|
-
status_code: int
|
|
16
|
-
|
|
17
|
-
def __init__(self, json_data: Optional[Dict[str, Any]] = None, text: str = "", status_code: int = 200):
|
|
18
|
-
self.json_data = json_data or {}
|
|
19
|
-
self.text = text
|
|
20
|
-
self.status_code = status_code
|
|
21
|
-
|
|
22
|
-
def json(self) -> Dict[str, Any]:
|
|
23
|
-
return self.json_data
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class TestSendRequest(unittest.TestCase):
|
|
27
|
-
"""测试send_request函数"""
|
|
28
|
-
|
|
29
|
-
@patch('requests.request')
|
|
30
|
-
def test_get_request_json_response(self, mock_request):
|
|
31
|
-
"""测试GET请求返回JSON"""
|
|
32
|
-
mock_response = MockResponse(json_data={"key": "value"})
|
|
33
|
-
mock_request.return_value = mock_response
|
|
34
|
-
|
|
35
|
-
result = send_request(
|
|
36
|
-
method="GET",
|
|
37
|
-
url="https://api.example.com/data",
|
|
38
|
-
headers={"Content-Type": "application/json"}
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
self.assertEqual(result, {"key": "value"})
|
|
42
|
-
mock_request.assert_called_once()
|
|
43
|
-
|
|
44
|
-
@patch('requests.request')
|
|
45
|
-
def test_post_request_with_data(self, mock_request):
|
|
46
|
-
"""测试POST请求带数据"""
|
|
47
|
-
mock_response = MockResponse(json_data={"status": "created"})
|
|
48
|
-
mock_request.return_value = mock_response
|
|
49
|
-
|
|
50
|
-
result = send_request(
|
|
51
|
-
method="POST",
|
|
52
|
-
url="https://api.example.com/users",
|
|
53
|
-
headers={"Content-Type": "application/json"},
|
|
54
|
-
data={"name": "test", "email": "test@example.com"}
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
self.assertEqual(result, {"status": "created"})
|
|
58
|
-
mock_request.assert_called_once()
|
|
59
|
-
|
|
60
|
-
@patch('requests.request')
|
|
61
|
-
def test_return_response_object(self, mock_request):
|
|
62
|
-
"""测试返回response对象"""
|
|
63
|
-
mock_response = MockResponse(text="raw response")
|
|
64
|
-
mock_request.return_value = mock_response
|
|
65
|
-
|
|
66
|
-
result = send_request(
|
|
67
|
-
method="GET",
|
|
68
|
-
url="https://api.example.com/raw",
|
|
69
|
-
headers={},
|
|
70
|
-
return_type="response"
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
self.assertEqual(result, mock_response)
|
|
74
|
-
|
|
75
|
-
@patch('requests.request')
|
|
76
|
-
def test_return_text_response(self, mock_request):
|
|
77
|
-
"""测试返回文本响应"""
|
|
78
|
-
mock_response = MockResponse(text="plain text response")
|
|
79
|
-
mock_request.return_value = mock_response
|
|
80
|
-
|
|
81
|
-
result = send_request(
|
|
82
|
-
method="GET",
|
|
83
|
-
url="https://api.example.com/text",
|
|
84
|
-
headers={},
|
|
85
|
-
return_type="text"
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
self.assertEqual(result, "plain text response")
|
|
89
|
-
|
|
90
|
-
@patch('requests.request')
|
|
91
|
-
def test_custom_timeout(self, mock_request):
|
|
92
|
-
"""测试自定义超时时间"""
|
|
93
|
-
mock_response = MockResponse()
|
|
94
|
-
mock_request.return_value = mock_response
|
|
95
|
-
|
|
96
|
-
send_request(
|
|
97
|
-
method="GET",
|
|
98
|
-
url="https://api.example.com/data",
|
|
99
|
-
headers={},
|
|
100
|
-
timeout=30
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
mock_request.assert_called_once()
|
|
104
|
-
|
|
105
|
-
@patch('funcguard.tools.retry_function')
|
|
106
|
-
@patch('requests.request')
|
|
107
|
-
def test_auto_retry_enabled(self, mock_request, mock_retry):
|
|
108
|
-
"""测试启用自动重试"""
|
|
109
|
-
# 设置retry_function返回一个包含正确json数据的MockResponse对象
|
|
110
|
-
mock_response = MockResponse(json_data={"success": True})
|
|
111
|
-
mock_retry.return_value = mock_response
|
|
112
|
-
|
|
113
|
-
result = send_request(
|
|
114
|
-
method="POST",
|
|
115
|
-
url="https://api.example.com/data",
|
|
116
|
-
headers={"Content-Type": "application/json"},
|
|
117
|
-
data={"key": "value"},
|
|
118
|
-
auto_retry={
|
|
119
|
-
"task_name": "API测试",
|
|
120
|
-
"max_retries": 3,
|
|
121
|
-
"execute_timeout": 60
|
|
122
|
-
}
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
# 验证结果 - 使用更安全的方式处理可能的类型差异
|
|
126
|
-
if isinstance(result, dict):
|
|
127
|
-
# 如果result是字典(JSON解析结果)
|
|
128
|
-
self.assertEqual(result, {"success": True})
|
|
129
|
-
elif hasattr(result, 'json_data'):
|
|
130
|
-
# 如果result是MockResponse对象,使用getattr避免类型检查错误
|
|
131
|
-
json_data = getattr(result, 'json_data')
|
|
132
|
-
self.assertEqual(json_data, {"success": True})
|
|
133
|
-
else:
|
|
134
|
-
# 其他情况,直接比较
|
|
135
|
-
self.assertEqual(result, {"success": True})
|
|
136
|
-
mock_retry.assert_called_once()
|
|
137
|
-
|
|
138
|
-
@patch('requests.request')
|
|
139
|
-
def test_none_response_raises_error(self, mock_request):
|
|
140
|
-
"""测试None响应抛出错误"""
|
|
141
|
-
mock_request.return_value = None
|
|
142
|
-
|
|
143
|
-
with self.assertRaises(ValueError) as context:
|
|
144
|
-
send_request("GET", "https://api.example.com/data", {})
|
|
145
|
-
|
|
146
|
-
self.assertEqual(str(context.exception), "请求返回的响应为None")
|
|
147
|
-
|
|
148
|
-
def test_invalid_return_type(self):
|
|
149
|
-
"""测试无效的返回类型"""
|
|
150
|
-
with patch('requests.request') as mock_request:
|
|
151
|
-
mock_response = MockResponse()
|
|
152
|
-
mock_request.return_value = mock_response
|
|
153
|
-
|
|
154
|
-
# 对于无效的return_type,函数会尝试访问response.text
|
|
155
|
-
result = send_request(
|
|
156
|
-
method="GET",
|
|
157
|
-
url="https://api.example.com/data",
|
|
158
|
-
headers={},
|
|
159
|
-
return_type="invalid"
|
|
160
|
-
)
|
|
161
|
-
self.assertEqual(result, "")
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if __name__ == '__main__':
|
|
165
|
-
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|