ezKit 1.6.2__py3-none-any.whl → 1.7.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.
- ezKit/utils.py +454 -394
- {ezKit-1.6.2.dist-info → ezKit-1.7.0.dist-info}/METADATA +1 -1
- {ezKit-1.6.2.dist-info → ezKit-1.7.0.dist-info}/RECORD +6 -6
- {ezKit-1.6.2.dist-info → ezKit-1.7.0.dist-info}/WHEEL +1 -1
- {ezKit-1.6.2.dist-info → ezKit-1.7.0.dist-info}/LICENSE +0 -0
- {ezKit-1.6.2.dist-info → ezKit-1.7.0.dist-info}/top_level.txt +0 -0
ezKit/utils.py
CHANGED
@@ -1,17 +1,6 @@
|
|
1
1
|
"""
|
2
2
|
Python Utils
|
3
3
|
"""
|
4
|
-
"""
|
5
|
-
函数
|
6
|
-
|
7
|
-
- 明确 参数 和 返回值 的类型
|
8
|
-
- 必须有返回值
|
9
|
-
- 添加一个 debug 参数, 用于调试
|
10
|
-
- 如果 debug 为 True, 则输出相关信息, 否则一律不输出任何信息
|
11
|
-
- logger.exception(e) if v_true(debug, bool) else next
|
12
|
-
- 必须有说明
|
13
|
-
- 必须用 try ... except ... 包裹
|
14
|
-
"""
|
15
4
|
import csv
|
16
5
|
import datetime
|
17
6
|
import hashlib
|
@@ -52,51 +41,47 @@ def v_true(
|
|
52
41
|
"""
|
53
42
|
检查变量类型以及变量是否为True
|
54
43
|
"""
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
Dictionary dict {}
|
44
|
+
# 常见类型:
|
45
|
+
# Boolean bool False
|
46
|
+
# Numbers int/float 0/0.0
|
47
|
+
# String str ""
|
48
|
+
# List list/tuple/set []/()/{}
|
49
|
+
# Dictionary dict {}
|
50
|
+
# 函数使用 callable(func) 判断
|
63
51
|
|
64
|
-
函数使用 callable(func) 判断
|
65
|
-
"""
|
66
52
|
try:
|
53
|
+
|
67
54
|
if isinstance(v_instance, v_type):
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
55
|
+
|
56
|
+
if all(
|
57
|
+
true_list is not None,
|
58
|
+
false_list is None,
|
59
|
+
isinstance(true_list, (list, tuple, set, str))
|
73
60
|
):
|
74
|
-
return
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
61
|
+
return v_instance in true_list
|
62
|
+
|
63
|
+
if all(
|
64
|
+
true_list is None,
|
65
|
+
false_list is not None,
|
66
|
+
isinstance(false_list, (list, tuple, set, str))
|
80
67
|
):
|
81
|
-
return
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
isinstance(false_list, list) or
|
89
|
-
isinstance(false_list, tuple) or
|
90
|
-
isinstance(false_list, set) or
|
91
|
-
isinstance(false_list, str)
|
68
|
+
return v_instance not in false_list
|
69
|
+
|
70
|
+
if all(
|
71
|
+
true_list is not None,
|
72
|
+
false_list is not None,
|
73
|
+
isinstance(true_list, (list, tuple, set, str)),
|
74
|
+
isinstance(false_list, (list, tuple, set, str))
|
92
75
|
):
|
93
|
-
return
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
76
|
+
return (v_instance in true_list) and (v_instance not in false_list)
|
77
|
+
|
78
|
+
return v_instance not in [False, None, 0, 0.0, '', (), [], {*()}, {*[]}, {*{}}, {}]
|
79
|
+
|
80
|
+
return False
|
81
|
+
|
98
82
|
except Exception as e:
|
99
|
-
|
83
|
+
if v_true(debug, bool):
|
84
|
+
logger.exception(e)
|
100
85
|
return False
|
101
86
|
|
102
87
|
|
@@ -106,22 +91,25 @@ def os_environ(
|
|
106
91
|
debug: bool = False
|
107
92
|
) -> any:
|
108
93
|
"""伪全局变量"""
|
109
|
-
|
110
|
-
|
111
|
-
为了解决这个问题, 将变量设置为系统变量, 从而实现多个文件调用同一个变量
|
112
|
-
"""
|
94
|
+
# Python 没有全局变量, 多个文件无法调用同一个变量
|
95
|
+
# 为了解决这个问题, 将变量设置为系统变量, 从而实现多个文件调用同一个变量
|
113
96
|
try:
|
97
|
+
|
114
98
|
# 变量名添加一个前缀, 防止和系统中其它变量名冲突
|
115
99
|
variable_name = f'PYTHON_VARIABLE_{name}'
|
116
|
-
|
100
|
+
|
101
|
+
if value is None:
|
117
102
|
data = os.environ.get(variable_name)
|
118
103
|
return json.loads(data)
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
104
|
+
|
105
|
+
data = json.dumps(value)
|
106
|
+
os.environ[variable_name] = data
|
107
|
+
|
108
|
+
return value
|
109
|
+
|
123
110
|
except Exception as e:
|
124
|
-
|
111
|
+
if v_true(debug, bool):
|
112
|
+
logger.exception(e)
|
125
113
|
return None
|
126
114
|
|
127
115
|
|
@@ -133,11 +121,10 @@ def mam_of_numbers(
|
|
133
121
|
dest_type: str | None = None,
|
134
122
|
debug: bool = False
|
135
123
|
) -> tuple[int | float, int | float, int | float] | tuple[None, None, None]:
|
136
|
-
"""
|
137
|
-
返回一组数字中的 最大值(maximum), 平均值(average), 最小值(minimum)
|
138
|
-
numbers 数字列表 (仅支持 list 和 tuple, 不支 set)
|
139
|
-
dest_type 目标类型 (将数字列表中的数字转换成统一的类型)
|
140
|
-
"""
|
124
|
+
"""(maximum, average, minimum)"""
|
125
|
+
# 返回一组数字中的 最大值(maximum), 平均值(average), 最小值(minimum)
|
126
|
+
# numbers 数字列表 (仅支持 list 和 tuple, 不支 set)
|
127
|
+
# dest_type 目标类型 (将数字列表中的数字转换成统一的类型)
|
141
128
|
try:
|
142
129
|
_numbers = deepcopy(numbers)
|
143
130
|
match True:
|
@@ -150,7 +137,8 @@ def mam_of_numbers(
|
|
150
137
|
_num_min = min(_numbers)
|
151
138
|
return _num_max, _num_avg, _num_min
|
152
139
|
except Exception as e:
|
153
|
-
|
140
|
+
if v_true(debug, bool):
|
141
|
+
logger.exception(e)
|
154
142
|
return None, None, None
|
155
143
|
|
156
144
|
|
@@ -159,28 +147,26 @@ def step_number_for_split_equally(
|
|
159
147
|
split_equally_number: int,
|
160
148
|
debug: bool = False
|
161
149
|
) -> int | None:
|
162
|
-
"""
|
163
|
-
平分数字的步长
|
164
|
-
integer 数字
|
165
|
-
split_equally_number 平分 integer 的数字
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
分成 5 份 -> [[1, 2], [3, 4], [5, 6], [7, 8], [9]] -> 返回 2
|
176
|
-
"""
|
150
|
+
"""step number for split equally"""
|
151
|
+
# 平分数字的步长
|
152
|
+
# integer 数字
|
153
|
+
# split_equally_number 平分 integer 的数字
|
154
|
+
#
|
155
|
+
# 示例:
|
156
|
+
#
|
157
|
+
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
158
|
+
#
|
159
|
+
# 分成 2 份 -> [[1, 2, 3, 4, 5], [6, 7, 8, 9]] -> 返回 5
|
160
|
+
# 分成 3 份 -> [[1, 2, 3], [4, 5, 6], [7, 8, 9]] -> 返回 3
|
161
|
+
# 分成 4 份 -> [[1, 2, 3], [4, 5], [6, 7], [8, 9]] -> 返回 3
|
162
|
+
# 分成 5 份 -> [[1, 2], [3, 4], [5, 6], [7, 8], [9]] -> 返回 2
|
177
163
|
try:
|
178
164
|
if integer % split_equally_number == 0:
|
179
165
|
return int(integer / split_equally_number)
|
180
|
-
|
181
|
-
return int(integer / split_equally_number) + 1
|
166
|
+
return int(integer / split_equally_number) + 1
|
182
167
|
except Exception as e:
|
183
|
-
|
168
|
+
if v_true(debug, bool):
|
169
|
+
logger.exception(e)
|
184
170
|
return None
|
185
171
|
|
186
172
|
|
@@ -189,13 +175,12 @@ def division(
|
|
189
175
|
divisor: int | float,
|
190
176
|
debug: bool = False
|
191
177
|
) -> float | None:
|
192
|
-
"""
|
193
|
-
除法
|
194
|
-
"""
|
178
|
+
"""Division"""
|
195
179
|
try:
|
196
180
|
return dividend / divisor
|
197
181
|
except Exception as e:
|
198
|
-
|
182
|
+
if v_true(debug, bool):
|
183
|
+
logger.exception(e)
|
199
184
|
return None
|
200
185
|
|
201
186
|
|
@@ -203,13 +188,13 @@ def divisor_1000(
|
|
203
188
|
dividend: int | float,
|
204
189
|
debug: bool = False
|
205
190
|
) -> float | None:
|
206
|
-
"""
|
207
|
-
除法, 除以 1000
|
208
|
-
"""
|
191
|
+
"""Division (divisor: 1000)"""
|
192
|
+
# 除法, 除以 1000
|
209
193
|
try:
|
210
194
|
return dividend / 1000
|
211
195
|
except Exception as e:
|
212
|
-
|
196
|
+
if v_true(debug, bool):
|
197
|
+
logger.exception(e)
|
213
198
|
return None
|
214
199
|
|
215
200
|
|
@@ -217,13 +202,13 @@ def divisor_1024(
|
|
217
202
|
dividend: int | float,
|
218
203
|
debug: bool = False
|
219
204
|
) -> float | None:
|
220
|
-
"""
|
221
|
-
除法, 除以 1024
|
222
|
-
"""
|
205
|
+
"""Division (divisor: 1024)"""
|
206
|
+
# 除法, 除以 1024
|
223
207
|
try:
|
224
208
|
return dividend / 1024
|
225
209
|
except Exception as e:
|
226
|
-
|
210
|
+
if v_true(debug, bool):
|
211
|
+
logger.exception(e)
|
227
212
|
return None
|
228
213
|
|
229
214
|
|
@@ -231,13 +216,12 @@ def divisor_square_1000(
|
|
231
216
|
dividend: int | float,
|
232
217
|
debug: bool = False
|
233
218
|
) -> float | None:
|
234
|
-
"""
|
235
|
-
除法, 除以 1000的次方
|
236
|
-
"""
|
219
|
+
"""Division (divisor: 1000*1000)"""
|
237
220
|
try:
|
238
221
|
return dividend / (1000 * 1000)
|
239
222
|
except Exception as e:
|
240
|
-
|
223
|
+
if v_true(debug, bool):
|
224
|
+
logger.exception(e)
|
241
225
|
return None
|
242
226
|
|
243
227
|
|
@@ -245,13 +229,12 @@ def divisor_square_1024(
|
|
245
229
|
dividend: int | float,
|
246
230
|
debug: bool = False
|
247
231
|
) -> float | None:
|
248
|
-
"""
|
249
|
-
除法, 除以 1024的次方
|
250
|
-
"""
|
232
|
+
"""Division (divisor: 1024*1024)"""
|
251
233
|
try:
|
252
234
|
return dividend / (1024 * 1024)
|
253
235
|
except Exception as e:
|
254
|
-
|
236
|
+
if v_true(debug, bool):
|
237
|
+
logger.exception(e)
|
255
238
|
return None
|
256
239
|
|
257
240
|
|
@@ -263,11 +246,10 @@ def check_file_type(
|
|
263
246
|
file_type: str,
|
264
247
|
debug: bool = False
|
265
248
|
) -> bool | None:
|
266
|
-
"""
|
267
|
-
检查文件类型
|
268
|
-
file_object 文件对象
|
269
|
-
file_type 文件类型
|
270
|
-
"""
|
249
|
+
"""check file type"""
|
250
|
+
# 检查文件类型
|
251
|
+
# file_object 文件对象
|
252
|
+
# file_type 文件类型
|
271
253
|
try:
|
272
254
|
_file_path = Path(file_object)
|
273
255
|
match True:
|
@@ -296,7 +278,8 @@ def check_file_type(
|
|
296
278
|
case _:
|
297
279
|
return False
|
298
280
|
except Exception as e:
|
299
|
-
|
281
|
+
if v_true(debug, bool):
|
282
|
+
logger.exception(e)
|
300
283
|
return False
|
301
284
|
|
302
285
|
|
@@ -309,14 +292,11 @@ def list_sort(
|
|
309
292
|
debug: bool = False,
|
310
293
|
**kwargs
|
311
294
|
) -> list | None:
|
312
|
-
"""
|
313
|
-
列表排序, 示例: list_sort(['1.2.3.4', '2.3.4.5'], key=inet_aton)
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
https://stackoverflow.com/a/4183538
|
318
|
-
https://blog.csdn.net/u013541325/article/details/117530957
|
319
|
-
"""
|
295
|
+
"""list sort"""
|
296
|
+
# 列表排序, 示例: list_sort(['1.2.3.4', '2.3.4.5'], key=inet_aton)
|
297
|
+
# 参考文档:
|
298
|
+
# https://stackoverflow.com/a/4183538
|
299
|
+
# https://blog.csdn.net/u013541325/article/details/117530957
|
320
300
|
try:
|
321
301
|
|
322
302
|
# from ipaddress import ip_address
|
@@ -331,7 +311,8 @@ def list_sort(
|
|
331
311
|
return _data
|
332
312
|
|
333
313
|
except Exception as e:
|
334
|
-
|
314
|
+
if v_true(debug, bool):
|
315
|
+
logger.exception(e)
|
335
316
|
return None
|
336
317
|
|
337
318
|
|
@@ -341,18 +322,15 @@ def list_dict_sorted_by_key(
|
|
341
322
|
debug: bool = False,
|
342
323
|
**kwargs
|
343
324
|
) -> list | None:
|
344
|
-
"""
|
345
|
-
列表字典排序
|
346
|
-
|
347
|
-
"""
|
348
|
-
参考文档:
|
349
|
-
https://stackoverflow.com/a/73050
|
350
|
-
"""
|
325
|
+
"""list dict sorted by key"""
|
326
|
+
# 列表字典排序
|
327
|
+
# 参考文档: https://stackoverflow.com/a/73050
|
351
328
|
try:
|
352
329
|
_data = deepcopy(data)
|
353
330
|
return sorted(_data, key=lambda x: x[key], **kwargs)
|
354
331
|
except Exception as e:
|
355
|
-
|
332
|
+
if v_true(debug, bool):
|
333
|
+
logger.exception(e)
|
356
334
|
return None
|
357
335
|
|
358
336
|
|
@@ -362,33 +340,32 @@ def list_split(
|
|
362
340
|
equally: bool = False,
|
363
341
|
debug: bool = False
|
364
342
|
) -> list | None:
|
365
|
-
"""
|
366
|
-
列表分割
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
343
|
+
"""list split"""
|
344
|
+
# 列表分割
|
345
|
+
#
|
346
|
+
# 默认: 将 list 以 number个元素为一个list 分割
|
347
|
+
#
|
348
|
+
# data = [1, 2, 3, 4, 5, 6, 7]
|
349
|
+
#
|
350
|
+
# list_split(data, 2) -> 将 data 以 2个元素为一个 list 分割
|
351
|
+
# [[1, 2], [3, 4], [5, 6], [7]]
|
352
|
+
#
|
353
|
+
# list_split(data, 3) -> 将 data 以 3个元素为一个 list 分割
|
354
|
+
# [[1, 2, 3], [4, 5, 6], [7]]
|
355
|
+
#
|
356
|
+
# equally 为 True 时, 将 data 平均分成 number 份
|
357
|
+
#
|
358
|
+
# data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
|
359
|
+
#
|
360
|
+
# list_split_equally(data, 5) -> 将 data 平均分成 5 份
|
361
|
+
# [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16], [17, 18, 19]]
|
362
|
+
#
|
363
|
+
# list_split_equally(data, 6) -> 将 data 平均分成 6 份
|
364
|
+
# [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10], [11, 12, 13], [14, 15, 16], [17, 18, 19]]
|
365
|
+
#
|
366
|
+
# list_split_equally(data, 7) -> 将 data 平均分成 7 份
|
367
|
+
# [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12], [13, 14, 15], [16, 17], [18, 19]]
|
372
368
|
|
373
|
-
list_split(data, 2) -> 将 data 以 2个元素为一个 list 分割
|
374
|
-
[[1, 2], [3, 4], [5, 6], [7]]
|
375
|
-
|
376
|
-
list_split(data, 3) -> 将 data 以 3个元素为一个 list 分割
|
377
|
-
[[1, 2, 3], [4, 5, 6], [7]]
|
378
|
-
|
379
|
-
equally 为 True 时, 将 data 平均分成 number 份
|
380
|
-
|
381
|
-
data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
|
382
|
-
|
383
|
-
list_split_equally(data, 5) -> 将 data 平均分成 5 份
|
384
|
-
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16], [17, 18, 19]]
|
385
|
-
|
386
|
-
list_split_equally(data, 6) -> 将 data 平均分成 6 份
|
387
|
-
[[1, 2, 3, 4], [5, 6, 7], [8, 9, 10], [11, 12, 13], [14, 15, 16], [17, 18, 19]]
|
388
|
-
|
389
|
-
list_split_equally(data, 7) -> 将 data 平均分成 7 份
|
390
|
-
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12], [13, 14, 15], [16, 17], [18, 19]]
|
391
|
-
"""
|
392
369
|
try:
|
393
370
|
|
394
371
|
# 数据拷贝
|
@@ -403,29 +380,46 @@ def list_split(
|
|
403
380
|
logger.info(f"data length: {_data_length}")
|
404
381
|
|
405
382
|
if _data_length < number:
|
406
|
-
|
383
|
+
if v_true(debug, bool):
|
384
|
+
logger.error('number must greater than data length')
|
407
385
|
return None
|
408
|
-
|
386
|
+
|
387
|
+
if _data_length == number:
|
388
|
+
|
409
389
|
_data_result = [[i] for i in _data_object]
|
390
|
+
|
410
391
|
else:
|
411
392
|
|
412
393
|
if equally is True:
|
413
394
|
|
414
395
|
# 数据平分时, 每份数据的最大长度
|
415
396
|
_step_number = step_number_for_split_equally(_data_length, number, debug=debug)
|
416
|
-
|
397
|
+
|
398
|
+
if v_true(debug, bool):
|
399
|
+
logger.info(f"step number: {_step_number}")
|
400
|
+
|
417
401
|
if _data_length % number == 0:
|
402
|
+
|
418
403
|
index_number_list = list(range(0, _data_length, number))
|
419
|
-
|
404
|
+
|
405
|
+
if v_true(debug, bool):
|
406
|
+
logger.info(f"index number list: {index_number_list}")
|
407
|
+
|
420
408
|
for index_number in index_number_list:
|
421
|
-
|
409
|
+
|
410
|
+
if v_true(debug, bool):
|
411
|
+
logger.info(f"index: {index_number}, data: {_data_object[index_number:index_number + number]}")
|
412
|
+
|
422
413
|
_data_result.append(deepcopy(_data_object[index_number:index_number + number]))
|
414
|
+
|
423
415
|
else:
|
416
|
+
|
424
417
|
# 前一部分
|
425
418
|
previous_end_number = (_data_length % number) * _step_number
|
426
419
|
previous_index_number_list = list(range(0, previous_end_number, _step_number))
|
427
420
|
for index_number in previous_index_number_list:
|
428
421
|
_data_result.append(deepcopy(_data_object[index_number:index_number + _step_number]))
|
422
|
+
|
429
423
|
# 后一部分
|
430
424
|
next_number_list = list(range(previous_end_number, _data_length, _step_number - 1))
|
431
425
|
for index_number in next_number_list:
|
@@ -439,7 +433,8 @@ def list_split(
|
|
439
433
|
return _data_result
|
440
434
|
|
441
435
|
except Exception as e:
|
442
|
-
|
436
|
+
if v_true(debug, bool):
|
437
|
+
logger.exception(e)
|
443
438
|
return None
|
444
439
|
|
445
440
|
|
@@ -449,16 +444,16 @@ def list_print_by_step(
|
|
449
444
|
separator: str | None = None,
|
450
445
|
debug: bool = False
|
451
446
|
) -> bool:
|
452
|
-
"""
|
453
|
-
列表按照 步长 和 分隔符 有规律的输出
|
454
|
-
"""
|
447
|
+
"""list print by step"""
|
448
|
+
# 列表按照 步长 和 分隔符 有规律的输出
|
455
449
|
try:
|
456
450
|
_data_list = list_split(data, number, debug=debug)
|
457
451
|
for _item in _data_list:
|
458
452
|
print(*_item, sep=separator)
|
459
453
|
return True
|
460
454
|
except Exception as e:
|
461
|
-
|
455
|
+
if v_true(debug, bool):
|
456
|
+
logger.exception(e)
|
462
457
|
return False
|
463
458
|
|
464
459
|
|
@@ -467,12 +462,14 @@ def list_remove_list(
|
|
467
462
|
remove: list,
|
468
463
|
debug: bool = False
|
469
464
|
) -> list | None:
|
465
|
+
"""List remove List"""
|
470
466
|
try:
|
471
467
|
_original = deepcopy(original)
|
472
468
|
_remove = deepcopy(remove)
|
473
469
|
return [i for i in _original if i not in _remove]
|
474
470
|
except Exception as e:
|
475
|
-
|
471
|
+
if v_true(debug, bool):
|
472
|
+
logger.exception(e)
|
476
473
|
return None
|
477
474
|
|
478
475
|
|
@@ -480,14 +477,16 @@ def list_merge(
|
|
480
477
|
data: list,
|
481
478
|
debug: bool = False
|
482
479
|
) -> list | None:
|
483
|
-
"""
|
480
|
+
"""list merge"""
|
481
|
+
# 合并 List 中的 List 为一个 List
|
484
482
|
try:
|
485
483
|
_results = []
|
486
484
|
for i in deepcopy(data):
|
487
485
|
_results += i
|
488
486
|
return _results
|
489
487
|
except Exception as e:
|
490
|
-
|
488
|
+
if v_true(debug, bool):
|
489
|
+
logger.exception(e)
|
491
490
|
return None
|
492
491
|
|
493
492
|
|
@@ -497,13 +496,15 @@ def list_to_file(
|
|
497
496
|
encoding: str = 'utf-8-sig',
|
498
497
|
debug: bool = False
|
499
498
|
) -> bool:
|
499
|
+
"""list to file"""
|
500
500
|
try:
|
501
501
|
with open(file, 'w', encoding=encoding) as _file:
|
502
502
|
for line in data:
|
503
503
|
_file.write(f"{line}\n")
|
504
504
|
return True
|
505
505
|
except Exception as e:
|
506
|
-
|
506
|
+
if v_true(debug, bool):
|
507
|
+
logger.exception(e)
|
507
508
|
return False
|
508
509
|
|
509
510
|
def list_to_csvfile(
|
@@ -514,6 +515,7 @@ def list_to_csvfile(
|
|
514
515
|
debug: bool = False,
|
515
516
|
**kwargs
|
516
517
|
) -> bool:
|
518
|
+
"""list to csvfile"""
|
517
519
|
try:
|
518
520
|
with open(file, 'w', encoding=encoding) as _file:
|
519
521
|
# CRLF replaced by LF
|
@@ -524,7 +526,8 @@ def list_to_csvfile(
|
|
524
526
|
outcsv.writerows(data)
|
525
527
|
return True
|
526
528
|
except Exception as e:
|
527
|
-
|
529
|
+
if v_true(debug, bool):
|
530
|
+
logger.exception(e)
|
528
531
|
return False
|
529
532
|
|
530
533
|
def range_zfill(
|
@@ -534,7 +537,8 @@ def range_zfill(
|
|
534
537
|
width: int,
|
535
538
|
debug: bool = False
|
536
539
|
) -> list | None:
|
537
|
-
"""
|
540
|
+
"""range zfill"""
|
541
|
+
# 生成长度相同的字符串的列表
|
538
542
|
# 示例: range_zfill(8, 13, 1, 2) => ['08', '09', '10', '11', '12']
|
539
543
|
# 生成 小时 列表: range_zfill(0, 24, 1, 2)
|
540
544
|
# 生成 分钟和秒 列表: range_zfill(0, 60, 1, 2)
|
@@ -543,7 +547,8 @@ def range_zfill(
|
|
543
547
|
try:
|
544
548
|
return [str(i).zfill(width) for i in range(start, stop, step)]
|
545
549
|
except Exception as e:
|
546
|
-
|
550
|
+
if v_true(debug, bool):
|
551
|
+
logger.exception(e)
|
547
552
|
return None
|
548
553
|
|
549
554
|
|
@@ -555,12 +560,14 @@ def dict_remove_key(
|
|
555
560
|
key: str,
|
556
561
|
debug: bool = False
|
557
562
|
) -> None | dict:
|
563
|
+
"""dict remove key"""
|
558
564
|
try:
|
559
565
|
data_copy: dict = deepcopy(data)
|
560
566
|
data_copy.pop(key)
|
561
567
|
return data_copy
|
562
568
|
except Exception as e:
|
563
|
-
|
569
|
+
if v_true(debug, bool):
|
570
|
+
logger.exception(e)
|
564
571
|
return None
|
565
572
|
|
566
573
|
def dict_to_file(
|
@@ -570,12 +577,14 @@ def dict_to_file(
|
|
570
577
|
debug: bool = False,
|
571
578
|
**kwargs
|
572
579
|
) -> bool:
|
580
|
+
"""dict to file"""
|
573
581
|
try:
|
574
582
|
with open(file, 'w', encoding=encoding) as _file:
|
575
583
|
json.dump(obj=data, fp=_file, indent=4, sort_keys=True, **kwargs)
|
576
584
|
return True
|
577
585
|
except Exception as e:
|
578
|
-
|
586
|
+
if v_true(debug, bool):
|
587
|
+
logger.exception(e)
|
579
588
|
return False
|
580
589
|
|
581
590
|
|
@@ -585,10 +594,9 @@ def dict_nested_update(
|
|
585
594
|
value: any,
|
586
595
|
debug: bool = False
|
587
596
|
) -> bool:
|
588
|
-
"""
|
589
|
-
dictionary nested update
|
590
|
-
https://stackoverflow.com/a/58885744
|
591
|
-
"""
|
597
|
+
"""dict nested update"""
|
598
|
+
# dictionary nested update
|
599
|
+
# https://stackoverflow.com/a/58885744
|
592
600
|
try:
|
593
601
|
if v_true(data, dict, debug=debug):
|
594
602
|
for _k, _v in data.items():
|
@@ -610,7 +618,8 @@ def dict_nested_update(
|
|
610
618
|
return False
|
611
619
|
return True
|
612
620
|
except Exception as e:
|
613
|
-
|
621
|
+
if v_true(debug, bool):
|
622
|
+
logger.exception(e)
|
614
623
|
return False
|
615
624
|
|
616
625
|
|
@@ -622,23 +631,32 @@ def filename(
|
|
622
631
|
split: str = '.',
|
623
632
|
debug: bool = False
|
624
633
|
) -> str | None:
|
625
|
-
"""
|
626
|
-
|
627
|
-
https://stackoverflow.com/questions/678236/how-do-i-get-the-filename-without-the-extension-from-a-path-in-python
|
628
|
-
https://stackoverflow.com/questions/4152963/get-name-of-current-script-in-python
|
629
|
-
'''
|
634
|
+
"""filename"""
|
635
|
+
# 获取文件名称
|
636
|
+
# https://stackoverflow.com/questions/678236/how-do-i-get-the-filename-without-the-extension-from-a-path-in-python
|
637
|
+
# https://stackoverflow.com/questions/4152963/get-name-of-current-script-in-python
|
630
638
|
try:
|
639
|
+
|
631
640
|
if v_true(debug, bool):
|
632
641
|
logger.info(f"file: {file}")
|
633
642
|
logger.info(f"split: {split}")
|
643
|
+
|
634
644
|
_basename = str(os.path.basename(file))
|
635
|
-
|
645
|
+
|
646
|
+
if v_true(debug, bool):
|
647
|
+
logger.info(f"basename: {_basename}")
|
648
|
+
|
636
649
|
_index_of_split = _basename.index(split)
|
637
|
-
|
638
|
-
|
650
|
+
|
651
|
+
if v_true(debug, bool):
|
652
|
+
logger.info(f"index of split: {_index_of_split}")
|
653
|
+
logger.info(f"filename: {_basename[:_index_of_split]}")
|
654
|
+
|
639
655
|
return _basename[:_index_of_split]
|
656
|
+
|
640
657
|
except Exception as e:
|
641
|
-
|
658
|
+
if v_true(debug, bool):
|
659
|
+
logger.exception(e)
|
642
660
|
return None
|
643
661
|
|
644
662
|
|
@@ -647,12 +665,11 @@ def filehash(
|
|
647
665
|
sha: str = 'md5',
|
648
666
|
debug: bool = False
|
649
667
|
) -> str | None:
|
650
|
-
"""
|
651
|
-
|
652
|
-
参考文档:
|
653
|
-
|
654
|
-
|
655
|
-
"""
|
668
|
+
"""filehash"""
|
669
|
+
# 获取文件Hash
|
670
|
+
# 参考文档:
|
671
|
+
# https://stackoverflow.com/a/59056837
|
672
|
+
# https://stackoverflow.com/questions/22058048/hashing-a-file-in-python
|
656
673
|
try:
|
657
674
|
with open(file, "rb") as _file:
|
658
675
|
match True:
|
@@ -687,7 +704,8 @@ def filehash(
|
|
687
704
|
file_hash.update(chunk)
|
688
705
|
return file_hash.hexdigest()
|
689
706
|
except Exception as e:
|
690
|
-
|
707
|
+
if v_true(debug, bool):
|
708
|
+
logger.exception(e)
|
691
709
|
return None
|
692
710
|
|
693
711
|
|
@@ -695,11 +713,13 @@ def filesize(
|
|
695
713
|
file: str,
|
696
714
|
debug: bool = False
|
697
715
|
) -> int | None:
|
698
|
-
"""
|
716
|
+
"""filesize"""
|
717
|
+
# 获取文件大小
|
699
718
|
try:
|
700
719
|
return os.path.getsize(file)
|
701
720
|
except Exception as e:
|
702
|
-
|
721
|
+
if v_true(debug, bool):
|
722
|
+
logger.exception(e)
|
703
723
|
return None
|
704
724
|
|
705
725
|
|
@@ -707,7 +727,8 @@ def filesize(
|
|
707
727
|
|
708
728
|
|
709
729
|
def resolve_path() -> str | None:
|
710
|
-
"""
|
730
|
+
"""resolve path"""
|
731
|
+
# 获取当前目录名称
|
711
732
|
return str(Path().resolve())
|
712
733
|
|
713
734
|
|
@@ -720,7 +741,8 @@ def parent_path(
|
|
720
741
|
try:
|
721
742
|
return str(Path(path, **kwargs).parent.resolve()) if v_true(path, str, debug=debug) else None
|
722
743
|
except Exception as e:
|
723
|
-
|
744
|
+
if v_true(debug, bool):
|
745
|
+
logger.exception(e)
|
724
746
|
return None
|
725
747
|
|
726
748
|
|
@@ -731,10 +753,12 @@ def real_path(
|
|
731
753
|
) -> str | None:
|
732
754
|
"""获取真实路径"""
|
733
755
|
try:
|
734
|
-
|
756
|
+
if v_true(debug, bool):
|
757
|
+
logger.info(f"path: {path}")
|
735
758
|
return os.path.realpath(path, **kwargs)
|
736
759
|
except Exception as e:
|
737
|
-
|
760
|
+
if v_true(debug, bool):
|
761
|
+
logger.exception(e)
|
738
762
|
return None
|
739
763
|
|
740
764
|
|
@@ -748,10 +772,8 @@ def retry(
|
|
748
772
|
**kwargs
|
749
773
|
):
|
750
774
|
"""重试"""
|
751
|
-
|
752
|
-
|
753
|
-
callable() 判断类型是非为函数: https://stackoverflow.com/a/624939
|
754
|
-
"""
|
775
|
+
# 函数传递参数: https://stackoverflow.com/a/803632
|
776
|
+
# callable() 判断类型是非为函数: https://stackoverflow.com/a/624939
|
755
777
|
try:
|
756
778
|
_num = 0
|
757
779
|
while True:
|
@@ -764,61 +786,60 @@ def retry(
|
|
764
786
|
try:
|
765
787
|
return func(**kwargs)
|
766
788
|
except Exception as e:
|
767
|
-
|
789
|
+
if v_true(debug, bool):
|
790
|
+
logger.exception(e)
|
768
791
|
logger.success('retrying ...')
|
769
792
|
continue
|
770
793
|
# break
|
771
794
|
except Exception as e:
|
772
|
-
|
795
|
+
if v_true(debug, bool):
|
796
|
+
logger.exception(e)
|
773
797
|
|
774
798
|
|
775
799
|
# --------------------------------------------------------------------------------------------------
|
776
800
|
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
Unix Timestamp
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
datetime.fromisoformat(time.strftime(format, time.gmtime(dt)))
|
821
|
-
"""
|
801
|
+
# 日期时间有两种: UTC datetime (UTC时区日期时间) 和 Local datetime (当前时区日期时间)
|
802
|
+
#
|
803
|
+
# Unix Timestamp 仅为 UTC datetime 的值
|
804
|
+
#
|
805
|
+
# 但是, Local datetime 可以直接转换为 Unix Timestamp, UTC datetime 需要先转换到 UTC TimeZone 再转换为 Unix Timestamp
|
806
|
+
#
|
807
|
+
# 相反, Unix Timestamp 可以直接转换为 UTC datetime, 要获得 Local datetime, 需要再将 UTC datetime 转换为 Local datetime
|
808
|
+
#
|
809
|
+
# https://stackoverflow.com/a/13287083
|
810
|
+
# https://stackoverflow.com/a/466376
|
811
|
+
# https://stackoverflow.com/a/7999977
|
812
|
+
# https://stackoverflow.com/a/3682808
|
813
|
+
# https://stackoverflow.com/a/63920772
|
814
|
+
# https://www.geeksforgeeks.org/how-to-remove-timezone-information-from-datetime-object-in-python/
|
815
|
+
#
|
816
|
+
# pytz all timezones
|
817
|
+
#
|
818
|
+
# https://stackoverflow.com/a/13867319
|
819
|
+
# https://stackoverflow.com/a/15692958
|
820
|
+
#
|
821
|
+
# import pytz
|
822
|
+
# pytz.all_timezones
|
823
|
+
# pytz.common_timezones
|
824
|
+
# pytz.timezone('US/Eastern')
|
825
|
+
#
|
826
|
+
# timezone
|
827
|
+
#
|
828
|
+
# https://stackoverflow.com/a/39079819
|
829
|
+
# https://stackoverflow.com/a/1681600
|
830
|
+
# https://stackoverflow.com/a/4771733
|
831
|
+
# https://stackoverflow.com/a/63920772
|
832
|
+
# https://toutiao.io/posts/sin4x0/preview
|
833
|
+
#
|
834
|
+
# 其它:
|
835
|
+
#
|
836
|
+
# dt.replace(tzinfo=timezone.utc).astimezone(tz=None)
|
837
|
+
#
|
838
|
+
# (dt.replace(tzinfo=timezone.utc).astimezone(tz=None)).strftime(format)
|
839
|
+
# datetime.fromisoformat((dt.replace(tzinfo=timezone.utc).astimezone(tz=None)).strftime(format))
|
840
|
+
# string_to_datetime((dt.replace(tzinfo=timezone.utc).astimezone(tz=None)).strftime(format), format)
|
841
|
+
#
|
842
|
+
# datetime.fromisoformat(time.strftime(format, time.gmtime(dt)))
|
822
843
|
|
823
844
|
|
824
845
|
def date_to_datetime(
|
@@ -830,20 +851,22 @@ def date_to_datetime(
|
|
830
851
|
try:
|
831
852
|
return datetime.datetime.combine(date_object, datetime.datetime.min.time())
|
832
853
|
except Exception as e:
|
833
|
-
|
854
|
+
if v_true(debug, bool):
|
855
|
+
logger.exception(e)
|
834
856
|
return None
|
835
857
|
|
836
858
|
|
837
859
|
def datetime_to_date(
|
838
|
-
|
860
|
+
datetime_instance: datetime.datetime,
|
839
861
|
debug: bool = False
|
840
862
|
) -> datetime.date | None:
|
841
863
|
"""'日期时间'转换为'日期'"""
|
842
864
|
# https://stackoverflow.com/a/3743240
|
843
865
|
try:
|
844
|
-
return
|
866
|
+
return datetime_instance.date()
|
845
867
|
except Exception as e:
|
846
|
-
|
868
|
+
if v_true(debug, bool):
|
869
|
+
logger.exception(e)
|
847
870
|
return None
|
848
871
|
|
849
872
|
|
@@ -861,12 +884,13 @@ def datetime_now(
|
|
861
884
|
try:
|
862
885
|
return datetime.datetime.utcnow() if _utc is True else datetime.datetime.now(**kwargs)
|
863
886
|
except Exception as e:
|
864
|
-
|
887
|
+
if v_true(debug, bool):
|
888
|
+
logger.exception(e)
|
865
889
|
return None
|
866
890
|
|
867
891
|
|
868
892
|
def datetime_offset(
|
869
|
-
|
893
|
+
datetime_instance: datetime.datetime,
|
870
894
|
debug: bool = False,
|
871
895
|
**kwargs
|
872
896
|
) -> datetime.datetime | None:
|
@@ -877,30 +901,31 @@ def datetime_offset(
|
|
877
901
|
"""
|
878
902
|
_utc = kwargs.pop("utc", False)
|
879
903
|
try:
|
880
|
-
if isinstance(
|
881
|
-
return
|
882
|
-
else
|
883
|
-
return datetime.datetime.utcnow() + datetime.timedelta(**kwargs) if _utc is True else datetime.datetime.now() + datetime.timedelta(**kwargs)
|
904
|
+
if isinstance(datetime_instance, datetime.datetime):
|
905
|
+
return datetime_instance + datetime.timedelta(**kwargs)
|
906
|
+
return datetime.datetime.utcnow() + datetime.timedelta(**kwargs) if _utc is True else datetime.datetime.now() + datetime.timedelta(**kwargs)
|
884
907
|
except Exception as e:
|
885
|
-
|
908
|
+
if v_true(debug, bool):
|
909
|
+
logger.exception(e)
|
886
910
|
return None
|
887
911
|
|
888
912
|
|
889
913
|
def datetime_to_string(
|
890
|
-
|
914
|
+
datetime_instance: datetime.datetime,
|
891
915
|
string_format: str = '%Y-%m-%d %H:%M:%S',
|
892
916
|
debug: bool = False
|
893
917
|
) -> str | None:
|
894
918
|
"""'日期时间'转换为'字符串'"""
|
895
919
|
try:
|
896
|
-
return datetime.datetime.strftime(
|
920
|
+
return datetime.datetime.strftime(datetime_instance, string_format) if isinstance(datetime_instance, datetime.datetime) is True else None
|
897
921
|
except Exception as e:
|
898
|
-
|
922
|
+
if v_true(debug, bool):
|
923
|
+
logger.exception(e)
|
899
924
|
return None
|
900
925
|
|
901
926
|
|
902
927
|
def datetime_to_timestamp(
|
903
|
-
|
928
|
+
datetime_instance: datetime.datetime,
|
904
929
|
utc: bool = False,
|
905
930
|
debug: bool = False
|
906
931
|
) -> int | None:
|
@@ -910,17 +935,17 @@ def datetime_to_timestamp(
|
|
910
935
|
UTC datetime 需要先替换 timezone 再转换为 Unix Timestamp
|
911
936
|
"""
|
912
937
|
try:
|
913
|
-
if isinstance(
|
914
|
-
return int(
|
915
|
-
|
916
|
-
return None
|
938
|
+
if isinstance(datetime_instance, datetime.datetime):
|
939
|
+
return int(datetime_instance.replace(tzinfo=datetime.timezone.utc).timestamp()) if utc is True else int(datetime_instance.timestamp())
|
940
|
+
return None
|
917
941
|
except Exception as e:
|
918
|
-
|
942
|
+
if v_true(debug, bool):
|
943
|
+
logger.exception(e)
|
919
944
|
return None
|
920
945
|
|
921
946
|
|
922
947
|
def datetime_local_to_timezone(
|
923
|
-
|
948
|
+
datetime_instance: datetime.datetime,
|
924
949
|
tz: datetime.timezone = datetime.timezone.utc,
|
925
950
|
debug: bool = False
|
926
951
|
) -> datetime.datetime | None:
|
@@ -929,17 +954,18 @@ def datetime_local_to_timezone(
|
|
929
954
|
replace(tzinfo=None) 移除结尾的时区信息
|
930
955
|
"""
|
931
956
|
try:
|
932
|
-
if isinstance(
|
933
|
-
return (datetime.datetime.fromtimestamp(
|
957
|
+
if isinstance(datetime_instance, datetime.datetime) is True:
|
958
|
+
return (datetime.datetime.fromtimestamp(datetime_instance.timestamp(), tz=tz)).replace(tzinfo=None)
|
934
959
|
else:
|
935
960
|
return None
|
936
961
|
except Exception as e:
|
937
|
-
|
962
|
+
if v_true(debug, bool):
|
963
|
+
logger.exception(e)
|
938
964
|
return None
|
939
965
|
|
940
966
|
|
941
967
|
def datetime_utc_to_timezone(
|
942
|
-
|
968
|
+
datetime_instance: datetime.datetime,
|
943
969
|
tz: datetime.timezone = datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo,
|
944
970
|
debug: bool = False
|
945
971
|
) -> datetime.datetime | None:
|
@@ -948,12 +974,13 @@ def datetime_utc_to_timezone(
|
|
948
974
|
replace(tzinfo=None) 移除结尾的时区信息
|
949
975
|
"""
|
950
976
|
try:
|
951
|
-
if isinstance(
|
952
|
-
return
|
977
|
+
if isinstance(datetime_instance, datetime.datetime) is True:
|
978
|
+
return datetime_instance.replace(tzinfo=datetime.timezone.utc).astimezone(tz).replace(tzinfo=None)
|
953
979
|
else:
|
954
980
|
return None
|
955
981
|
except Exception as e:
|
956
|
-
|
982
|
+
if v_true(debug, bool):
|
983
|
+
logger.exception(e)
|
957
984
|
return None
|
958
985
|
|
959
986
|
|
@@ -966,7 +993,8 @@ def timestamp_to_datetime(
|
|
966
993
|
try:
|
967
994
|
return (datetime.datetime.fromtimestamp(timestamp, tz=tz)).replace(tzinfo=None) if v_true(timestamp, int, debug=debug) else None
|
968
995
|
except Exception as e:
|
969
|
-
|
996
|
+
if v_true(debug, bool):
|
997
|
+
logger.exception(e)
|
970
998
|
return None
|
971
999
|
|
972
1000
|
|
@@ -975,10 +1003,12 @@ def datetime_string_to_datetime(
|
|
975
1003
|
datetime_format: str = '%Y-%m-%d %H:%M:%S',
|
976
1004
|
debug: bool = False
|
977
1005
|
) -> datetime.datetime | None:
|
1006
|
+
"""datetime string to datetime"""
|
978
1007
|
try:
|
979
1008
|
return datetime.datetime.strptime(datetime_string, datetime_format) if v_true(datetime_string, str, debug=debug) else None
|
980
1009
|
except Exception as e:
|
981
|
-
|
1010
|
+
if v_true(debug, bool):
|
1011
|
+
logger.exception(e)
|
982
1012
|
return None
|
983
1013
|
|
984
1014
|
|
@@ -987,10 +1017,12 @@ def datetime_string_to_timestamp(
|
|
987
1017
|
datetime_format: str = '%Y-%m-%d %H:%M:%S',
|
988
1018
|
debug: bool = False
|
989
1019
|
) -> int | None:
|
1020
|
+
"""datetime string to timestamp"""
|
990
1021
|
try:
|
991
1022
|
return int(time.mktime(time.strptime(datetime_string, datetime_format))) if v_true(datetime_string, str, debug=debug) else None
|
992
1023
|
except Exception as e:
|
993
|
-
|
1024
|
+
if v_true(debug, bool):
|
1025
|
+
logger.exception(e)
|
994
1026
|
return None
|
995
1027
|
|
996
1028
|
|
@@ -998,6 +1030,7 @@ def datetime_object(
|
|
998
1030
|
date_time: datetime.datetime,
|
999
1031
|
debug: bool = False
|
1000
1032
|
) -> dict | None:
|
1033
|
+
"""datetime object"""
|
1001
1034
|
try:
|
1002
1035
|
return {
|
1003
1036
|
'date': date_time.strftime("%Y-%m-%d"),
|
@@ -1008,31 +1041,30 @@ def datetime_object(
|
|
1008
1041
|
'datetime_zero': date_time.strftime('%Y-%m-%d 00:00:00')
|
1009
1042
|
}
|
1010
1043
|
except Exception as e:
|
1011
|
-
|
1044
|
+
if v_true(debug, bool):
|
1045
|
+
logger.exception(e)
|
1012
1046
|
return None
|
1013
1047
|
|
1014
1048
|
|
1015
1049
|
# --------------------------------------------------------------------------------------------------
|
1016
1050
|
|
1017
1051
|
|
1018
|
-
''
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
#
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
print(outputs, type(outputs))
|
1035
|
-
'''
|
1052
|
+
# run_cmd = bash('echo ok', universal_newlines=True, stdout=PIPE)
|
1053
|
+
#
|
1054
|
+
# if run_cmd != None:
|
1055
|
+
# returncode = run_cmd.returncode
|
1056
|
+
# outputs = run_cmd.stdout.splitlines()
|
1057
|
+
# print(returncode, type(returncode))
|
1058
|
+
# print(outputs, type(outputs))
|
1059
|
+
#
|
1060
|
+
# # echo 'echo ok' > /tmp/ok.sh
|
1061
|
+
# run_script = bash('/tmp/ok.sh', file=True, universal_newlines=True, stdout=PIPE)
|
1062
|
+
#
|
1063
|
+
# if run_script != None:
|
1064
|
+
# returncode = run_script.returncode
|
1065
|
+
# outputs = run_script.stdout.splitlines()
|
1066
|
+
# print(returncode, type(returncode))
|
1067
|
+
# print(outputs, type(outputs))
|
1036
1068
|
|
1037
1069
|
|
1038
1070
|
def shell(
|
@@ -1050,18 +1082,17 @@ def shell(
|
|
1050
1082
|
return None
|
1051
1083
|
case True if v_true(sh_shell, str, debug=debug) and v_true(command, str, debug=debug):
|
1052
1084
|
if isfile is True:
|
1053
|
-
if sh_option
|
1054
|
-
return subprocess.run([sh_shell, command], **kwargs)
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
sh_option = '-c'
|
1060
|
-
return subprocess.run([sh_shell, sh_option, command], **kwargs)
|
1085
|
+
if sh_option is None:
|
1086
|
+
return subprocess.run([sh_shell, command], **kwargs, check=False)
|
1087
|
+
return subprocess.run([sh_shell, sh_option, command], **kwargs, check=False)
|
1088
|
+
if sh_option is None:
|
1089
|
+
sh_option = '-c'
|
1090
|
+
return subprocess.run([sh_shell, sh_option, command], **kwargs, check=False)
|
1061
1091
|
case _:
|
1062
1092
|
return None
|
1063
1093
|
except Exception as e:
|
1064
|
-
|
1094
|
+
if v_true(debug, bool):
|
1095
|
+
logger.exception(e)
|
1065
1096
|
return None
|
1066
1097
|
|
1067
1098
|
|
@@ -1072,39 +1103,37 @@ def json_file_parser(
|
|
1072
1103
|
file: str,
|
1073
1104
|
debug: bool = False
|
1074
1105
|
) -> dict | None:
|
1106
|
+
"""JSON File Parser"""
|
1075
1107
|
try:
|
1076
1108
|
if check_file_type(file, 'file', debug=debug):
|
1077
|
-
with open(file) as json_raw:
|
1109
|
+
with open(file, encoding="utf-8") as json_raw:
|
1078
1110
|
json_dict = json.load(json_raw)
|
1079
1111
|
return json_dict
|
1080
|
-
|
1081
|
-
logger.error(f"No such file: {file}")
|
1082
|
-
|
1112
|
+
if v_true(debug, bool):
|
1113
|
+
logger.error(f"No such file: {file}")
|
1114
|
+
return None
|
1083
1115
|
except Exception as e:
|
1084
|
-
|
1116
|
+
if v_true(debug, bool):
|
1117
|
+
logger.exception(e)
|
1085
1118
|
return None
|
1086
1119
|
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
"markdown.preview.fontSize": 14,
|
1105
|
-
"workbench.iconTheme": "vscode-icons"
|
1106
|
-
}
|
1107
|
-
"""
|
1120
|
+
# json_raw = '''
|
1121
|
+
# {
|
1122
|
+
# "markdown.preview.fontSize": 14,
|
1123
|
+
# "editor.minimap.enabled": false,
|
1124
|
+
# "workbench.iconTheme": "vscode-icons",
|
1125
|
+
# "http.proxy": "http://127.0.0.1:1087"
|
1126
|
+
# }
|
1127
|
+
# '''
|
1128
|
+
#
|
1129
|
+
# print(json_sort(json_raw))
|
1130
|
+
#
|
1131
|
+
# {
|
1132
|
+
# "editor.minimap.enabled": false,
|
1133
|
+
# "http.proxy": "http://127.0.0.1:1087",
|
1134
|
+
# "markdown.preview.fontSize": 14,
|
1135
|
+
# "workbench.iconTheme": "vscode-icons"
|
1136
|
+
# }
|
1108
1137
|
|
1109
1138
|
|
1110
1139
|
def json_sort(
|
@@ -1112,10 +1141,12 @@ def json_sort(
|
|
1112
1141
|
debug: bool = False,
|
1113
1142
|
**kwargs
|
1114
1143
|
) -> dict | None:
|
1144
|
+
"""JSON Sort"""
|
1115
1145
|
try:
|
1116
1146
|
return json.dumps(json.loads(string), indent=4, sort_keys=True, **kwargs) if v_true(string, str, debug=debug) else None
|
1117
1147
|
except Exception as e:
|
1118
|
-
|
1148
|
+
if v_true(debug, bool):
|
1149
|
+
logger.exception(e)
|
1119
1150
|
return None
|
1120
1151
|
|
1121
1152
|
|
@@ -1132,31 +1163,33 @@ def delete_files(
|
|
1132
1163
|
if v_true(files, str, debug=debug) and check_file_type(files, 'file', debug=debug):
|
1133
1164
|
|
1134
1165
|
os.remove(files)
|
1135
|
-
|
1166
|
+
if v_true(debug, bool):
|
1167
|
+
logger.success(f'deleted file: {files}')
|
1136
1168
|
return True
|
1137
1169
|
|
1138
|
-
|
1170
|
+
if v_true(files, list, debug=debug):
|
1139
1171
|
|
1140
1172
|
for _file in files:
|
1141
1173
|
|
1142
1174
|
if v_true(_file, str, debug=debug) and check_file_type(_file, 'file', debug=debug):
|
1143
1175
|
try:
|
1144
1176
|
os.remove(_file)
|
1145
|
-
logger.success('deleted file: {}'
|
1177
|
+
logger.success(f'deleted file: {_file}')
|
1146
1178
|
except Exception as e:
|
1147
|
-
logger.error('error file: {} {}'
|
1179
|
+
logger.error(f'error file: {_file} {e}')
|
1148
1180
|
else:
|
1149
|
-
logger.error('error file: {}'
|
1181
|
+
logger.error(f'error file: {_file}')
|
1150
1182
|
|
1151
1183
|
return True
|
1152
1184
|
|
1153
|
-
|
1185
|
+
if v_true(debug, bool):
|
1186
|
+
logger.error(f'error file: {files}')
|
1154
1187
|
|
1155
|
-
|
1156
|
-
return False
|
1188
|
+
return False
|
1157
1189
|
|
1158
1190
|
except Exception as e:
|
1159
|
-
|
1191
|
+
if v_true(debug, bool):
|
1192
|
+
logger.exception(e)
|
1160
1193
|
return False
|
1161
1194
|
|
1162
1195
|
|
@@ -1192,7 +1225,10 @@ def delete_directory(
|
|
1192
1225
|
if v_true(directory, str, debug=debug) and check_file_type(directory, 'dir', debug=debug):
|
1193
1226
|
|
1194
1227
|
rmtree(directory)
|
1195
|
-
|
1228
|
+
|
1229
|
+
if v_true(debug, bool):
|
1230
|
+
logger.success(f'deleted directory: {directory}')
|
1231
|
+
|
1196
1232
|
return True
|
1197
1233
|
|
1198
1234
|
elif v_true(directory, list, debug=debug):
|
@@ -1202,21 +1238,25 @@ def delete_directory(
|
|
1202
1238
|
if v_true(_dir, str, debug=debug) and check_file_type(_dir, 'dir', debug=debug):
|
1203
1239
|
try:
|
1204
1240
|
rmtree(_dir)
|
1205
|
-
|
1241
|
+
if v_true(debug, bool):
|
1242
|
+
logger.success(f'deleted directory: {_dir}')
|
1206
1243
|
except Exception as e:
|
1207
|
-
|
1244
|
+
if v_true(debug, bool):
|
1245
|
+
logger.error(f'error directory: {_dir} {e}')
|
1208
1246
|
else:
|
1209
|
-
|
1247
|
+
if v_true(debug, bool):
|
1248
|
+
logger.error(f'error directory: {_dir}')
|
1210
1249
|
|
1211
1250
|
return True
|
1212
1251
|
|
1213
1252
|
else:
|
1214
|
-
|
1215
|
-
|
1253
|
+
if v_true(debug, bool):
|
1254
|
+
logger.error(f'error directory: {directory}')
|
1216
1255
|
return False
|
1217
1256
|
|
1218
1257
|
except Exception as e:
|
1219
|
-
|
1258
|
+
if v_true(debug, bool):
|
1259
|
+
logger.exception(e)
|
1220
1260
|
return False
|
1221
1261
|
|
1222
1262
|
|
@@ -1234,37 +1274,41 @@ def process_pool(
|
|
1234
1274
|
"""
|
1235
1275
|
多线程(MultiThread) | 多进程(MultiProcess)
|
1236
1276
|
"""
|
1237
|
-
|
1238
|
-
ThreadPool
|
1239
|
-
ThreadPool
|
1240
|
-
|
1241
|
-
https://stackoverflow.com/a/58897266
|
1242
|
-
"""
|
1277
|
+
# ThreadPool 线程池
|
1278
|
+
# ThreadPool 共享内存, Pool 不共享内存
|
1279
|
+
# ThreadPool 可以解决 Pool 在某些情况下产生的 Can't pickle local object 的错误
|
1280
|
+
# https://stackoverflow.com/a/58897266
|
1243
1281
|
try:
|
1244
1282
|
|
1245
1283
|
# 处理数据
|
1246
|
-
|
1284
|
+
if v_true(debug, bool):
|
1285
|
+
logger.info("data split ......")
|
1247
1286
|
if len(process_data) <= process_num:
|
1248
1287
|
process_num = len(process_data)
|
1249
1288
|
_data = process_data
|
1250
1289
|
else:
|
1251
1290
|
_data = list_split(process_data, process_num, equally=True, debug=debug)
|
1252
|
-
|
1291
|
+
|
1292
|
+
if v_true(debug, bool):
|
1293
|
+
logger.info(f"data: {_data}")
|
1253
1294
|
|
1254
1295
|
# 执行函数
|
1255
1296
|
if v_true(thread, bool):
|
1256
1297
|
# 多线程
|
1257
|
-
|
1298
|
+
if v_true(debug, bool):
|
1299
|
+
logger.info("execute multi thread ......")
|
1258
1300
|
with ThreadPool(process_num, **kwargs) as p:
|
1259
1301
|
return p.map(process_func, _data)
|
1260
1302
|
else:
|
1261
1303
|
# 多进程
|
1262
|
-
|
1304
|
+
if v_true(debug, bool):
|
1305
|
+
logger.info("execute multi process ......")
|
1263
1306
|
with Pool(process_num, **kwargs) as p:
|
1264
1307
|
return p.map(process_func, _data)
|
1265
1308
|
|
1266
1309
|
except Exception as e:
|
1267
|
-
|
1310
|
+
if v_true(debug, bool):
|
1311
|
+
logger.exception(e)
|
1268
1312
|
return False
|
1269
1313
|
|
1270
1314
|
|
@@ -1276,6 +1320,7 @@ def new_process(
|
|
1276
1320
|
debug: bool = False,
|
1277
1321
|
**kwargs
|
1278
1322
|
) -> Thread | Process | bool:
|
1323
|
+
"""New Process"""
|
1279
1324
|
try:
|
1280
1325
|
if v_true(thread, bool):
|
1281
1326
|
process = Thread(target=process_func, args=process_data, **kwargs)
|
@@ -1285,7 +1330,8 @@ def new_process(
|
|
1285
1330
|
process.start()
|
1286
1331
|
return process
|
1287
1332
|
except Exception as e:
|
1288
|
-
|
1333
|
+
if v_true(debug, bool):
|
1334
|
+
logger.exception(e)
|
1289
1335
|
return False
|
1290
1336
|
|
1291
1337
|
|
@@ -1296,20 +1342,25 @@ def create_empty_file(
|
|
1296
1342
|
file: str | None = None,
|
1297
1343
|
debug: bool = False
|
1298
1344
|
) -> str | None:
|
1345
|
+
"""create empty file"""
|
1299
1346
|
try:
|
1300
1347
|
if file is None:
|
1301
1348
|
# 当前时间戳(纳秒)
|
1302
1349
|
timestamp = time.time_ns()
|
1303
|
-
|
1350
|
+
if v_true(debug, bool):
|
1351
|
+
logger.info(f"timestamp: {timestamp}")
|
1304
1352
|
# 空文件路径
|
1305
1353
|
file = f'/tmp/empty_file_{timestamp}.txt'
|
1306
1354
|
# 创建一个空文件
|
1307
|
-
|
1355
|
+
if v_true(debug, bool):
|
1356
|
+
logger.info(f"file: {file}")
|
1357
|
+
# pylint: disable=R1732,disable=W1514
|
1308
1358
|
open(file, 'w').close()
|
1309
1359
|
# 返回文件路径
|
1310
1360
|
return file
|
1311
1361
|
except Exception as e:
|
1312
|
-
|
1362
|
+
if v_true(debug, bool):
|
1363
|
+
logger.exception(e)
|
1313
1364
|
return None
|
1314
1365
|
|
1315
1366
|
|
@@ -1317,6 +1368,7 @@ def create_empty_file(
|
|
1317
1368
|
|
1318
1369
|
|
1319
1370
|
def uuid4_hex() -> str:
|
1371
|
+
"""UUID"""
|
1320
1372
|
return uuid4().hex
|
1321
1373
|
|
1322
1374
|
|
@@ -1330,7 +1382,8 @@ def increment_version(
|
|
1330
1382
|
version_numbers[-1] = str(int(version_numbers[-1]) + 1)
|
1331
1383
|
return '.'.join(version_numbers)
|
1332
1384
|
except Exception as e:
|
1333
|
-
|
1385
|
+
if v_true(debug, bool):
|
1386
|
+
logger.exception(e)
|
1334
1387
|
return None
|
1335
1388
|
|
1336
1389
|
|
@@ -1346,7 +1399,8 @@ def make_directory(
|
|
1346
1399
|
os.makedirs(directory)
|
1347
1400
|
return True
|
1348
1401
|
except Exception as e:
|
1349
|
-
|
1402
|
+
if v_true(debug, bool):
|
1403
|
+
logger.exception(e)
|
1350
1404
|
return False
|
1351
1405
|
|
1352
1406
|
def change_directory(
|
@@ -1356,16 +1410,19 @@ def change_directory(
|
|
1356
1410
|
"""改变目录"""
|
1357
1411
|
try:
|
1358
1412
|
directory = str(directory) if v_true(directory, str, debug=debug) else next
|
1359
|
-
|
1413
|
+
if v_true(debug, bool):
|
1414
|
+
logger.info(f"directory: {directory}")
|
1360
1415
|
if check_file_type(directory, 'dir', debug=debug):
|
1361
|
-
|
1416
|
+
if v_true(debug, bool):
|
1417
|
+
logger.info(f"change directory to {directory}")
|
1362
1418
|
os.chdir(directory)
|
1363
1419
|
return True
|
1364
|
-
|
1365
|
-
logger.error(f"no such directory: {directory}")
|
1366
|
-
|
1420
|
+
if v_true(debug, bool):
|
1421
|
+
logger.error(f"no such directory: {directory}")
|
1422
|
+
return False
|
1367
1423
|
except Exception as e:
|
1368
|
-
|
1424
|
+
if v_true(debug, bool):
|
1425
|
+
logger.exception(e)
|
1369
1426
|
return False
|
1370
1427
|
|
1371
1428
|
|
@@ -1376,16 +1433,20 @@ def load_toml_file(
|
|
1376
1433
|
file: str = None,
|
1377
1434
|
debug: bool = False
|
1378
1435
|
) -> dict | None:
|
1436
|
+
"""Load TOML file"""
|
1379
1437
|
try:
|
1380
1438
|
info = '解析配置文件'
|
1381
|
-
|
1439
|
+
if v_true(debug, bool):
|
1440
|
+
logger.info(f'{info}[执行]')
|
1382
1441
|
with open(file, "rb") as _file:
|
1383
1442
|
config = toml_load(_file)
|
1384
|
-
|
1443
|
+
if v_true(debug, bool):
|
1444
|
+
logger.success(f'{info}[成功]')
|
1385
1445
|
return config
|
1386
1446
|
except Exception as e:
|
1387
|
-
|
1388
|
-
|
1447
|
+
if v_true(debug, bool):
|
1448
|
+
logger.error(f'{info}[失败]')
|
1449
|
+
logger.exception(e)
|
1389
1450
|
return None
|
1390
1451
|
|
1391
1452
|
|
@@ -1397,6 +1458,7 @@ def git_clone(
|
|
1397
1458
|
log_prefix: str = '',
|
1398
1459
|
debug: bool = False,
|
1399
1460
|
) -> bool:
|
1461
|
+
"""GIT Clone"""
|
1400
1462
|
try:
|
1401
1463
|
|
1402
1464
|
# 日志前缀
|
@@ -1428,21 +1490,17 @@ def git_clone(
|
|
1428
1490
|
)
|
1429
1491
|
result_code = result.returncode
|
1430
1492
|
result_info = result.stdout.splitlines()
|
1493
|
+
if v_true(debug, bool):
|
1494
|
+
logger.error(f'{log_prefix}unsuccessful')
|
1495
|
+
for i in result_info:
|
1496
|
+
logger.error(f'{log_prefix}{i}')
|
1431
1497
|
if result_code == 0:
|
1432
|
-
logger.success(f'{log_prefix}successful') if v_true(debug, bool) else next
|
1433
|
-
if v_true(debug, bool):
|
1434
|
-
for i in result_info:
|
1435
|
-
logger.success(f'{log_prefix}{i}') if v_true(debug, bool) else next
|
1436
1498
|
return True
|
1437
|
-
|
1438
|
-
logger.error(f'{log_prefix}unsuccessful') if v_true(debug, bool) else next
|
1439
|
-
if v_true(debug, bool):
|
1440
|
-
for i in result_info:
|
1441
|
-
logger.error(f'{log_prefix}{i}') if v_true(debug, bool) else next
|
1442
|
-
return False
|
1499
|
+
return False
|
1443
1500
|
except Exception as e:
|
1444
|
-
|
1445
|
-
|
1501
|
+
if v_true(debug, bool):
|
1502
|
+
logger.error(f'{log_prefix}unsuccessful')
|
1503
|
+
logger.exception(e)
|
1446
1504
|
return False
|
1447
1505
|
|
1448
1506
|
|
@@ -1451,18 +1509,20 @@ def url_parse(
|
|
1451
1509
|
scheme: str = 'http',
|
1452
1510
|
debug: bool = False
|
1453
1511
|
) -> ParseResult:
|
1512
|
+
"""URL Parse"""
|
1454
1513
|
none_result = ParseResult(scheme='', netloc='', path='', params='', query='', fragment='')
|
1455
1514
|
try:
|
1456
|
-
|
1515
|
+
if v_true(debug, bool):
|
1516
|
+
logger.info(f'url: {url}')
|
1457
1517
|
# 如果没有 scheme 的话, 字符串是不解析的. 所以, 如果没有 scheme, 就添加一个 scheme, 默认添加 http
|
1458
1518
|
if v_true(url, str) and (url.find('://') == -1) and v_true(scheme, str):
|
1459
1519
|
url = f'{scheme}://{url}'
|
1460
1520
|
if v_true(url, str):
|
1461
1521
|
return urlparse(url)
|
1462
|
-
|
1463
|
-
return none_result
|
1522
|
+
return none_result
|
1464
1523
|
except Exception as e:
|
1465
|
-
|
1524
|
+
if v_true(debug, bool):
|
1525
|
+
logger.exception(e)
|
1466
1526
|
return none_result
|
1467
1527
|
|
1468
1528
|
# def debug_log(
|