pyxllib 0.0.43__py3-none-any.whl → 0.3.197__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.
- pyxllib/__init__.py +9 -2
- pyxllib/algo/__init__.py +8 -0
- pyxllib/algo/disjoint.py +54 -0
- pyxllib/algo/geo.py +541 -0
- pyxllib/{util/mathlib.py → algo/intervals.py} +172 -36
- pyxllib/algo/matcher.py +389 -0
- pyxllib/algo/newbie.py +166 -0
- pyxllib/algo/pupil.py +629 -0
- pyxllib/algo/shapelylib.py +67 -0
- pyxllib/algo/specialist.py +241 -0
- pyxllib/algo/stat.py +494 -0
- pyxllib/algo/treelib.py +149 -0
- pyxllib/algo/unitlib.py +66 -0
- pyxllib/autogui/__init__.py +5 -0
- pyxllib/autogui/activewin.py +246 -0
- pyxllib/autogui/all.py +9 -0
- pyxllib/autogui/autogui.py +852 -0
- pyxllib/autogui/uiautolib.py +362 -0
- pyxllib/autogui/virtualkey.py +102 -0
- pyxllib/autogui/wechat.py +827 -0
- pyxllib/autogui/wechat_msg.py +421 -0
- pyxllib/autogui/wxautolib.py +84 -0
- pyxllib/cv/__init__.py +1 -11
- pyxllib/cv/expert.py +267 -0
- pyxllib/cv/{imlib.py → imfile.py} +18 -83
- pyxllib/cv/imhash.py +39 -0
- pyxllib/cv/pupil.py +9 -0
- pyxllib/cv/rgbfmt.py +1525 -0
- pyxllib/cv/slidercaptcha.py +137 -0
- pyxllib/cv/trackbartools.py +163 -49
- pyxllib/cv/xlcvlib.py +1040 -0
- pyxllib/cv/xlpillib.py +423 -0
- pyxllib/data/__init__.py +0 -0
- pyxllib/data/echarts.py +240 -0
- pyxllib/data/jsonlib.py +89 -0
- pyxllib/{util/oss2_.py → data/oss.py} +11 -9
- pyxllib/data/pglib.py +1127 -0
- pyxllib/data/sqlite.py +568 -0
- pyxllib/{util → data}/sqllib.py +13 -31
- pyxllib/ext/JLineViewer.py +505 -0
- pyxllib/ext/__init__.py +6 -0
- pyxllib/{util → ext}/demolib.py +119 -35
- pyxllib/ext/drissionlib.py +277 -0
- pyxllib/ext/kq5034lib.py +12 -0
- pyxllib/{util/main.py → ext/old.py} +122 -284
- pyxllib/ext/qt.py +449 -0
- pyxllib/ext/robustprocfile.py +497 -0
- pyxllib/ext/seleniumlib.py +76 -0
- pyxllib/{util/tklib.py → ext/tk.py} +10 -11
- pyxllib/ext/unixlib.py +827 -0
- pyxllib/ext/utools.py +351 -0
- pyxllib/{util/webhooklib.py → ext/webhook.py} +45 -17
- pyxllib/ext/win32lib.py +40 -0
- pyxllib/ext/wjxlib.py +88 -0
- pyxllib/ext/wpsapi.py +124 -0
- pyxllib/ext/xlwork.py +9 -0
- pyxllib/ext/yuquelib.py +1105 -0
- pyxllib/file/__init__.py +17 -0
- pyxllib/file/docxlib.py +761 -0
- pyxllib/{util → file}/gitlib.py +40 -27
- pyxllib/file/libreoffice.py +165 -0
- pyxllib/file/movielib.py +148 -0
- pyxllib/file/newbie.py +10 -0
- pyxllib/file/onenotelib.py +1469 -0
- pyxllib/file/packlib/__init__.py +330 -0
- pyxllib/{util → file/packlib}/zipfile.py +598 -195
- pyxllib/file/pdflib.py +426 -0
- pyxllib/file/pupil.py +185 -0
- pyxllib/file/specialist/__init__.py +685 -0
- pyxllib/{basic/_5_dirlib.py → file/specialist/dirlib.py} +364 -93
- pyxllib/file/specialist/download.py +193 -0
- pyxllib/file/specialist/filelib.py +2829 -0
- pyxllib/file/xlsxlib.py +3131 -0
- pyxllib/file/xlsyncfile.py +341 -0
- pyxllib/prog/__init__.py +5 -0
- pyxllib/prog/cachetools.py +64 -0
- pyxllib/prog/deprecatedlib.py +233 -0
- pyxllib/prog/filelock.py +42 -0
- pyxllib/prog/ipyexec.py +253 -0
- pyxllib/prog/multiprogs.py +940 -0
- pyxllib/prog/newbie.py +451 -0
- pyxllib/prog/pupil.py +1197 -0
- pyxllib/{sitepackages.py → prog/sitepackages.py} +5 -3
- pyxllib/prog/specialist/__init__.py +391 -0
- pyxllib/prog/specialist/bc.py +203 -0
- pyxllib/prog/specialist/browser.py +497 -0
- pyxllib/prog/specialist/common.py +347 -0
- pyxllib/prog/specialist/datetime.py +199 -0
- pyxllib/prog/specialist/tictoc.py +240 -0
- pyxllib/prog/specialist/xllog.py +180 -0
- pyxllib/prog/xlosenv.py +108 -0
- pyxllib/stdlib/__init__.py +17 -0
- pyxllib/{util → stdlib}/tablepyxl/__init__.py +1 -3
- pyxllib/{util → stdlib}/tablepyxl/style.py +1 -1
- pyxllib/{util → stdlib}/tablepyxl/tablepyxl.py +2 -4
- pyxllib/text/__init__.py +8 -0
- pyxllib/text/ahocorasick.py +39 -0
- pyxllib/text/airscript.js +744 -0
- pyxllib/text/charclasslib.py +121 -0
- pyxllib/text/jiebalib.py +267 -0
- pyxllib/text/jinjalib.py +32 -0
- pyxllib/text/jsa_ai_prompt.md +271 -0
- pyxllib/text/jscode.py +922 -0
- pyxllib/text/latex/__init__.py +158 -0
- pyxllib/text/levenshtein.py +303 -0
- pyxllib/text/nestenv.py +1215 -0
- pyxllib/text/newbie.py +300 -0
- pyxllib/text/pupil/__init__.py +8 -0
- pyxllib/text/pupil/common.py +1121 -0
- pyxllib/text/pupil/xlalign.py +326 -0
- pyxllib/text/pycode.py +47 -0
- pyxllib/text/specialist/__init__.py +8 -0
- pyxllib/text/specialist/common.py +112 -0
- pyxllib/text/specialist/ptag.py +186 -0
- pyxllib/text/spellchecker.py +172 -0
- pyxllib/text/templates/echart_base.html +11 -0
- pyxllib/text/templates/highlight_code.html +17 -0
- pyxllib/text/templates/latex_editor.html +103 -0
- pyxllib/text/vbacode.py +17 -0
- pyxllib/text/xmllib.py +747 -0
- pyxllib/xl.py +39 -0
- pyxllib/xlcv.py +17 -0
- pyxllib-0.3.197.dist-info/METADATA +48 -0
- pyxllib-0.3.197.dist-info/RECORD +126 -0
- {pyxllib-0.0.43.dist-info → pyxllib-0.3.197.dist-info}/WHEEL +4 -5
- pyxllib/basic/_1_strlib.py +0 -945
- pyxllib/basic/_2_timelib.py +0 -488
- pyxllib/basic/_3_pathlib.py +0 -916
- pyxllib/basic/_4_loglib.py +0 -419
- pyxllib/basic/__init__.py +0 -54
- pyxllib/basic/arrow_.py +0 -250
- pyxllib/basic/chardet_.py +0 -66
- pyxllib/basic/dirlib.py +0 -529
- pyxllib/basic/dprint.py +0 -202
- pyxllib/basic/extension.py +0 -12
- pyxllib/basic/judge.py +0 -31
- pyxllib/basic/log.py +0 -204
- pyxllib/basic/pathlib_.py +0 -705
- pyxllib/basic/pytictoc.py +0 -102
- pyxllib/basic/qiniu_.py +0 -61
- pyxllib/basic/strlib.py +0 -761
- pyxllib/basic/timer.py +0 -132
- pyxllib/cv/cv.py +0 -834
- pyxllib/cv/cvlib/_1_geo.py +0 -543
- pyxllib/cv/cvlib/_2_cvprcs.py +0 -309
- pyxllib/cv/cvlib/_2_imgproc.py +0 -594
- pyxllib/cv/cvlib/_3_pilprcs.py +0 -80
- pyxllib/cv/cvlib/_4_cvimg.py +0 -211
- pyxllib/cv/cvlib/__init__.py +0 -10
- pyxllib/cv/debugtools.py +0 -82
- pyxllib/cv/fitz_.py +0 -300
- pyxllib/cv/installer.py +0 -42
- pyxllib/debug/_0_installer.py +0 -38
- pyxllib/debug/_1_typelib.py +0 -277
- pyxllib/debug/_2_chrome.py +0 -198
- pyxllib/debug/_3_showdir.py +0 -161
- pyxllib/debug/_4_bcompare.py +0 -140
- pyxllib/debug/__init__.py +0 -49
- pyxllib/debug/bcompare.py +0 -132
- pyxllib/debug/chrome.py +0 -198
- pyxllib/debug/installer.py +0 -38
- pyxllib/debug/showdir.py +0 -158
- pyxllib/debug/typelib.py +0 -278
- pyxllib/image/__init__.py +0 -12
- pyxllib/torch/__init__.py +0 -20
- pyxllib/torch/modellib.py +0 -37
- pyxllib/torch/trainlib.py +0 -344
- pyxllib/util/__init__.py +0 -20
- pyxllib/util/aip_.py +0 -141
- pyxllib/util/casiadb.py +0 -59
- pyxllib/util/excellib.py +0 -495
- pyxllib/util/filelib.py +0 -612
- pyxllib/util/jsondata.py +0 -27
- pyxllib/util/jsondata2.py +0 -92
- pyxllib/util/labelmelib.py +0 -139
- pyxllib/util/onepy/__init__.py +0 -29
- pyxllib/util/onepy/onepy.py +0 -574
- pyxllib/util/onepy/onmanager.py +0 -170
- pyxllib/util/pyautogui_.py +0 -219
- pyxllib/util/textlib.py +0 -1305
- pyxllib/util/unorder.py +0 -22
- pyxllib/util/xmllib.py +0 -639
- pyxllib-0.0.43.dist-info/METADATA +0 -39
- pyxllib-0.0.43.dist-info/RECORD +0 -80
- pyxllib-0.0.43.dist-info/top_level.txt +0 -1
- {pyxllib-0.0.43.dist-info → pyxllib-0.3.197.dist-info/licenses}/LICENSE +0 -0
pyxllib/basic/_4_loglib.py
DELETED
@@ -1,419 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
# -*- coding: utf-8 -*-
|
3
|
-
# @Author : 陈坤泽
|
4
|
-
# @Email : 877362867@qq.com
|
5
|
-
# @Data : 2020/09/18 22:16
|
6
|
-
|
7
|
-
import concurrent.futures
|
8
|
-
import inspect
|
9
|
-
import logging
|
10
|
-
import math
|
11
|
-
import os
|
12
|
-
import queue
|
13
|
-
import sys
|
14
|
-
import traceback
|
15
|
-
|
16
|
-
# https://pypi.org/project/verboselogs/
|
17
|
-
# import verboselogs
|
18
|
-
# verboselogs.install()
|
19
|
-
|
20
|
-
from pyxllib.basic._1_strlib import shorten
|
21
|
-
from pyxllib.basic._3_pathlib import Path
|
22
|
-
|
23
|
-
XLLOG_CONF_FILE = 'xllog.yaml'
|
24
|
-
|
25
|
-
____dprint = """
|
26
|
-
调试相关功能
|
27
|
-
|
28
|
-
TODO 高亮格式?
|
29
|
-
"""
|
30
|
-
|
31
|
-
|
32
|
-
def typename(c):
|
33
|
-
"""简化输出的type类型
|
34
|
-
>>> typename(123)
|
35
|
-
'int'
|
36
|
-
"""
|
37
|
-
return str(type(c))[8:-2]
|
38
|
-
|
39
|
-
|
40
|
-
def func_input_message(depth=2) -> dict:
|
41
|
-
"""假设调用了这个函数的函数叫做f,这个函数会获得
|
42
|
-
调用f的时候输入的参数信息,返回一个dict,键值对为
|
43
|
-
fullfilename:完整文件名
|
44
|
-
filename:文件名
|
45
|
-
funcname:所在函数名
|
46
|
-
lineno:代码所在行号
|
47
|
-
comment:尾巴的注释
|
48
|
-
depth:深度
|
49
|
-
funcnames:整个调用过程的函数名,用/隔开,例如...
|
50
|
-
|
51
|
-
argnames:变量名(list),这里的变量名也有可能是一个表达式
|
52
|
-
types:变量类型(list),如果是表达式,类型指表达式的运算结果类型
|
53
|
-
argvals:变量值(list)
|
54
|
-
|
55
|
-
这样以后要加新的键值对也很方便
|
56
|
-
|
57
|
-
:param depth: 需要分析的层级
|
58
|
-
0,当前func_input_message函数的参数输入情况
|
59
|
-
1,调用func_input_message的函数 f 参数输入情况
|
60
|
-
2,调用 f 的函数 g ,g的参数输入情况
|
61
|
-
|
62
|
-
参考: func_input_message 的具体使用方法可以参考 dformat 函数
|
63
|
-
细节:inspect可以获得函数签名,也可以获得一个函数各个参数的输入值,但我想要展现的是原始表达式,
|
64
|
-
例如func(a),以func(1+2)调用,inpect只能获得“a=3”,但我想要的是“1+2=3”的效果
|
65
|
-
"""
|
66
|
-
res = {}
|
67
|
-
# 1 找出调用函数的代码
|
68
|
-
ss = inspect.stack()
|
69
|
-
frameinfo = ss[depth]
|
70
|
-
arginfo = inspect.getargvalues(ss[depth - 1][0])
|
71
|
-
if arginfo.varargs:
|
72
|
-
origin_args = arginfo.locals[arginfo.varargs]
|
73
|
-
else:
|
74
|
-
origin_args = list(map(lambda x: arginfo.locals[x], arginfo.args))
|
75
|
-
|
76
|
-
res['fullfilename'] = frameinfo.filename
|
77
|
-
res['filename'] = os.path.basename(frameinfo.filename)
|
78
|
-
res['funcname'] = frameinfo.function
|
79
|
-
res['lineno'] = frameinfo.lineno
|
80
|
-
res['depth'] = len(ss)
|
81
|
-
ls_ = list(map(lambda x: x.function, ss))
|
82
|
-
# ls.reverse()
|
83
|
-
res['funcnames'] = '/'.join(ls_)
|
84
|
-
|
85
|
-
if frameinfo.code_context:
|
86
|
-
code_line = frameinfo.code_context[0].strip()
|
87
|
-
else: # 命令模式无法获得代码,是一个None对象
|
88
|
-
code_line = ''
|
89
|
-
|
90
|
-
funcname = ss[depth - 1].function # 调用的函数名
|
91
|
-
# 这一行代码不一定是从“funcname(”开始,所以要用find找到开始位置
|
92
|
-
code = code_line[code_line.find(funcname + '(') + len(funcname):]
|
93
|
-
|
94
|
-
# 2 先找到函数的()中参数列表,需要以')'作为分隔符分析
|
95
|
-
# TODO 可以考虑用ast重实现
|
96
|
-
ls = code.split(')')
|
97
|
-
logo, i = True, 1
|
98
|
-
while logo and i <= len(ls):
|
99
|
-
# 先将'='做特殊处理,防止字典类参数导致的语法错误
|
100
|
-
s = ')'.join(ls[:i]).replace('=', '+') + ')'
|
101
|
-
try:
|
102
|
-
compile(s, '<string>', 'single')
|
103
|
-
except SyntaxError:
|
104
|
-
i += 1
|
105
|
-
else: # 正常情况
|
106
|
-
logo = False
|
107
|
-
code = ')'.join(ls[:i])[1:]
|
108
|
-
|
109
|
-
# 3 获得注释
|
110
|
-
# 这个注释实现的不是很完美,不过影响应该不大,还没有想到比较完美的解决方案
|
111
|
-
t = ')'.join(ls[i:])
|
112
|
-
comment = t[t.find('#'):] if '#' in t else ''
|
113
|
-
res['comment'] = comment
|
114
|
-
|
115
|
-
# 4 获得变量名
|
116
|
-
ls = code.split(',')
|
117
|
-
n = len(ls)
|
118
|
-
argnames = list()
|
119
|
-
i, j = 0, 1
|
120
|
-
while j <= n:
|
121
|
-
s = ','.join(ls[i:j])
|
122
|
-
try:
|
123
|
-
compile(s.lstrip(), '<string>', 'single')
|
124
|
-
except SyntaxError:
|
125
|
-
j += 1
|
126
|
-
else: # 没有错误的时候执行
|
127
|
-
argnames.append(s.strip())
|
128
|
-
i = j
|
129
|
-
j = i + 1
|
130
|
-
|
131
|
-
# 5 获得变量值和类型
|
132
|
-
res['argvals'] = origin_args
|
133
|
-
res['types'] = list(map(typename, origin_args))
|
134
|
-
|
135
|
-
if not argnames: # 如果在命令行环境下调用,argnames会有空,需要根据argvals长度置空名称
|
136
|
-
argnames = [''] * len(res['argvals'])
|
137
|
-
res['argnames'] = argnames
|
138
|
-
|
139
|
-
return res
|
140
|
-
|
141
|
-
|
142
|
-
def dformat(*args, depth=2,
|
143
|
-
delimiter=' ' * 4,
|
144
|
-
strfunc=repr,
|
145
|
-
fmt='[{depth:02}]{filename}/{lineno}: {argmsg} {comment}',
|
146
|
-
subfmt='{name}<{tp}>={val}'):
|
147
|
-
r"""
|
148
|
-
:param args: 需要检查的表达式
|
149
|
-
这里看似没有调用,其实在func_input_message用inspect会提取到args的信息
|
150
|
-
:param depth: 处理对象
|
151
|
-
默认值2,即处理dformat本身
|
152
|
-
2以下值没意义
|
153
|
-
2以上的值,可以不传入args参数
|
154
|
-
:param delimiter: 每个变量值展示之间的分界
|
155
|
-
:param strfunc: 对每个变量值的文本化方法,常见的有repr、str
|
156
|
-
:param fmt: 展示格式,除了func_input_message中的关键字,新增
|
157
|
-
argmsg:所有的「变量名=变量值」,或所有的「变量名<变量类型>=变量值」,或自定义格式,采用delimiter作为分界符
|
158
|
-
旧版还用过这种格式: '{filename}/{funcname}/{lineno}: {argmsg} {comment}'
|
159
|
-
:param subfmt: 自定义每个变量值对的显示形式
|
160
|
-
name,变量名
|
161
|
-
val,变量值
|
162
|
-
tp,变量类型
|
163
|
-
:return: 返回格式化好的文本字符串
|
164
|
-
"""
|
165
|
-
res = func_input_message(depth)
|
166
|
-
ls = [subfmt.format(name=name, val=strfunc(val), tp=tp)
|
167
|
-
for name, val, tp in zip(res['argnames'], res['argvals'], res['types'])]
|
168
|
-
res['argmsg'] = delimiter.join(ls)
|
169
|
-
return fmt.format(**res)
|
170
|
-
|
171
|
-
|
172
|
-
def dprint(*args, **kwargs):
|
173
|
-
r"""
|
174
|
-
# 故意写的特别复杂,测试在极端情况下是否能正确解析出表达式
|
175
|
-
>> a, b = 1, 2
|
176
|
-
>> re.sub(str(dprint(1, b, a, "aa" + "bb)", "a[,ba\nbb""b", [2, 3])), '', '##') # 注释 # 注
|
177
|
-
[08]<doctest debuglib.dprint[1]>/1: 1<int>=1 b<int>=2 a<int>=1 "aa" + "bb)"<str>='aabb)' "a[,ba\nbb""b"<str>='a[,ba\nbbb' [2, 3]<list>=[2, 3] ##') # 注释 # 注
|
178
|
-
'##'
|
179
|
-
"""
|
180
|
-
print(dformat(depth=3, **kwargs))
|
181
|
-
|
182
|
-
|
183
|
-
def demo_dprint():
|
184
|
-
"""这里演示dprint常用功能
|
185
|
-
"""
|
186
|
-
from ._2_timelib import TicToc
|
187
|
-
|
188
|
-
# 1 查看程序是否运行到某个位置
|
189
|
-
dprint()
|
190
|
-
# [05]dprint.py/169: 意思:这是堆栈的第5层,所运行的位置是 dprint.py文件的第169行
|
191
|
-
|
192
|
-
# 2 查看变量、表达式的 '<类型>' 和 ':值'
|
193
|
-
a, b, s = 1, 2, 'ab'
|
194
|
-
dprint(a, b, a ^ b, s * 2)
|
195
|
-
# [05]dprint.py/174: a<int>=1 b<int>=2 a ^ b<int>=3 s*2<str>='abab'
|
196
|
-
|
197
|
-
# 3 异常警告
|
198
|
-
b = 0
|
199
|
-
if b:
|
200
|
-
c = a / b
|
201
|
-
else:
|
202
|
-
c = 0
|
203
|
-
dprint(a, b, c) # b=0不能作为除数,c默认值暂按0处理
|
204
|
-
# [05]dprint.py/183: a<int>=1 b<int>=0 c<int>=0 # b=0不能作为除数,c默认值暂按0处理
|
205
|
-
|
206
|
-
# 4 如果想在其他地方使用dprint的格式内容,可以调底层dformat函数实现
|
207
|
-
with TicToc(dformat(fmt='[{depth:02}]{fullfilename}/{lineno}: {argmsg}')):
|
208
|
-
for _ in range(10 ** 7):
|
209
|
-
pass
|
210
|
-
# [04]D:\slns\pyxllib\pyxllib\debug\dprint.py/187: 0.173 秒.
|
211
|
-
|
212
|
-
|
213
|
-
____xllog = """
|
214
|
-
"""
|
215
|
-
|
216
|
-
|
217
|
-
def get_xllog():
|
218
|
-
""" 获得pyxllib库的日志类
|
219
|
-
|
220
|
-
由于日志类可能要读取yaml配置文件,需要使用Path类,所以实现代码先放在pathlib_.py
|
221
|
-
|
222
|
-
TODO 类似企业微信机器人的机制怎么设?或者如何配置出问题发邮件?
|
223
|
-
"""
|
224
|
-
import logging, coloredlogs
|
225
|
-
|
226
|
-
if 'pyxllib.xllog' in logging.root.manager.loggerDict:
|
227
|
-
# 1 判断xllog是否已存在,直接返回
|
228
|
-
pass
|
229
|
-
elif os.path.isfile(XLLOG_CONF_FILE):
|
230
|
-
# 2 若不存在,尝试在默认位置是否有自定义配置文件,读取配置文件来创建
|
231
|
-
import logging.config
|
232
|
-
data = Path(XLLOG_CONF_FILE).read()
|
233
|
-
if isinstance(data, dict):
|
234
|
-
# 推荐使用yaml的字典结构,格式更简洁清晰
|
235
|
-
logging.config.dictConfig(data)
|
236
|
-
else:
|
237
|
-
# 但是普通的conf配置文件也支持
|
238
|
-
logging.config.fileConfig(XLLOG_CONF_FILE)
|
239
|
-
else:
|
240
|
-
# 3 否则生成一个非常简易版的xllog
|
241
|
-
# TODO 不同级别能设不同的格式(颜色)?
|
242
|
-
xllog = logging.getLogger('pyxllib.xllog')
|
243
|
-
# xllog.setLevel(logging.DEBUG)
|
244
|
-
# ch = logging.StreamHandler()
|
245
|
-
# ch.setLevel(logging.DEBUG)
|
246
|
-
# ch.setFormatter(logging.Formatter('%(asctime)s %(message)s'))
|
247
|
-
# xllog.addHandler(ch)
|
248
|
-
coloredlogs.install(level='DEBUG', logger=xllog, fmt='%(asctime)s %(message)s')
|
249
|
-
return logging.getLogger('pyxllib.xllog')
|
250
|
-
|
251
|
-
|
252
|
-
def format_exception(e):
|
253
|
-
return ''.join(traceback.format_exception(etype=type(e), value=e, tb=e.__traceback__))
|
254
|
-
|
255
|
-
|
256
|
-
____iterate = """
|
257
|
-
"""
|
258
|
-
|
259
|
-
|
260
|
-
class EmptyPoolExecutor:
|
261
|
-
"""伪造一个类似concurrent.futures.ThreadPoolExecutor、ProcessPoolExecutor的接口类
|
262
|
-
用来检查多线程、多进程中的错误
|
263
|
-
|
264
|
-
即并行中不会直接报出每个线程的错误,只能串行执行才好检查
|
265
|
-
但是两种版本代码来回修改很麻烦,故设计此类,只需把
|
266
|
-
concurrent.futures.ThreadPoolExecutor 暂时改为 EmptyPoolExecutor 进行调试即可
|
267
|
-
"""
|
268
|
-
|
269
|
-
def __init__(self, *args, **kwargs):
|
270
|
-
"""参数并不需要实际处理,并没有真正并行,而是串行执行"""
|
271
|
-
self._work_queue = queue.Queue()
|
272
|
-
|
273
|
-
def submit(self, func, *args, **kwargs):
|
274
|
-
"""执行函数"""
|
275
|
-
func(*args, **kwargs)
|
276
|
-
|
277
|
-
def shutdown(self):
|
278
|
-
print('并行执行结束')
|
279
|
-
|
280
|
-
|
281
|
-
class Iterate:
|
282
|
-
""" 迭代器类,用来封装一些特定模式的for循环操作
|
283
|
-
|
284
|
-
TODO 双循环,需要内部两两对比的迭代功能
|
285
|
-
|
286
|
-
200920周日18:20,最初设计的时候,是提供run_pair、run_pair2的功能的
|
287
|
-
不过后来想想,这个其实就是排列组合,在itertools里有combinations, permutations可以代替
|
288
|
-
甚至有放回的组合也有combinations_with_replacement,我实在是不需要再这里写这些冗余的功能
|
289
|
-
所以就移除了
|
290
|
-
"""
|
291
|
-
|
292
|
-
def __init__(self, items):
|
293
|
-
# 没有总长度倒也能接受,关键是可能要用start、end切片,所以还是先转成tuple更方便操作
|
294
|
-
self.items = tuple(items)
|
295
|
-
self.n_items = len(self.items)
|
296
|
-
self.format_width = math.ceil(math.log10(self.n_items + 1))
|
297
|
-
self.xllog = get_xllog()
|
298
|
-
|
299
|
-
def _format_pinterval(self, pinterval=None):
|
300
|
-
if isinstance(pinterval, str) and pinterval.endswith('%'):
|
301
|
-
# 百分比的情况,重算出间隔元素数
|
302
|
-
return int(round(self.n_items * float(pinterval[:-1]) / 100))
|
303
|
-
else: # 其他格式暂不解析,按原格式处理
|
304
|
-
return pinterval
|
305
|
-
|
306
|
-
def _step1_check_number(self, pinterval, func):
|
307
|
-
if pinterval:
|
308
|
-
sys.stdout.flush() # 让逻辑在前的标准输出先print出来,但其实这句也不一定能让print及时输出的~~可能会被日志提前抢输出了
|
309
|
-
self.xllog.info(f"使用 {func} 处理 {self.n_items} 个数据 {shorten(str(self.items), 30)}")
|
310
|
-
|
311
|
-
def _step2_check_range(self, start, end):
|
312
|
-
if start:
|
313
|
-
self.xllog.info(f"使用start参数,只处理≥{start}的条目")
|
314
|
-
else:
|
315
|
-
start = 0
|
316
|
-
if end:
|
317
|
-
# 这里空格是为了对齐,别删
|
318
|
-
self.xllog.info(f"使用 end 参数,只处理<{end}的条目")
|
319
|
-
else:
|
320
|
-
end = len(self.items)
|
321
|
-
return start, end
|
322
|
-
|
323
|
-
def _step3_executor(self, pinterval, max_workers):
|
324
|
-
if max_workers == 1:
|
325
|
-
# workers=1,实际上并不用多线程,用一个假的多线程类代替,能大大提速
|
326
|
-
executor = EmptyPoolExecutor()
|
327
|
-
# executor = concurrent.futures.ThreadPoolExecutor(max_workers)
|
328
|
-
else:
|
329
|
-
executor = concurrent.futures.ThreadPoolExecutor(max_workers)
|
330
|
-
if pinterval:
|
331
|
-
self.xllog.info(f'多线程执行,当前迭代所用线程数:{executor._max_workers}')
|
332
|
-
return executor
|
333
|
-
|
334
|
-
def _step4_iter(self, i, pinterval, executor):
|
335
|
-
# 队列中没有新任务时,才放入新任务,这样能确保pinterval的输出能反应实时情况,而不是一下全部进入队列,把for循环跑完了
|
336
|
-
while executor._work_queue.qsize(): pass
|
337
|
-
if pinterval and (i or pinterval == 1) and i % pinterval == 0:
|
338
|
-
message = f' {self.items[i]}' if pinterval == 1 else ''
|
339
|
-
self.xllog.info(f'{i:{self.format_width}d}/{self.n_items}={i / self.n_items:6.2%}{message}')
|
340
|
-
|
341
|
-
def _step5_finish(self, pinterval, interrupt):
|
342
|
-
if not interrupt and pinterval:
|
343
|
-
self.xllog.info(f'{self.n_items / self.n_items:6.2%} 完成迭代')
|
344
|
-
sys.stderr.flush()
|
345
|
-
|
346
|
-
def run(self, func, start=0, end=None, pinterval=None, max_workers=1, interrupt=True):
|
347
|
-
"""
|
348
|
-
:param func: 对每个item执行的功能
|
349
|
-
:param start: 跳过<start的数据,只处理>=start编号以上
|
350
|
-
:param end: 只处理 < end 的数据
|
351
|
-
:param pinterval: 每隔多少条目输出进度日志,默认不输出进度日志(但是错误日志依然会输出)
|
352
|
-
支持按百分比进度显示,例如每20%,pinterval='20%',不过一些底层实现机制原因,会有些许误差
|
353
|
-
TODO 支持按指定时间间隔显示? 例如每15秒,pinterval='15s' 感觉这种功能太花哨了,没必要搞
|
354
|
-
:param max_workers: 默认线程数,默认1,即串行
|
355
|
-
:type max_workers: int, None
|
356
|
-
:param interrupt: 出现错误时是否中断,默认True会终止程序,否则只会输出错误日志
|
357
|
-
:return:
|
358
|
-
"""
|
359
|
-
|
360
|
-
# 1 统一的参数处理部分
|
361
|
-
pinterval = self._format_pinterval(pinterval)
|
362
|
-
self._step1_check_number(pinterval, func)
|
363
|
-
start, end = self._step2_check_range(start, end)
|
364
|
-
error = False
|
365
|
-
executor = self._step3_executor(pinterval, max_workers)
|
366
|
-
|
367
|
-
# 2 封装的子处理部分
|
368
|
-
def wrap_func(func, i):
|
369
|
-
nonlocal error
|
370
|
-
item = self.items[i]
|
371
|
-
try:
|
372
|
-
func(item)
|
373
|
-
except Exception as e:
|
374
|
-
error = True
|
375
|
-
self.xllog.error(f'💔idx={i}运行出错:{item}\n{format_exception(e)}')
|
376
|
-
|
377
|
-
# 3 执行迭代
|
378
|
-
for i in range(start, end):
|
379
|
-
self._step4_iter(i, pinterval, executor)
|
380
|
-
executor.submit(wrap_func, func, i)
|
381
|
-
if interrupt and error: break
|
382
|
-
executor.shutdown() # 必须等executor结束,error才是准确的
|
383
|
-
self._step5_finish(pinterval, interrupt and error)
|
384
|
-
|
385
|
-
|
386
|
-
class RunOnlyOnce:
|
387
|
-
""" 被装饰的函数,不同的参数输入形式,只会被执行一次,
|
388
|
-
|
389
|
-
重复执行时会从内存直接调用上次相同参数调用下的运行的结果
|
390
|
-
可以使用reset成员函数重置,下一次调用该函数时则会重新执行
|
391
|
-
|
392
|
-
文档:https://www.yuque.com/xlpr/pyxllib/RunOnlyOnce
|
393
|
-
|
394
|
-
使用好该装饰器,可能让一些动态规划dp、搜索问题变得更简洁,
|
395
|
-
以及一些配置文件操作,可以做到只读一遍
|
396
|
-
"""
|
397
|
-
|
398
|
-
def __init__(self, func, distinct_args=True):
|
399
|
-
"""
|
400
|
-
:param func: 封装的函数
|
401
|
-
:param distinct_args: 默认不同输入参数形式,都会保存一个结果
|
402
|
-
设为False,则不管何种参数形式,函数就真的只会保存第一次运行的结果
|
403
|
-
"""
|
404
|
-
self.func = func
|
405
|
-
self.distinct_args = distinct_args
|
406
|
-
self.results = {}
|
407
|
-
|
408
|
-
def __call__(self, *args, **kwargs):
|
409
|
-
tag = f'{args}{kwargs}' if self.distinct_args else ''
|
410
|
-
# TODO 思考更严谨,考虑了值类型的tag标记
|
411
|
-
# 目前的tag规则,是必要不充分条件。还可以使用id,则是充分不必要条件
|
412
|
-
# 能找到充要条件来做是最好的,不行的话,也应该用更严谨的充分条件来做
|
413
|
-
# TODO kwargs的顺序应该是没影响的,要去掉顺序干扰
|
414
|
-
if tag not in self.results:
|
415
|
-
self.results[tag] = self.func(*args, **kwargs)
|
416
|
-
return self.results[tag]
|
417
|
-
|
418
|
-
def reset(self):
|
419
|
-
self.results = {}
|
pyxllib/basic/__init__.py
DELETED
@@ -1,54 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
# -*- coding: utf-8 -*-
|
3
|
-
# @Author : 陈坤泽
|
4
|
-
# @Email : 877362867@qq.com
|
5
|
-
# @Data : 2020/08/14 21:52
|
6
|
-
|
7
|
-
|
8
|
-
"""
|
9
|
-
最基础常用的一些功能
|
10
|
-
|
11
|
-
basic中依赖的三方库有直接写到 requirements.txt 中
|
12
|
-
(其他debug、cv等库的依赖项都是等使用到了才加入)
|
13
|
-
|
14
|
-
且basic依赖的三方库,都确保是体积小
|
15
|
-
能快速pip install
|
16
|
-
及pyinstaller -F打包生成的exe也不大的库
|
17
|
-
"""
|
18
|
-
|
19
|
-
# 1 文本处理等一些基础杂项功能
|
20
|
-
from pyxllib.basic._1_strlib import *
|
21
|
-
# 2 时间相关工具
|
22
|
-
from pyxllib.basic._2_timelib import *
|
23
|
-
# 3 文件、路径工具
|
24
|
-
from pyxllib.basic._3_pathlib import *
|
25
|
-
# 4 调试工具,Iterate等一些高级通用功能
|
26
|
-
from pyxllib.basic._4_loglib import *
|
27
|
-
# 5 目录工具
|
28
|
-
from pyxllib.basic._5_dirlib import *
|
29
|
-
|
30
|
-
____other = """
|
31
|
-
"""
|
32
|
-
|
33
|
-
|
34
|
-
class SingletonForEveryInitArgs(type):
|
35
|
-
"""Python单例模式(Singleton)的N种实现 - 知乎: https://zhuanlan.zhihu.com/p/37534850
|
36
|
-
|
37
|
-
注意!注意!注意!重要的事说三遍!
|
38
|
-
我的单例类不是传统意义上的单例类。
|
39
|
-
传统意义的单例类,不管用怎样不同的初始化参数创建对象,永远都只有最初的那个对象类。
|
40
|
-
但是我的单例类,为每种不同的参数创建形式,都构造了一个对象。
|
41
|
-
"""
|
42
|
-
_instances = {}
|
43
|
-
|
44
|
-
def __call__(cls, *args, **kwargs):
|
45
|
-
tag = f'{cls}{args}{kwargs}' # id加上所有参数的影响来控制单例类
|
46
|
-
# 其实转字符串来判断是不太严谨的,有些类型字符串后的显示效果是一模一样的
|
47
|
-
# dprint(tag)
|
48
|
-
if tag not in cls._instances:
|
49
|
-
cls._instances[tag] = super(SingletonForEveryInitArgs, cls).__call__(*args, **kwargs)
|
50
|
-
return cls._instances[tag]
|
51
|
-
|
52
|
-
|
53
|
-
if __name__ == '__main__':
|
54
|
-
pass
|