ezKit 1.12.0__py3-none-any.whl → 1.12.2__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.
- ezKit/bottle_extensions.py +8 -4
- ezKit/cipher.py +19 -16
- ezKit/database.py +15 -25
- ezKit/dockerhub.py +9 -3
- ezKit/http.py +9 -6
- ezKit/mongo.py +3 -2
- ezKit/qywx.py +34 -24
- ezKit/redis.py +2 -1
- ezKit/sendemail.py +12 -4
- ezKit/token.py +13 -13
- ezKit/utils.py +136 -294
- ezKit/xftp.py +39 -17
- ezKit/zabbix.py +140 -101
- {ezkit-1.12.0.dist-info → ezkit-1.12.2.dist-info}/METADATA +1 -1
- ezkit-1.12.2.dist-info/RECORD +22 -0
- {ezkit-1.12.0.dist-info → ezkit-1.12.2.dist-info}/WHEEL +1 -1
- ezkit-1.12.0.dist-info/RECORD +0 -22
- {ezkit-1.12.0.dist-info → ezkit-1.12.2.dist-info}/licenses/LICENSE +0 -0
- {ezkit-1.12.0.dist-info → ezkit-1.12.2.dist-info}/top_level.txt +0 -0
ezKit/utils.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
"""Utils"""
|
1
|
+
"""Utils Library"""
|
2
|
+
|
2
3
|
import csv
|
3
4
|
import hashlib
|
4
5
|
import importlib
|
@@ -72,46 +73,39 @@ def isTrue(target: object, typeClass: Any) -> bool:
|
|
72
73
|
# --------------------------------------------------------------------------------------------------
|
73
74
|
|
74
75
|
|
75
|
-
def check_arguments(data: list) -> bool:
|
76
|
-
|
76
|
+
# def check_arguments(data: list) -> bool:
|
77
|
+
# """检查函数参数"""
|
77
78
|
|
78
|
-
|
79
|
+
# # data 示例: [(name, str, "name"), (age, int, "age")]
|
79
80
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
81
|
+
# try:
|
82
|
+
# if not isTrue(data, list):
|
83
|
+
# logger.error(f"argument error: data {type(data)}")
|
84
|
+
# return False
|
85
|
+
# for arg, arg_type, info in data:
|
86
|
+
# if not isTrue(arg, arg_type):
|
87
|
+
# logger.error(f"argument error: {info} {type(arg)}")
|
88
|
+
# return False
|
89
|
+
# return True
|
90
|
+
# except Exception as e:
|
91
|
+
# logger.exception(e)
|
92
|
+
# return False
|
92
93
|
|
93
94
|
|
94
95
|
# --------------------------------------------------------------------------------------------------
|
95
96
|
|
96
97
|
|
97
|
-
def os_environ(
|
98
|
-
name: str,
|
99
|
-
value: Any = None
|
100
|
-
) -> Any:
|
98
|
+
def os_environ(name: str, value: Any = None) -> Any:
|
101
99
|
"""系统变量"""
|
102
100
|
|
103
101
|
# 伪全局变量
|
104
102
|
# Python 没有全局变量, 多个文件无法调用同一个变量.
|
105
103
|
# 为了解决这个问题, 将变量设置为系统变量, 从而实现多个文件调用同一个变量.
|
106
104
|
|
107
|
-
# 检查参数是否正确
|
108
|
-
if not check_arguments([(name, str, "name")]):
|
109
|
-
return None
|
110
|
-
|
111
105
|
try:
|
112
106
|
|
113
107
|
# 变量名添加一个前缀, 防止和系统中其它变量名冲突
|
114
|
-
_variable_name = f
|
108
|
+
_variable_name = f"PYTHON_VARIABLE_{name}"
|
115
109
|
|
116
110
|
# 如果 value 的值是 None, 则从系统环境获取变量数据
|
117
111
|
if value is None:
|
@@ -140,19 +134,12 @@ def os_environ(
|
|
140
134
|
# --------------------------------------------------------------------------------------------------
|
141
135
|
|
142
136
|
|
143
|
-
def mam_of_numbers(
|
144
|
-
numbers: list | tuple,
|
145
|
-
dest_type: str | None = None
|
146
|
-
) -> tuple | None:
|
137
|
+
def mam_of_numbers(numbers: list | tuple, dest_type: str | None = None) -> tuple | None:
|
147
138
|
"""返回一组数字中的 最大值(maximum), 平均值(average), 最小值(minimum)"""
|
148
139
|
|
149
140
|
# numbers 数字列表 (仅支持 list 和 tuple, 不支 set)
|
150
141
|
# dest_type 目标类型 (将数字列表中的数字转换成统一的类型)
|
151
142
|
|
152
|
-
# 检查参数是否正确
|
153
|
-
if not check_arguments([(numbers, (list, tuple), "numbers")]):
|
154
|
-
return None
|
155
|
-
|
156
143
|
if not any([isTrue(dest_type, str), dest_type is None]):
|
157
144
|
logger.error("argument error: dest_type")
|
158
145
|
return None
|
@@ -189,10 +176,7 @@ def mam_of_numbers(
|
|
189
176
|
# --------------------------------------------------------------------------------------------------
|
190
177
|
|
191
178
|
|
192
|
-
def step_number_for_split_equally(
|
193
|
-
integer: int,
|
194
|
-
split_equally_number: int
|
195
|
-
) -> int | None:
|
179
|
+
def step_number_for_split_equally(integer: int, split_equally_number: int) -> int | None:
|
196
180
|
"""
|
197
181
|
step number for split equally
|
198
182
|
|
@@ -222,14 +206,8 @@ def step_number_for_split_equally(
|
|
222
206
|
# --------------------------------------------------------------------------------------------------
|
223
207
|
|
224
208
|
|
225
|
-
def division(
|
226
|
-
dividend: int | float,
|
227
|
-
divisor: int | float
|
228
|
-
) -> float | None:
|
209
|
+
def division(dividend: int | float, divisor: int | float) -> float | None:
|
229
210
|
"""Division"""
|
230
|
-
# 检查参数是否正确
|
231
|
-
if not check_arguments([(dividend, (int, float), "dividend"), (divisor, (int, float), "divisor")]):
|
232
|
-
return None
|
233
211
|
try:
|
234
212
|
return dividend / divisor
|
235
213
|
except Exception as e:
|
@@ -245,14 +223,6 @@ def format_float(number: float | int, index: int = 2, limit: int = 3) -> float |
|
|
245
223
|
|
246
224
|
# 以两位和三位为例, 如果结果为假, 即: 0, 0.0, 0.00 等,保留小数点后的三位, 否则保留小数点后的两位.
|
247
225
|
|
248
|
-
# 检查参数
|
249
|
-
if not check_arguments([
|
250
|
-
(number, (float, int), "number"),
|
251
|
-
(index, int, "index"),
|
252
|
-
(limit, int, "limit")
|
253
|
-
]):
|
254
|
-
return False
|
255
|
-
|
256
226
|
try:
|
257
227
|
rounded_float = round(number, index)
|
258
228
|
return rounded_float if bool(rounded_float) else round(number, limit)
|
@@ -264,19 +234,12 @@ def format_float(number: float | int, index: int = 2, limit: int = 3) -> float |
|
|
264
234
|
# --------------------------------------------------------------------------------------------------
|
265
235
|
|
266
236
|
|
267
|
-
def check_file_type(
|
268
|
-
file_object: str,
|
269
|
-
file_type: str
|
270
|
-
) -> bool:
|
237
|
+
def check_file_type(file_object: str, file_type: str) -> bool:
|
271
238
|
"""检查文件类型"""
|
272
239
|
|
273
240
|
# file_object 文件对象
|
274
241
|
# file_type 文件类型
|
275
242
|
|
276
|
-
# 检查参数是否正确
|
277
|
-
if not check_arguments([(file_object, str, "file"), (file_type, str, "file_type")]):
|
278
|
-
return False
|
279
|
-
|
280
243
|
try:
|
281
244
|
|
282
245
|
_file_path = Path(file_object)
|
@@ -284,25 +247,27 @@ def check_file_type(
|
|
284
247
|
match True:
|
285
248
|
case True if _file_path.exists() is False:
|
286
249
|
result = False
|
287
|
-
case True if file_type ==
|
288
|
-
result = True
|
289
|
-
case True if file_type == 'block_device' and _file_path.is_block_device() is True:
|
250
|
+
case True if file_type == "absolute" and _file_path.is_absolute() is True:
|
290
251
|
result = True
|
291
|
-
case True if file_type ==
|
252
|
+
case True if file_type == "block_device" and _file_path.is_block_device() is True:
|
292
253
|
result = True
|
293
|
-
case True if file_type ==
|
254
|
+
case True if file_type == "dir" and _file_path.is_dir() is True:
|
294
255
|
result = True
|
295
|
-
case True if file_type ==
|
256
|
+
case True if file_type == "fifo" and _file_path.is_fifo() is True:
|
296
257
|
result = True
|
297
|
-
case True if file_type ==
|
258
|
+
case True if file_type == "file" and _file_path.is_file() is True:
|
298
259
|
result = True
|
299
|
-
case True if file_type ==
|
260
|
+
case True if file_type == "mount" and _file_path.is_mount() is True:
|
300
261
|
result = True
|
301
|
-
case True if
|
262
|
+
# case True if (
|
263
|
+
# file_type == "relative_to" and _file_path.is_relative_to() is True
|
264
|
+
# ):
|
265
|
+
# result = True
|
266
|
+
case True if file_type == "reserved" and _file_path.is_reserved() is True:
|
302
267
|
result = True
|
303
|
-
case True if file_type ==
|
268
|
+
case True if file_type == "socket" and _file_path.is_socket() is True:
|
304
269
|
result = True
|
305
|
-
case True if file_type ==
|
270
|
+
case True if file_type == "symlink" and _file_path.is_symlink() is True:
|
306
271
|
result = True
|
307
272
|
case _:
|
308
273
|
result = False
|
@@ -317,11 +282,7 @@ def check_file_type(
|
|
317
282
|
# --------------------------------------------------------------------------------------------------
|
318
283
|
|
319
284
|
|
320
|
-
def list_sort(
|
321
|
-
data: list,
|
322
|
-
deduplication: bool = False,
|
323
|
-
**kwargs
|
324
|
-
) -> list | None:
|
285
|
+
def list_sort(data: list, deduplication: bool = False, **kwargs) -> list | None:
|
325
286
|
"""list sort"""
|
326
287
|
# 列表排序, 示例: list_sort(['1.2.3.4', '2.3.4.5'], key=inet_aton)
|
327
288
|
# 参考文档:
|
@@ -345,11 +306,7 @@ def list_sort(
|
|
345
306
|
return None
|
346
307
|
|
347
308
|
|
348
|
-
def list_dict_sorted_by_key(
|
349
|
-
data: list | tuple,
|
350
|
-
key: str,
|
351
|
-
**kwargs
|
352
|
-
) -> list | None:
|
309
|
+
def list_dict_sorted_by_key(data: list | tuple, key: str, **kwargs) -> list | None:
|
353
310
|
"""list dict sorted by key"""
|
354
311
|
# 列表字典排序
|
355
312
|
# 参考文档: https://stackoverflow.com/a/73050
|
@@ -360,11 +317,8 @@ def list_dict_sorted_by_key(
|
|
360
317
|
logger.exception(e)
|
361
318
|
return None
|
362
319
|
|
363
|
-
|
364
|
-
|
365
|
-
number: int,
|
366
|
-
equally: bool = False
|
367
|
-
) -> list | None:
|
320
|
+
|
321
|
+
def list_split(data: list, number: int, equally: bool = False) -> list | None:
|
368
322
|
"""列表分割"""
|
369
323
|
|
370
324
|
# 列表分割
|
@@ -389,10 +343,6 @@ def list_split(
|
|
389
343
|
# list_split(data, 2, True) -> [[1, 2, 3, 4], [5, 6, 7, 8]] 将 data 平均分成 2个子list
|
390
344
|
# list_split(data, 3, True) -> [[1, 2, 3], [4, 5, 6], [7, 8]] 将 data 平均分成 3个子list
|
391
345
|
|
392
|
-
# 检查参数是否正确
|
393
|
-
if not check_arguments([(data, list, "data"), (number, int, "number")]):
|
394
|
-
return None
|
395
|
-
|
396
346
|
try:
|
397
347
|
|
398
348
|
# 要将列表平均分成 n 个子列表
|
@@ -410,17 +360,9 @@ def list_split(
|
|
410
360
|
return None
|
411
361
|
|
412
362
|
|
413
|
-
def list_print_by_step(
|
414
|
-
data: list,
|
415
|
-
step: int,
|
416
|
-
separator: str = " "
|
417
|
-
) -> bool:
|
363
|
+
def list_print_by_step(data: list, step: int, separator: str = " ") -> bool:
|
418
364
|
"""根据 步长 和 分隔符 有规律的打印列表中的数据"""
|
419
365
|
|
420
|
-
# 检查参数是否正确
|
421
|
-
if not check_arguments([(data, list, "data"), (step, int, "step"), (separator, str, "separator")]):
|
422
|
-
return False
|
423
|
-
|
424
366
|
try:
|
425
367
|
|
426
368
|
# 打印
|
@@ -433,7 +375,7 @@ def list_print_by_step(
|
|
433
375
|
|
434
376
|
# 每行最后一个 或者 所有数据最后一个, 不打印分隔符
|
435
377
|
if ((i % step) == (step - 1)) or ((i + 1) == len(data)):
|
436
|
-
print(v, end=
|
378
|
+
print(v, end="")
|
437
379
|
else:
|
438
380
|
print(v, end=separator)
|
439
381
|
|
@@ -446,10 +388,7 @@ def list_print_by_step(
|
|
446
388
|
return False
|
447
389
|
|
448
390
|
|
449
|
-
def list_remove_list(
|
450
|
-
original: list,
|
451
|
-
remove: list
|
452
|
-
) -> list | None:
|
391
|
+
def list_remove_list(original: list, remove: list) -> list | None:
|
453
392
|
"""List remove List"""
|
454
393
|
try:
|
455
394
|
_original = deepcopy(original)
|
@@ -460,9 +399,7 @@ def list_remove_list(
|
|
460
399
|
return None
|
461
400
|
|
462
401
|
|
463
|
-
def list_merge(
|
464
|
-
data: list
|
465
|
-
) -> list | None:
|
402
|
+
def list_merge(data: list) -> list | None:
|
466
403
|
"""list merge"""
|
467
404
|
# 合并 List 中的 List 为一个 List
|
468
405
|
try:
|
@@ -475,14 +412,10 @@ def list_merge(
|
|
475
412
|
return None
|
476
413
|
|
477
414
|
|
478
|
-
def list_to_file(
|
479
|
-
data: list,
|
480
|
-
file: str,
|
481
|
-
encoding: str = 'utf-8-sig'
|
482
|
-
) -> bool:
|
415
|
+
def list_to_file(data: list, file: str, encoding: str = "utf-8-sig") -> bool:
|
483
416
|
"""list to file"""
|
484
417
|
try:
|
485
|
-
with open(file,
|
418
|
+
with open(file, "w", encoding=encoding) as _file:
|
486
419
|
for line in data:
|
487
420
|
_file.write(f"{line}\n")
|
488
421
|
return True
|
@@ -490,16 +423,17 @@ def list_to_file(
|
|
490
423
|
logger.exception(e)
|
491
424
|
return False
|
492
425
|
|
426
|
+
|
493
427
|
def list_to_csvfile(
|
494
428
|
data: list,
|
495
429
|
file: str,
|
496
430
|
fields: list | None = None,
|
497
|
-
encoding: str =
|
498
|
-
**kwargs
|
431
|
+
encoding: str = "utf-8-sig",
|
432
|
+
**kwargs,
|
499
433
|
) -> bool:
|
500
434
|
"""list to csvfile"""
|
501
435
|
try:
|
502
|
-
with open(file,
|
436
|
+
with open(file, "w", encoding=encoding) as _file:
|
503
437
|
# CRLF replaced by LF
|
504
438
|
# https://stackoverflow.com/a/29976091
|
505
439
|
outcsv = csv.writer(_file, lineterminator=os.linesep, **kwargs)
|
@@ -511,12 +445,8 @@ def list_to_csvfile(
|
|
511
445
|
logger.exception(e)
|
512
446
|
return False
|
513
447
|
|
514
|
-
|
515
|
-
|
516
|
-
stop: int,
|
517
|
-
step: int,
|
518
|
-
width: int
|
519
|
-
) -> list | None:
|
448
|
+
|
449
|
+
def range_zfill(start: int, stop: int, step: int, width: int) -> list | None:
|
520
450
|
"""range zfill"""
|
521
451
|
# 生成长度相同的字符串的列表
|
522
452
|
# 示例: range_zfill(8, 13, 1, 2) => ['08', '09', '10', '11', '12']
|
@@ -534,10 +464,7 @@ def range_zfill(
|
|
534
464
|
# --------------------------------------------------------------------------------------------------
|
535
465
|
|
536
466
|
|
537
|
-
def dict_remove_key(
|
538
|
-
data: dict,
|
539
|
-
key: str
|
540
|
-
) -> dict | None:
|
467
|
+
def dict_remove_key(data: dict, key: str) -> dict | None:
|
541
468
|
"""dict remove key"""
|
542
469
|
try:
|
543
470
|
data_copy: dict = deepcopy(data)
|
@@ -547,15 +474,11 @@ def dict_remove_key(
|
|
547
474
|
logger.exception(e)
|
548
475
|
return None
|
549
476
|
|
550
|
-
|
551
|
-
|
552
|
-
file: str,
|
553
|
-
encoding: str = 'utf-8-sig',
|
554
|
-
**kwargs
|
555
|
-
) -> bool:
|
477
|
+
|
478
|
+
def dict_to_file(data: dict, file: str, encoding: str = "utf-8-sig", **kwargs) -> bool:
|
556
479
|
"""dict to file"""
|
557
480
|
try:
|
558
|
-
with open(file,
|
481
|
+
with open(file, "w", encoding=encoding) as _file:
|
559
482
|
json.dump(obj=data, fp=_file, indent=4, sort_keys=True, **kwargs)
|
560
483
|
return True
|
561
484
|
except Exception as e:
|
@@ -563,11 +486,7 @@ def dict_to_file(
|
|
563
486
|
return False
|
564
487
|
|
565
488
|
|
566
|
-
def dict_nested_update(
|
567
|
-
data: dict,
|
568
|
-
key: str,
|
569
|
-
value: Any
|
570
|
-
) -> bool:
|
489
|
+
def dict_nested_update(data: dict, key: str, value: Any) -> bool:
|
571
490
|
"""dict nested update"""
|
572
491
|
# dictionary nested update
|
573
492
|
# https://stackoverflow.com/a/58885744
|
@@ -602,10 +521,7 @@ def dict_nested_update(
|
|
602
521
|
# --------------------------------------------------------------------------------------------------
|
603
522
|
|
604
523
|
|
605
|
-
def filename(
|
606
|
-
file: str,
|
607
|
-
split: str = '.'
|
608
|
-
) -> str | None:
|
524
|
+
def filename(file: str, split: str = ".") -> str | None:
|
609
525
|
"""filename"""
|
610
526
|
# 获取文件名称
|
611
527
|
# https://stackoverflow.com/questions/678236/how-do-i-get-the-filename-without-the-extension-from-a-path-in-python
|
@@ -619,10 +535,7 @@ def filename(
|
|
619
535
|
return None
|
620
536
|
|
621
537
|
|
622
|
-
def filehash(
|
623
|
-
file: str,
|
624
|
-
sha: str = 'md5'
|
625
|
-
) -> str | None:
|
538
|
+
def filehash(file: str, sha: str = "md5") -> str | None:
|
626
539
|
"""filehash"""
|
627
540
|
# 获取文件Hash
|
628
541
|
# 参考文档:
|
@@ -631,23 +544,23 @@ def filehash(
|
|
631
544
|
try:
|
632
545
|
with open(file, "rb") as _file:
|
633
546
|
match True:
|
634
|
-
case True if sha ==
|
547
|
+
case True if sha == "sha1":
|
635
548
|
file_hash = hashlib.sha1()
|
636
|
-
case True if sha ==
|
549
|
+
case True if sha == "sha224":
|
637
550
|
file_hash = hashlib.sha224()
|
638
|
-
case True if sha ==
|
551
|
+
case True if sha == "sha256":
|
639
552
|
file_hash = hashlib.sha256()
|
640
|
-
case True if sha ==
|
553
|
+
case True if sha == "sha384":
|
641
554
|
file_hash = hashlib.sha384()
|
642
|
-
case True if sha ==
|
555
|
+
case True if sha == "sha512":
|
643
556
|
file_hash = hashlib.sha512()
|
644
|
-
case True if sha ==
|
557
|
+
case True if sha == "sha3_224":
|
645
558
|
file_hash = hashlib.sha3_224()
|
646
|
-
case True if sha ==
|
559
|
+
case True if sha == "sha3_256":
|
647
560
|
file_hash = hashlib.sha3_256()
|
648
|
-
case True if sha ==
|
561
|
+
case True if sha == "sha3_384":
|
649
562
|
file_hash = hashlib.sha3_384()
|
650
|
-
case True if sha ==
|
563
|
+
case True if sha == "sha3_512":
|
651
564
|
file_hash = hashlib.sha3_512()
|
652
565
|
# case True if sha == 'shake_128':
|
653
566
|
# file_hash = hashlib.shake_128()
|
@@ -666,9 +579,7 @@ def filehash(
|
|
666
579
|
return None
|
667
580
|
|
668
581
|
|
669
|
-
def filesize(
|
670
|
-
file: str
|
671
|
-
) -> int | None:
|
582
|
+
def filesize(file: str) -> int | None:
|
672
583
|
"""filesize"""
|
673
584
|
# 获取文件大小
|
674
585
|
try:
|
@@ -701,10 +612,7 @@ def filesize(
|
|
701
612
|
# return None
|
702
613
|
|
703
614
|
|
704
|
-
def realpath(
|
705
|
-
path: str,
|
706
|
-
**kwargs
|
707
|
-
) -> str | None:
|
615
|
+
def realpath(path: str, **kwargs) -> str | None:
|
708
616
|
"""获取对象真实路径"""
|
709
617
|
try:
|
710
618
|
if not isTrue(path, str):
|
@@ -715,10 +623,7 @@ def realpath(
|
|
715
623
|
return None
|
716
624
|
|
717
625
|
|
718
|
-
def current_dir(
|
719
|
-
path: str,
|
720
|
-
**kwargs
|
721
|
-
) -> str | None:
|
626
|
+
def current_dir(path: str, **kwargs) -> str | None:
|
722
627
|
"""获取对象所在目录"""
|
723
628
|
try:
|
724
629
|
if not isTrue(path, str):
|
@@ -729,10 +634,7 @@ def current_dir(
|
|
729
634
|
return None
|
730
635
|
|
731
636
|
|
732
|
-
def parent_dir(
|
733
|
-
path: str,
|
734
|
-
**kwargs
|
735
|
-
) -> str | None:
|
637
|
+
def parent_dir(path: str, **kwargs) -> str | None:
|
736
638
|
"""获取对象所在目录的父目录"""
|
737
639
|
try:
|
738
640
|
if not isTrue(path, str):
|
@@ -759,7 +661,7 @@ def retry(func: Callable, times: int = 3, **kwargs) -> Any:
|
|
759
661
|
except Exception as e:
|
760
662
|
logger.exception(e)
|
761
663
|
if attempt < (times - 1):
|
762
|
-
logger.info(
|
664
|
+
logger.info("retrying ...")
|
763
665
|
else:
|
764
666
|
logger.error("all retries failed")
|
765
667
|
return False
|
@@ -811,9 +713,7 @@ def retry(func: Callable, times: int = 3, **kwargs) -> Any:
|
|
811
713
|
# datetime.fromisoformat(time.strftime(format, time.gmtime(dt)))
|
812
714
|
|
813
715
|
|
814
|
-
def date_to_datetime(
|
815
|
-
date_object: datetime
|
816
|
-
) -> datetime | None:
|
716
|
+
def date_to_datetime(date_object: datetime) -> datetime | None:
|
817
717
|
"""'日期'转换为'日期时间'"""
|
818
718
|
# https://stackoverflow.com/a/1937636
|
819
719
|
try:
|
@@ -823,9 +723,7 @@ def date_to_datetime(
|
|
823
723
|
return None
|
824
724
|
|
825
725
|
|
826
|
-
def datetime_to_date(
|
827
|
-
datetime_instance: datetime
|
828
|
-
) -> date | None:
|
726
|
+
def datetime_to_date(datetime_instance: datetime) -> date | None:
|
829
727
|
"""'日期时间'转换为'日期'"""
|
830
728
|
# https://stackoverflow.com/a/3743240
|
831
729
|
try:
|
@@ -852,10 +750,7 @@ def datetime_now(**kwargs) -> datetime | None:
|
|
852
750
|
return None
|
853
751
|
|
854
752
|
|
855
|
-
def datetime_offset(
|
856
|
-
datetime_instance: datetime | None = None,
|
857
|
-
**kwargs
|
858
|
-
) -> datetime | None:
|
753
|
+
def datetime_offset(datetime_instance: datetime | None = None, **kwargs) -> datetime | None:
|
859
754
|
"""
|
860
755
|
获取 '向前或向后特定日期时间' 的日期和时间
|
861
756
|
|
@@ -876,10 +771,7 @@ def datetime_offset(
|
|
876
771
|
return None
|
877
772
|
|
878
773
|
|
879
|
-
def datetime_to_string(
|
880
|
-
datetime_instance: datetime,
|
881
|
-
string_format: str = '%Y-%m-%d %H:%M:%S'
|
882
|
-
) -> str | None:
|
774
|
+
def datetime_to_string(datetime_instance: datetime, string_format: str = "%Y-%m-%d %H:%M:%S") -> str | None:
|
883
775
|
"""'日期时间'转换为'字符串'"""
|
884
776
|
try:
|
885
777
|
if not isTrue(datetime_instance, datetime):
|
@@ -890,10 +782,7 @@ def datetime_to_string(
|
|
890
782
|
return None
|
891
783
|
|
892
784
|
|
893
|
-
def datetime_to_timestamp(
|
894
|
-
datetime_instance: datetime,
|
895
|
-
utc: bool = False
|
896
|
-
) -> int | None:
|
785
|
+
def datetime_to_timestamp(datetime_instance: datetime, utc: bool = False) -> int | None:
|
897
786
|
"""
|
898
787
|
Datatime 转换为 Unix Timestamp
|
899
788
|
Local datetime 可以直接转换为 Unix Timestamp
|
@@ -908,10 +797,7 @@ def datetime_to_timestamp(
|
|
908
797
|
return None
|
909
798
|
|
910
799
|
|
911
|
-
def datetime_local_to_timezone(
|
912
|
-
datetime_instance: datetime,
|
913
|
-
tz: timezone = timezone.utc
|
914
|
-
) -> datetime | None:
|
800
|
+
def datetime_local_to_timezone(datetime_instance: datetime, tz: timezone = timezone.utc) -> datetime | None:
|
915
801
|
"""
|
916
802
|
Local datetime to TimeZone datetime(默认转换为 UTC datetime)
|
917
803
|
replace(tzinfo=None) 移除结尾的时区信息
|
@@ -927,7 +813,7 @@ def datetime_local_to_timezone(
|
|
927
813
|
|
928
814
|
def datetime_utc_to_timezone(
|
929
815
|
datetime_instance: datetime,
|
930
|
-
tz: Any = datetime.now(timezone.utc).astimezone().tzinfo
|
816
|
+
tz: Any = datetime.now(timezone.utc).astimezone().tzinfo,
|
931
817
|
) -> datetime | None:
|
932
818
|
"""
|
933
819
|
UTC datetime to TimeZone datetime(默认转换为 Local datetime)
|
@@ -943,10 +829,7 @@ def datetime_utc_to_timezone(
|
|
943
829
|
return None
|
944
830
|
|
945
831
|
|
946
|
-
def timestamp_to_datetime(
|
947
|
-
timestamp: int,
|
948
|
-
tz: timezone = timezone.utc
|
949
|
-
) -> datetime | None:
|
832
|
+
def timestamp_to_datetime(timestamp: int, tz: timezone = timezone.utc) -> datetime | None:
|
950
833
|
"""Unix Timestamp 转换为 Datatime"""
|
951
834
|
try:
|
952
835
|
if not isTrue(timestamp, int):
|
@@ -957,10 +840,7 @@ def timestamp_to_datetime(
|
|
957
840
|
return None
|
958
841
|
|
959
842
|
|
960
|
-
def datetime_string_to_datetime(
|
961
|
-
datetime_string: str,
|
962
|
-
datetime_format: str = '%Y-%m-%d %H:%M:%S'
|
963
|
-
) -> datetime | None:
|
843
|
+
def datetime_string_to_datetime(datetime_string: str, datetime_format: str = "%Y-%m-%d %H:%M:%S") -> datetime | None:
|
964
844
|
"""datetime string to datetime"""
|
965
845
|
try:
|
966
846
|
if not isTrue(datetime_string, str):
|
@@ -971,10 +851,7 @@ def datetime_string_to_datetime(
|
|
971
851
|
return None
|
972
852
|
|
973
853
|
|
974
|
-
def datetime_string_to_timestamp(
|
975
|
-
datetime_string: str,
|
976
|
-
datetime_format: str = '%Y-%m-%d %H:%M:%S'
|
977
|
-
) -> int | None:
|
854
|
+
def datetime_string_to_timestamp(datetime_string: str, datetime_format: str = "%Y-%m-%d %H:%M:%S") -> int | None:
|
978
855
|
"""datetime string to timestamp"""
|
979
856
|
try:
|
980
857
|
if not isTrue(datetime_string, str):
|
@@ -985,18 +862,16 @@ def datetime_string_to_timestamp(
|
|
985
862
|
return None
|
986
863
|
|
987
864
|
|
988
|
-
def datetime_object(
|
989
|
-
date_time: datetime
|
990
|
-
) -> dict | None:
|
865
|
+
def datetime_object(date_time: datetime) -> dict | None:
|
991
866
|
"""datetime object"""
|
992
867
|
try:
|
993
868
|
return {
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
869
|
+
"date": date_time.strftime("%Y-%m-%d"),
|
870
|
+
"time": date_time.strftime("%H:%M:%S"),
|
871
|
+
"datetime_now": date_time.strftime("%Y-%m-%d %H:%M:%S"),
|
872
|
+
"datetime_minute": date_time.strftime("%Y-%m-%d %H:%M:00"),
|
873
|
+
"datetime_hour": date_time.strftime("%Y-%m-%d %H:00:00"),
|
874
|
+
"datetime_zero": date_time.strftime("%Y-%m-%d 00:00:00"),
|
1000
875
|
}
|
1001
876
|
except Exception as e:
|
1002
877
|
logger.exception(e)
|
@@ -1011,7 +886,7 @@ def shell(
|
|
1011
886
|
isfile: bool = False,
|
1012
887
|
sh_shell: str = "/bin/bash",
|
1013
888
|
sh_option: str | None = None,
|
1014
|
-
**kwargs
|
889
|
+
**kwargs,
|
1015
890
|
) -> subprocess.CompletedProcess | None:
|
1016
891
|
"""执行 Shell 命令 或 脚本"""
|
1017
892
|
|
@@ -1053,12 +928,10 @@ def shell(
|
|
1053
928
|
# --------------------------------------------------------------------------------------------------
|
1054
929
|
|
1055
930
|
|
1056
|
-
def json_file_parser(
|
1057
|
-
file: str
|
1058
|
-
) -> dict | None:
|
931
|
+
def json_file_parser(file: str) -> dict | None:
|
1059
932
|
"""JSON File Parser"""
|
1060
933
|
try:
|
1061
|
-
if not check_file_type(file,
|
934
|
+
if not check_file_type(file, "file"):
|
1062
935
|
logger.error(f"no such file: {file}")
|
1063
936
|
return None
|
1064
937
|
with open(file, encoding="utf-8") as json_raw:
|
@@ -1068,6 +941,7 @@ def json_file_parser(
|
|
1068
941
|
logger.exception(e)
|
1069
942
|
return None
|
1070
943
|
|
944
|
+
|
1071
945
|
# json_raw = '''
|
1072
946
|
# {
|
1073
947
|
# "markdown.preview.fontSize": 14,
|
@@ -1087,10 +961,7 @@ def json_file_parser(
|
|
1087
961
|
# }
|
1088
962
|
|
1089
963
|
|
1090
|
-
def json_sort(
|
1091
|
-
string: str,
|
1092
|
-
**kwargs
|
1093
|
-
) -> str | None:
|
964
|
+
def json_sort(string: str, **kwargs) -> str | None:
|
1094
965
|
"""JSON Sort"""
|
1095
966
|
try:
|
1096
967
|
if not isTrue(string, str):
|
@@ -1104,15 +975,13 @@ def json_sort(
|
|
1104
975
|
# --------------------------------------------------------------------------------------------------
|
1105
976
|
|
1106
977
|
|
1107
|
-
def delete_files(
|
1108
|
-
files: str | list
|
1109
|
-
) -> bool:
|
978
|
+
def delete_files(files: str | list) -> bool:
|
1110
979
|
"""删除文件"""
|
1111
980
|
try:
|
1112
981
|
|
1113
|
-
if isinstance(files, str) and check_file_type(files,
|
982
|
+
if isinstance(files, str) and check_file_type(files, "file"):
|
1114
983
|
os.remove(files)
|
1115
|
-
logger.success(f
|
984
|
+
logger.success(f"deleted file: {files}")
|
1116
985
|
return True
|
1117
986
|
|
1118
987
|
if not isTrue(files, list):
|
@@ -1120,14 +989,14 @@ def delete_files(
|
|
1120
989
|
|
1121
990
|
for _file in files:
|
1122
991
|
|
1123
|
-
if isinstance(_file, str) and check_file_type(_file,
|
992
|
+
if isinstance(_file, str) and check_file_type(_file, "file"):
|
1124
993
|
try:
|
1125
994
|
os.remove(_file)
|
1126
|
-
logger.success(f
|
995
|
+
logger.success(f"deleted file: {_file}")
|
1127
996
|
except Exception as e:
|
1128
|
-
logger.error(f
|
997
|
+
logger.error(f"error file: {_file} {e}")
|
1129
998
|
else:
|
1130
|
-
logger.error(f
|
999
|
+
logger.error(f"error file: {_file}")
|
1131
1000
|
|
1132
1001
|
return True
|
1133
1002
|
|
@@ -1164,25 +1033,25 @@ def delete_directory(
|
|
1164
1033
|
"""
|
1165
1034
|
try:
|
1166
1035
|
|
1167
|
-
if isinstance(directory, str) and check_file_type(directory,
|
1036
|
+
if isinstance(directory, str) and check_file_type(directory, "dir"):
|
1168
1037
|
rmtree(directory)
|
1169
|
-
logger.success(f
|
1038
|
+
logger.success(f"deleted directory: {directory}")
|
1170
1039
|
return True
|
1171
1040
|
|
1172
1041
|
if not isTrue(directory, list):
|
1173
|
-
logger.error(f
|
1042
|
+
logger.error(f"error directory: {directory}")
|
1174
1043
|
return False
|
1175
1044
|
|
1176
1045
|
for _dir in directory:
|
1177
1046
|
|
1178
|
-
if isTrue(_dir, str) and check_file_type(_dir,
|
1047
|
+
if isTrue(_dir, str) and check_file_type(_dir, "dir"):
|
1179
1048
|
try:
|
1180
1049
|
rmtree(_dir)
|
1181
|
-
logger.success(f
|
1050
|
+
logger.success(f"deleted directory: {_dir}")
|
1182
1051
|
except Exception as e:
|
1183
|
-
logger.error(f
|
1052
|
+
logger.error(f"error directory: {_dir} {e}")
|
1184
1053
|
else:
|
1185
|
-
logger.error(f
|
1054
|
+
logger.error(f"error directory: {_dir}")
|
1186
1055
|
|
1187
1056
|
return True
|
1188
1057
|
|
@@ -1199,7 +1068,7 @@ def processor(
|
|
1199
1068
|
process_data: list,
|
1200
1069
|
process_num: int = 2,
|
1201
1070
|
thread: bool = False,
|
1202
|
-
**kwargs
|
1071
|
+
**kwargs,
|
1203
1072
|
) -> Any:
|
1204
1073
|
"""使用多线程或多进程对数据进行并行处理"""
|
1205
1074
|
|
@@ -1224,25 +1093,15 @@ def processor(
|
|
1224
1093
|
|
1225
1094
|
try:
|
1226
1095
|
|
1227
|
-
# 检查参数
|
1228
|
-
if not check_arguments([(process_data, list, "process_data")]):
|
1229
|
-
return False
|
1230
|
-
|
1231
1096
|
# 确保并行数不超过数据量
|
1232
1097
|
process_num = min(len(process_data), process_num)
|
1233
|
-
data_chunks = (
|
1234
|
-
list_split(process_data, process_num, equally=True)
|
1235
|
-
if process_num > 1
|
1236
|
-
else [process_data]
|
1237
|
-
)
|
1098
|
+
data_chunks = list_split(process_data, process_num, equally=True) if process_num > 1 else [process_data]
|
1238
1099
|
|
1239
1100
|
if not data_chunks:
|
1240
1101
|
logger.error("data chunks error")
|
1241
1102
|
return False
|
1242
1103
|
|
1243
|
-
logger.info(
|
1244
|
-
f"Starting {'multi-threading' if thread else 'multi-processing'} with {process_num} workers..."
|
1245
|
-
)
|
1104
|
+
logger.info(f"Starting {'multi-threading' if thread else 'multi-processing'} with {process_num} workers...")
|
1246
1105
|
|
1247
1106
|
# 执行多线程或多进程任务
|
1248
1107
|
pool = ThreadPoolExecutor if thread else ProcessPoolExecutor
|
@@ -1257,16 +1116,14 @@ def processor(
|
|
1257
1116
|
# --------------------------------------------------------------------------------------------------
|
1258
1117
|
|
1259
1118
|
|
1260
|
-
def create_empty_file(
|
1261
|
-
file: str | None = None
|
1262
|
-
) -> str | None:
|
1119
|
+
def create_empty_file(file: str | None = None) -> str | None:
|
1263
1120
|
"""create empty file"""
|
1264
1121
|
try:
|
1265
1122
|
if file is None:
|
1266
1123
|
# 当前时间戳(纳秒)
|
1267
1124
|
timestamp = time.time_ns()
|
1268
1125
|
# 空文件路径
|
1269
|
-
file = f
|
1126
|
+
file = f"/tmp/none_{timestamp}.txt"
|
1270
1127
|
# 创建一个空文件
|
1271
1128
|
# pylint: disable=R1732
|
1272
1129
|
open(file, "w", encoding="utf-8").close()
|
@@ -1285,14 +1142,12 @@ def uuid4_hex() -> str:
|
|
1285
1142
|
return uuid4().hex
|
1286
1143
|
|
1287
1144
|
|
1288
|
-
def increment_version(
|
1289
|
-
version: str
|
1290
|
-
) -> str | None:
|
1145
|
+
def increment_version(version: str) -> str | None:
|
1291
1146
|
"""版本号递增"""
|
1292
1147
|
try:
|
1293
|
-
version_numbers = version.split(
|
1148
|
+
version_numbers = version.split(".")
|
1294
1149
|
version_numbers[-1] = str(int(version_numbers[-1]) + 1)
|
1295
|
-
return
|
1150
|
+
return ".".join(version_numbers)
|
1296
1151
|
except Exception as e:
|
1297
1152
|
logger.exception(e)
|
1298
1153
|
return None
|
@@ -1301,9 +1156,7 @@ def increment_version(
|
|
1301
1156
|
# --------------------------------------------------------------------------------------------------
|
1302
1157
|
|
1303
1158
|
|
1304
|
-
def make_directory(
|
1305
|
-
directory: str
|
1306
|
-
) -> bool:
|
1159
|
+
def make_directory(directory: str) -> bool:
|
1307
1160
|
"""创建目录"""
|
1308
1161
|
try:
|
1309
1162
|
os.makedirs(directory)
|
@@ -1312,16 +1165,15 @@ def make_directory(
|
|
1312
1165
|
logger.exception(e)
|
1313
1166
|
return False
|
1314
1167
|
|
1315
|
-
|
1316
|
-
|
1317
|
-
) -> bool:
|
1168
|
+
|
1169
|
+
def change_directory(directory: str) -> bool:
|
1318
1170
|
"""改变目录"""
|
1319
1171
|
try:
|
1320
1172
|
|
1321
1173
|
if not isTrue(directory, str):
|
1322
1174
|
return False
|
1323
1175
|
|
1324
|
-
if not check_file_type(directory,
|
1176
|
+
if not check_file_type(directory, "dir"):
|
1325
1177
|
logger.error(f"no such directory: {directory}")
|
1326
1178
|
return False
|
1327
1179
|
|
@@ -1337,19 +1189,17 @@ def change_directory(
|
|
1337
1189
|
# --------------------------------------------------------------------------------------------------
|
1338
1190
|
|
1339
1191
|
|
1340
|
-
def load_toml_file(
|
1341
|
-
file: str
|
1342
|
-
) -> dict | None:
|
1192
|
+
def load_toml_file(file: str) -> dict | None:
|
1343
1193
|
"""Load TOML file"""
|
1344
|
-
info =
|
1194
|
+
info = "解析配置文件"
|
1345
1195
|
try:
|
1346
|
-
logger.info(f
|
1196
|
+
logger.info(f"{info}[执行]")
|
1347
1197
|
with open(file, "rb") as _file:
|
1348
1198
|
config = tomllib.load(_file)
|
1349
|
-
logger.success(f
|
1199
|
+
logger.success(f"{info}[成功]")
|
1350
1200
|
return config
|
1351
1201
|
except Exception as e:
|
1352
|
-
logger.error(f
|
1202
|
+
logger.error(f"{info}[失败]")
|
1353
1203
|
logger.exception(e)
|
1354
1204
|
return None
|
1355
1205
|
|
@@ -1359,12 +1209,12 @@ def git_clone(
|
|
1359
1209
|
local_repository: str,
|
1360
1210
|
timeout: int = 30,
|
1361
1211
|
delete: bool = False,
|
1362
|
-
log_prefix: str =
|
1212
|
+
log_prefix: str = "",
|
1363
1213
|
) -> bool:
|
1364
1214
|
"""GIT Clone"""
|
1365
1215
|
|
1366
1216
|
# 日志前缀
|
1367
|
-
log_prefix = f
|
1217
|
+
log_prefix = f"{log_prefix} [GitClone]"
|
1368
1218
|
|
1369
1219
|
try:
|
1370
1220
|
|
@@ -1385,10 +1235,10 @@ def git_clone(
|
|
1385
1235
|
|
1386
1236
|
# 克隆仓库
|
1387
1237
|
result = shell(
|
1388
|
-
command=f
|
1238
|
+
command=f"timeout -s 9 {timeout} git clone {git_repository} {local_repository}",
|
1389
1239
|
universal_newlines=True,
|
1390
1240
|
stdout=subprocess.PIPE,
|
1391
|
-
stderr=subprocess.STDOUT
|
1241
|
+
stderr=subprocess.STDOUT,
|
1392
1242
|
)
|
1393
1243
|
|
1394
1244
|
if result is None:
|
@@ -1399,26 +1249,24 @@ def git_clone(
|
|
1399
1249
|
|
1400
1250
|
if result_code != 0:
|
1401
1251
|
for i in result_info:
|
1402
|
-
logger.error(f
|
1252
|
+
logger.error(f"{log_prefix}{i}")
|
1403
1253
|
return False
|
1404
1254
|
|
1405
1255
|
return True
|
1406
1256
|
|
1407
1257
|
except Exception as e:
|
1408
|
-
logger.error(f
|
1258
|
+
logger.error(f"{log_prefix} [failed]")
|
1409
1259
|
logger.exception(e)
|
1410
1260
|
return False
|
1411
1261
|
|
1412
1262
|
|
1413
1263
|
def url_parse(
|
1414
|
-
url: str
|
1264
|
+
url: str,
|
1415
1265
|
# scheme: str = "http"
|
1416
1266
|
) -> ParseResult | None:
|
1417
1267
|
"""URL Parse"""
|
1418
1268
|
# none_result = ParseResult(scheme='', netloc='', path='', params='', query='', fragment='')
|
1419
1269
|
try:
|
1420
|
-
if not check_arguments([(url, str, "url_parse -> url")]):
|
1421
|
-
return None
|
1422
1270
|
# 如果没有 scheme 的话, 字符串是不解析的. 所以, 如果没有 scheme, 就添加一个 scheme, 默认添加 http
|
1423
1271
|
# if isTrue(url, str) and (url.find('://') == -1) and isTrue(scheme, str):
|
1424
1272
|
# url = f'{scheme}://{url}'
|
@@ -1430,6 +1278,7 @@ def url_parse(
|
|
1430
1278
|
logger.exception(e)
|
1431
1279
|
return None
|
1432
1280
|
|
1281
|
+
|
1433
1282
|
# def debug_log(
|
1434
1283
|
# log: None | str = None,
|
1435
1284
|
# exception: None | Exception = None,
|
@@ -1462,13 +1311,6 @@ def markdown_to_html(markdown_file: str, html_file: str, title: str) -> bool:
|
|
1462
1311
|
|
1463
1312
|
logger.info(f"{info} [开始]")
|
1464
1313
|
|
1465
|
-
if not check_arguments([
|
1466
|
-
(markdown_file, str, "markdown_to_html -> markdown_file"),
|
1467
|
-
(html_file, str, "markdown_to_html -> html_file"),
|
1468
|
-
(title, str, "markdown_to_html -> title"),
|
1469
|
-
]):
|
1470
|
-
return False
|
1471
|
-
|
1472
1314
|
# 读取 HTML模版 文件
|
1473
1315
|
logger.info(f"{info} [读取 HTML模版 文件]")
|
1474
1316
|
html_template_file = f"{current_dir(__file__)}/markdown_to_html.template"
|
@@ -1483,7 +1325,7 @@ def markdown_to_html(markdown_file: str, html_file: str, title: str) -> bool:
|
|
1483
1325
|
# 将 Markdown 转换为 HTML
|
1484
1326
|
logger.info(f"{info} [将 Markdown 转换为 HTML]")
|
1485
1327
|
# pylint: disable=E0606
|
1486
|
-
html_body = markdown.markdown(markdown_content, extensions=[
|
1328
|
+
html_body = markdown.markdown(markdown_content, extensions=["tables"]) # type: ignore
|
1487
1329
|
# pylint: enable=E0606
|
1488
1330
|
|
1489
1331
|
# 构造完整的 HTML
|
@@ -1493,7 +1335,7 @@ def markdown_to_html(markdown_file: str, html_file: str, title: str) -> bool:
|
|
1493
1335
|
|
1494
1336
|
# 保存为 HTML 文件
|
1495
1337
|
logger.info(f"{info} [保存为 HTML 文件: {html_file}]")
|
1496
|
-
with open(html_file, "w", encoding=
|
1338
|
+
with open(html_file, "w", encoding="utf-8") as _:
|
1497
1339
|
_.write(html_content)
|
1498
1340
|
|
1499
1341
|
logger.success(f"{info} [成功]")
|