tretool 0.2.1__py3-none-any.whl → 1.0.0__py3-none-any.whl
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.
- tretool/__init__.py +39 -12
- tretool/config.py +406 -170
- tretool/decoratorlib.py +423 -0
- tretool/encoding.py +404 -75
- tretool/httplib.py +730 -0
- tretool/jsonlib.py +619 -151
- tretool/logger.py +712 -0
- tretool/mathlib.py +0 -33
- tretool/path.py +19 -0
- tretool/platformlib.py +469 -314
- tretool/plugin.py +437 -237
- tretool/smartCache.py +569 -0
- tretool/tasklib.py +730 -0
- tretool/transform/docx.py +544 -0
- tretool/transform/pdf.py +273 -95
- tretool/ziplib.py +664 -0
- {tretool-0.2.1.dist-info → tretool-1.0.0.dist-info}/METADATA +11 -5
- tretool-1.0.0.dist-info/RECORD +24 -0
- tretool/markfunc.py +0 -152
- tretool/memorizeTools.py +0 -24
- tretool/writeLog.py +0 -69
- tretool-0.2.1.dist-info/RECORD +0 -20
- {tretool-0.2.1.dist-info → tretool-1.0.0.dist-info}/WHEEL +0 -0
- {tretool-0.2.1.dist-info → tretool-1.0.0.dist-info}/licenses/LICENSE +0 -0
- {tretool-0.2.1.dist-info → tretool-1.0.0.dist-info}/top_level.txt +0 -0
tretool/decoratorlib.py
ADDED
@@ -0,0 +1,423 @@
|
|
1
|
+
"""
|
2
|
+
装饰器工具集,包含函数调用信息记录、弃用标记、重试机制和频率限制等功能。
|
3
|
+
"""
|
4
|
+
|
5
|
+
import functools
|
6
|
+
import warnings
|
7
|
+
import inspect
|
8
|
+
import time
|
9
|
+
import logging
|
10
|
+
import sys
|
11
|
+
import threading
|
12
|
+
import asyncio
|
13
|
+
from typing import Optional, Callable, Any, TypeVar, Union, Tuple
|
14
|
+
from pathlib import Path
|
15
|
+
|
16
|
+
T = TypeVar('T')
|
17
|
+
FuncType = Callable[..., T]
|
18
|
+
F = TypeVar('F', bound=FuncType[Any])
|
19
|
+
|
20
|
+
|
21
|
+
def info(
|
22
|
+
func: Optional[F] = None,
|
23
|
+
*,
|
24
|
+
show_args: bool = False,
|
25
|
+
show_kwargs: bool = False,
|
26
|
+
show_return: bool = False,
|
27
|
+
show_time: bool = False,
|
28
|
+
log_file: Optional[Union[str, Path]] = None,
|
29
|
+
indent: int = 0,
|
30
|
+
log_level: int = logging.INFO,
|
31
|
+
) -> Union[F, Callable[[F], F]]:
|
32
|
+
"""
|
33
|
+
装饰器:输出函数的详细调用信息
|
34
|
+
|
35
|
+
参数:
|
36
|
+
func: 被装饰的函数(自动传入,无需手动指定)
|
37
|
+
show_args: 是否显示位置参数,默认False
|
38
|
+
show_kwargs: 是否显示关键字参数,默认False
|
39
|
+
show_return: 是否显示返回值,默认False
|
40
|
+
show_time: 是否显示执行时间,默认False
|
41
|
+
log_file: 日志文件路径,不指定则输出到stdout
|
42
|
+
indent: 缩进空格数,用于嵌套调用时格式化输出
|
43
|
+
log_level: 日志级别,默认为logging.INFO
|
44
|
+
|
45
|
+
返回:
|
46
|
+
包装后的函数,调用时会输出详细信息
|
47
|
+
"""
|
48
|
+
def setup_logger() -> logging.Logger:
|
49
|
+
"""配置并返回logger实例"""
|
50
|
+
logger = logging.getLogger(f"func_logger.{indent}")
|
51
|
+
if logger.handlers:
|
52
|
+
return logger
|
53
|
+
|
54
|
+
logger.setLevel(log_level)
|
55
|
+
formatter = logging.Formatter(
|
56
|
+
"%(asctime)s - %(levelname)s - %(message)s",
|
57
|
+
datefmt="%Y-%m-%d %H:%M:%S"
|
58
|
+
)
|
59
|
+
|
60
|
+
if log_file:
|
61
|
+
handler = logging.FileHandler(log_file, encoding='utf-8')
|
62
|
+
else:
|
63
|
+
handler = logging.StreamHandler(sys.stdout)
|
64
|
+
|
65
|
+
handler.setFormatter(formatter)
|
66
|
+
logger.addHandler(handler)
|
67
|
+
logger.propagate = False
|
68
|
+
return logger
|
69
|
+
|
70
|
+
def decorator(f: F) -> F:
|
71
|
+
@functools.wraps(f)
|
72
|
+
def sync_wrapper(*args: Any, **kwargs: Any) -> Any:
|
73
|
+
logger = setup_logger()
|
74
|
+
indent_str = ' ' * indent
|
75
|
+
sig = inspect.signature(f)
|
76
|
+
|
77
|
+
log_lines = [f"{indent_str}┌── 调用函数: {f.__name__}"]
|
78
|
+
|
79
|
+
if show_args and args:
|
80
|
+
bound_args = sig.bind(*args, **kwargs)
|
81
|
+
bound_args.apply_defaults()
|
82
|
+
arg_details = [
|
83
|
+
f"{name}={repr(value)}"
|
84
|
+
for name, value in bound_args.arguments.items()
|
85
|
+
if name in sig.parameters and
|
86
|
+
name not in kwargs and
|
87
|
+
(len(args) > list(sig.parameters.keys()).index(name))
|
88
|
+
]
|
89
|
+
if arg_details:
|
90
|
+
log_lines.append(f"{indent_str}├── 位置参数: {', '.join(arg_details)}")
|
91
|
+
|
92
|
+
if show_kwargs and kwargs:
|
93
|
+
kwargs_details = [f"{k}={repr(v)}" for k, v in kwargs.items()]
|
94
|
+
log_lines.append(f"{indent_str}├── 关键字参数: {', '.join(kwargs_details)}")
|
95
|
+
|
96
|
+
start_time = time.perf_counter()
|
97
|
+
result = f(*args, **kwargs)
|
98
|
+
elapsed = time.perf_counter() - start_time
|
99
|
+
|
100
|
+
if show_return:
|
101
|
+
log_lines.append(f"{indent_str}├── 返回值: {repr(result)}")
|
102
|
+
|
103
|
+
if show_time:
|
104
|
+
log_lines.append(f"{indent_str}└── 执行时间: {elapsed:.6f}秒")
|
105
|
+
else:
|
106
|
+
log_lines[-1] = log_lines[-1].replace('├──', '└──')
|
107
|
+
|
108
|
+
logger.log(log_level, "\n".join(log_lines))
|
109
|
+
return result
|
110
|
+
|
111
|
+
@functools.wraps(f)
|
112
|
+
async def async_wrapper(*args: Any, **kwargs: Any) -> Any:
|
113
|
+
logger = setup_logger()
|
114
|
+
indent_str = ' ' * indent
|
115
|
+
sig = inspect.signature(f)
|
116
|
+
|
117
|
+
log_lines = [f"{indent_str}┌── 调用异步函数: {f.__name__}"]
|
118
|
+
|
119
|
+
if show_args and args:
|
120
|
+
bound_args = sig.bind(*args, **kwargs)
|
121
|
+
bound_args.apply_defaults()
|
122
|
+
arg_details = [
|
123
|
+
f"{name}={repr(value)}"
|
124
|
+
for name, value in bound_args.arguments.items()
|
125
|
+
if name in sig.parameters and
|
126
|
+
name not in kwargs and
|
127
|
+
(len(args) > list(sig.parameters.keys()).index(name))
|
128
|
+
]
|
129
|
+
if arg_details:
|
130
|
+
log_lines.append(f"{indent_str}├── 位置参数: {', '.join(arg_details)}")
|
131
|
+
|
132
|
+
if show_kwargs and kwargs:
|
133
|
+
kwargs_details = [f"{k}={repr(v)}" for k, v in kwargs.items()]
|
134
|
+
log_lines.append(f"{indent_str}├── 关键字参数: {', '.join(kwargs_details)}")
|
135
|
+
|
136
|
+
start_time = time.perf_counter()
|
137
|
+
result = await f(*args, **kwargs)
|
138
|
+
elapsed = time.perf_counter() - start_time
|
139
|
+
|
140
|
+
if show_return:
|
141
|
+
log_lines.append(f"{indent_str}├── 返回值: {repr(result)}")
|
142
|
+
|
143
|
+
if show_time:
|
144
|
+
log_lines.append(f"{indent_str}└── 执行时间: {elapsed:.6f}秒")
|
145
|
+
else:
|
146
|
+
log_lines[-1] = log_lines[-1].replace('├──', '└──')
|
147
|
+
|
148
|
+
logger.log(log_level, "\n".join(log_lines))
|
149
|
+
return result
|
150
|
+
|
151
|
+
return async_wrapper if inspect.iscoroutinefunction(f) else sync_wrapper
|
152
|
+
|
153
|
+
if func is None:
|
154
|
+
return decorator
|
155
|
+
else:
|
156
|
+
return decorator(func)
|
157
|
+
|
158
|
+
|
159
|
+
def deprecated_func(
|
160
|
+
func: Optional[F] = None,
|
161
|
+
*,
|
162
|
+
reason: Optional[str] = None,
|
163
|
+
version: Optional[str] = None,
|
164
|
+
alternative: Optional[str] = None,
|
165
|
+
since: Optional[str] = None,
|
166
|
+
) -> Union[F, Callable[[F], F]]:
|
167
|
+
"""
|
168
|
+
装饰器:标记函数已弃用,并在调用时发出警告
|
169
|
+
|
170
|
+
参数:
|
171
|
+
func: 被装饰的函数(自动传入,无需手动指定)
|
172
|
+
reason: 弃用的原因说明
|
173
|
+
version: 计划移除的版本号
|
174
|
+
alternative: 推荐的替代函数或方法
|
175
|
+
since: 从哪个版本开始弃用
|
176
|
+
|
177
|
+
返回:
|
178
|
+
包装后的函数,调用时会发出弃用警告
|
179
|
+
"""
|
180
|
+
def decorator(f: F) -> F:
|
181
|
+
@functools.wraps(f)
|
182
|
+
def sync_wrapper(*args: Any, **kwargs: Any) -> Any:
|
183
|
+
message_parts = [f"函数 {f.__name__} 已被弃用"]
|
184
|
+
|
185
|
+
if since:
|
186
|
+
message_parts.append(f"(自版本 {since})")
|
187
|
+
|
188
|
+
if reason:
|
189
|
+
message_parts.append(f",原因: {reason}")
|
190
|
+
|
191
|
+
if version:
|
192
|
+
message_parts.append(f",将在 {version} 版本中移除")
|
193
|
+
|
194
|
+
if alternative:
|
195
|
+
message_parts.append(f",请使用 {alternative} 替代")
|
196
|
+
|
197
|
+
message = "".join(message_parts)
|
198
|
+
warnings.warn(message, category=DeprecationWarning, stacklevel=2)
|
199
|
+
return f(*args, **kwargs)
|
200
|
+
|
201
|
+
@functools.wraps(f)
|
202
|
+
async def async_wrapper(*args: Any, **kwargs: Any) -> Any:
|
203
|
+
message = (
|
204
|
+
f"异步函数 {f.__name__} 已被弃用"
|
205
|
+
+ (f"(自版本 {since})" if since else "")
|
206
|
+
+ (f",原因: {reason}" if reason else "")
|
207
|
+
+ (f",将在 {version} 版本中移除" if version else "")
|
208
|
+
+ (f",请使用 {alternative} 替代" if alternative else "")
|
209
|
+
)
|
210
|
+
warnings.warn(message, category=DeprecationWarning, stacklevel=2)
|
211
|
+
return await f(*args, **kwargs)
|
212
|
+
|
213
|
+
wrapper = async_wrapper if inspect.iscoroutinefunction(f) else sync_wrapper
|
214
|
+
|
215
|
+
wrapper._is_deprecated = True # type: ignore
|
216
|
+
wrapper._deprecation_info = { # type: ignore
|
217
|
+
'reason': reason,
|
218
|
+
'version': version,
|
219
|
+
'alternative': alternative,
|
220
|
+
'since': since,
|
221
|
+
}
|
222
|
+
|
223
|
+
return wrapper # type: ignore
|
224
|
+
|
225
|
+
if func is None:
|
226
|
+
return decorator
|
227
|
+
else:
|
228
|
+
return decorator(func)
|
229
|
+
|
230
|
+
|
231
|
+
def retry(
|
232
|
+
max_attempts: int = 3,
|
233
|
+
delay: float = 1.0,
|
234
|
+
backoff: float = 2.0,
|
235
|
+
exceptions: Union[Tuple[Exception], Exception] = Exception,
|
236
|
+
logger: Optional[logging.Logger] = None,
|
237
|
+
):
|
238
|
+
"""
|
239
|
+
装饰器:函数执行失败时自动重试
|
240
|
+
|
241
|
+
参数:
|
242
|
+
max_attempts: 最大尝试次数 (默认: 3)
|
243
|
+
delay: 初始延迟时间(秒) (默认: 1.0)
|
244
|
+
backoff: 延迟时间倍增系数 (默认: 2.0)
|
245
|
+
exceptions: 需要捕获的异常类型 (默认: Exception)
|
246
|
+
logger: 自定义logger (默认: 使用print)
|
247
|
+
|
248
|
+
返回:
|
249
|
+
包装后的函数
|
250
|
+
"""
|
251
|
+
def decorator(f: F) -> F:
|
252
|
+
@functools.wraps(f)
|
253
|
+
def sync_wrapper(*args, **kwargs):
|
254
|
+
current_delay = delay
|
255
|
+
last_exception = None
|
256
|
+
|
257
|
+
for attempt in range(1, max_attempts + 1):
|
258
|
+
try:
|
259
|
+
return f(*args, **kwargs)
|
260
|
+
except exceptions as e:
|
261
|
+
last_exception = e
|
262
|
+
if attempt == max_attempts:
|
263
|
+
break
|
264
|
+
|
265
|
+
msg = (f"函数 {f.__name__} 第 {attempt} 次失败,"
|
266
|
+
f"{current_delay:.1f}秒后重试... 错误: {str(e)}")
|
267
|
+
if logger:
|
268
|
+
logger.warning(msg)
|
269
|
+
else:
|
270
|
+
print(msg)
|
271
|
+
|
272
|
+
time.sleep(current_delay)
|
273
|
+
current_delay *= backoff
|
274
|
+
|
275
|
+
raise RuntimeError(f"函数 {f.__name__} 在 {max_attempts} 次尝试后仍失败") from last_exception
|
276
|
+
|
277
|
+
@functools.wraps(f)
|
278
|
+
async def async_wrapper(*args, **kwargs):
|
279
|
+
current_delay = delay
|
280
|
+
last_exception = None
|
281
|
+
|
282
|
+
for attempt in range(1, max_attempts + 1):
|
283
|
+
try:
|
284
|
+
return await f(*args, **kwargs)
|
285
|
+
except exceptions as e:
|
286
|
+
last_exception = e
|
287
|
+
if attempt == max_attempts:
|
288
|
+
break
|
289
|
+
|
290
|
+
msg = (f"异步函数 {f.__name__} 第 {attempt} 次失败,"
|
291
|
+
f"{current_delay:.1f}秒后重试... 错误: {str(e)}")
|
292
|
+
if logger:
|
293
|
+
logger.warning(msg)
|
294
|
+
else:
|
295
|
+
print(msg)
|
296
|
+
|
297
|
+
await asyncio.sleep(current_delay)
|
298
|
+
current_delay *= backoff
|
299
|
+
|
300
|
+
raise RuntimeError(f"异步函数 {f.__name__} 在 {max_attempts} 次尝试后仍失败") from last_exception
|
301
|
+
|
302
|
+
return async_wrapper if inspect.iscoroutinefunction(f) else sync_wrapper
|
303
|
+
return decorator
|
304
|
+
|
305
|
+
|
306
|
+
def rate_limited(
|
307
|
+
calls: int = 1,
|
308
|
+
period: float = 1.0,
|
309
|
+
raise_on_limit: bool = False
|
310
|
+
):
|
311
|
+
"""
|
312
|
+
装饰器:限制函数调用频率
|
313
|
+
|
314
|
+
参数:
|
315
|
+
calls: 时间段内允许的最大调用次数 (默认: 1)
|
316
|
+
period: 时间段的长度(秒) (默认: 1.0)
|
317
|
+
raise_on_limit: 超出限制时是否抛出异常 (默认: False, 会阻塞等待)
|
318
|
+
|
319
|
+
返回:
|
320
|
+
包装后的函数
|
321
|
+
"""
|
322
|
+
def decorator(f: F) -> F:
|
323
|
+
call_times = []
|
324
|
+
lock = threading.Lock()
|
325
|
+
|
326
|
+
@functools.wraps(f)
|
327
|
+
def sync_wrapper(*args, **kwargs):
|
328
|
+
nonlocal call_times
|
329
|
+
|
330
|
+
with lock:
|
331
|
+
now = time.time()
|
332
|
+
call_times = [t for t in call_times if t > now - period]
|
333
|
+
|
334
|
+
if len(call_times) >= calls:
|
335
|
+
if raise_on_limit:
|
336
|
+
raise RuntimeError(f"调用频率限制: {calls}次/{period}秒")
|
337
|
+
else:
|
338
|
+
oldest = call_times[0]
|
339
|
+
wait_time = max(0, oldest + period - now)
|
340
|
+
if wait_time > 0:
|
341
|
+
time.sleep(wait_time)
|
342
|
+
|
343
|
+
call_times.append(time.time())
|
344
|
+
return f(*args, **kwargs)
|
345
|
+
|
346
|
+
@functools.wraps(f)
|
347
|
+
async def async_wrapper(*args, **kwargs):
|
348
|
+
nonlocal call_times
|
349
|
+
|
350
|
+
with lock:
|
351
|
+
now = time.time()
|
352
|
+
call_times = [t for t in call_times if t > now - period]
|
353
|
+
|
354
|
+
if len(call_times) >= calls:
|
355
|
+
if raise_on_limit:
|
356
|
+
raise RuntimeError(f"调用频率限制: {calls}次/{period}秒")
|
357
|
+
else:
|
358
|
+
oldest = call_times[0]
|
359
|
+
wait_time = max(0, oldest + period - now)
|
360
|
+
if wait_time > 0:
|
361
|
+
await asyncio.sleep(wait_time)
|
362
|
+
|
363
|
+
call_times.append(time.time())
|
364
|
+
return await f(*args, **kwargs)
|
365
|
+
|
366
|
+
return async_wrapper if inspect.iscoroutinefunction(f) else sync_wrapper
|
367
|
+
return decorator
|
368
|
+
|
369
|
+
|
370
|
+
def timeout(
|
371
|
+
seconds: float,
|
372
|
+
timeout_handler: Optional[Callable[[], Any]] = None,
|
373
|
+
exception: type = TimeoutError
|
374
|
+
):
|
375
|
+
"""
|
376
|
+
装饰器:为函数添加执行超时限制
|
377
|
+
|
378
|
+
参数:
|
379
|
+
seconds: 超时时间(秒)
|
380
|
+
timeout_handler: 超时时的处理函数 (默认: 抛出异常)
|
381
|
+
exception: 超时时抛出的异常类型 (默认: TimeoutError)
|
382
|
+
|
383
|
+
返回:
|
384
|
+
包装后的函数
|
385
|
+
"""
|
386
|
+
def decorator(f: F) -> F:
|
387
|
+
@functools.wraps(f)
|
388
|
+
def sync_wrapper(*args, **kwargs):
|
389
|
+
result = None
|
390
|
+
timed_out = False
|
391
|
+
|
392
|
+
def worker():
|
393
|
+
nonlocal result
|
394
|
+
result = f(*args, **kwargs)
|
395
|
+
|
396
|
+
thread = threading.Thread(target=worker)
|
397
|
+
thread.start()
|
398
|
+
thread.join(timeout=seconds)
|
399
|
+
|
400
|
+
if thread.is_alive():
|
401
|
+
timed_out = True
|
402
|
+
thread.join(0.1)
|
403
|
+
|
404
|
+
if timed_out:
|
405
|
+
if timeout_handler is not None:
|
406
|
+
return timeout_handler()
|
407
|
+
raise exception(f"函数 {f.__name__} 执行超时 ({seconds}秒)")
|
408
|
+
return result
|
409
|
+
|
410
|
+
@functools.wraps(f)
|
411
|
+
async def async_wrapper(*args, **kwargs):
|
412
|
+
try:
|
413
|
+
return await asyncio.wait_for(
|
414
|
+
f(*args, **kwargs),
|
415
|
+
timeout=seconds
|
416
|
+
)
|
417
|
+
except asyncio.TimeoutError:
|
418
|
+
if timeout_handler is not None:
|
419
|
+
return timeout_handler()
|
420
|
+
raise exception(f"异步函数 {f.__name__} 执行超时 ({seconds}秒)")
|
421
|
+
|
422
|
+
return async_wrapper if inspect.iscoroutinefunction(f) else sync_wrapper
|
423
|
+
return decorator
|