pyxllib 0.3.96__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/algo/geo.py +12 -0
- pyxllib/algo/intervals.py +1 -1
- pyxllib/algo/matcher.py +78 -0
- pyxllib/algo/pupil.py +187 -19
- pyxllib/algo/specialist.py +2 -1
- pyxllib/algo/stat.py +38 -2
- {pyxlpr → pyxllib/autogui}/__init__.py +1 -1
- pyxllib/autogui/activewin.py +246 -0
- pyxllib/autogui/all.py +9 -0
- pyxllib/{ext/autogui → autogui}/autogui.py +40 -11
- pyxllib/autogui/uiautolib.py +362 -0
- pyxllib/autogui/wechat.py +827 -0
- pyxllib/autogui/wechat_msg.py +421 -0
- pyxllib/autogui/wxautolib.py +84 -0
- pyxllib/cv/slidercaptcha.py +137 -0
- pyxllib/data/echarts.py +123 -12
- pyxllib/data/jsonlib.py +89 -0
- pyxllib/data/pglib.py +514 -30
- pyxllib/data/sqlite.py +231 -4
- pyxllib/ext/JLineViewer.py +14 -1
- pyxllib/ext/drissionlib.py +277 -0
- pyxllib/ext/kq5034lib.py +0 -1594
- pyxllib/ext/robustprocfile.py +497 -0
- pyxllib/ext/unixlib.py +6 -5
- pyxllib/ext/utools.py +108 -95
- pyxllib/ext/webhook.py +32 -14
- pyxllib/ext/wjxlib.py +88 -0
- pyxllib/ext/wpsapi.py +124 -0
- pyxllib/ext/xlwork.py +9 -0
- pyxllib/ext/yuquelib.py +1003 -71
- pyxllib/file/docxlib.py +1 -1
- pyxllib/file/libreoffice.py +165 -0
- pyxllib/file/movielib.py +9 -0
- pyxllib/file/packlib/__init__.py +112 -75
- pyxllib/file/pdflib.py +1 -1
- pyxllib/file/pupil.py +1 -1
- pyxllib/file/specialist/dirlib.py +1 -1
- pyxllib/file/specialist/download.py +10 -3
- pyxllib/file/specialist/filelib.py +266 -55
- pyxllib/file/xlsxlib.py +205 -50
- pyxllib/file/xlsyncfile.py +341 -0
- pyxllib/prog/cachetools.py +64 -0
- pyxllib/prog/filelock.py +42 -0
- pyxllib/prog/multiprogs.py +940 -0
- pyxllib/prog/newbie.py +9 -2
- pyxllib/prog/pupil.py +129 -60
- pyxllib/prog/specialist/__init__.py +176 -2
- pyxllib/prog/specialist/bc.py +5 -2
- pyxllib/prog/specialist/browser.py +11 -2
- pyxllib/prog/specialist/datetime.py +68 -0
- pyxllib/prog/specialist/tictoc.py +12 -13
- pyxllib/prog/specialist/xllog.py +5 -5
- pyxllib/prog/xlosenv.py +7 -0
- pyxllib/text/airscript.js +744 -0
- pyxllib/text/charclasslib.py +17 -5
- pyxllib/text/jiebalib.py +6 -3
- pyxllib/text/jinjalib.py +32 -0
- pyxllib/text/jsa_ai_prompt.md +271 -0
- pyxllib/text/jscode.py +159 -4
- pyxllib/text/nestenv.py +1 -1
- pyxllib/text/newbie.py +12 -0
- pyxllib/text/pupil/common.py +26 -0
- pyxllib/text/specialist/ptag.py +2 -2
- 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/xmllib.py +76 -14
- pyxllib/xl.py +2 -1
- pyxllib-0.3.197.dist-info/METADATA +48 -0
- pyxllib-0.3.197.dist-info/RECORD +126 -0
- {pyxllib-0.3.96.dist-info → pyxllib-0.3.197.dist-info}/WHEEL +1 -2
- pyxllib/ext/autogui/__init__.py +0 -8
- pyxllib-0.3.96.dist-info/METADATA +0 -51
- pyxllib-0.3.96.dist-info/RECORD +0 -333
- pyxllib-0.3.96.dist-info/top_level.txt +0 -2
- pyxlpr/ai/__init__.py +0 -5
- pyxlpr/ai/clientlib.py +0 -1281
- pyxlpr/ai/specialist.py +0 -286
- pyxlpr/ai/torch_app.py +0 -172
- pyxlpr/ai/xlpaddle.py +0 -655
- pyxlpr/ai/xltorch.py +0 -705
- pyxlpr/data/__init__.py +0 -11
- pyxlpr/data/coco.py +0 -1325
- pyxlpr/data/datacls.py +0 -365
- pyxlpr/data/datasets.py +0 -200
- pyxlpr/data/gptlib.py +0 -1291
- pyxlpr/data/icdar/__init__.py +0 -96
- pyxlpr/data/icdar/deteval.py +0 -377
- pyxlpr/data/icdar/icdar2013.py +0 -341
- pyxlpr/data/icdar/iou.py +0 -340
- pyxlpr/data/icdar/rrc_evaluation_funcs_1_1.py +0 -463
- pyxlpr/data/imtextline.py +0 -473
- pyxlpr/data/labelme.py +0 -866
- pyxlpr/data/removeline.py +0 -179
- pyxlpr/data/specialist.py +0 -57
- pyxlpr/eval/__init__.py +0 -85
- pyxlpr/paddleocr.py +0 -776
- pyxlpr/ppocr/__init__.py +0 -15
- pyxlpr/ppocr/configs/rec/multi_language/generate_multi_language_configs.py +0 -226
- pyxlpr/ppocr/data/__init__.py +0 -135
- pyxlpr/ppocr/data/imaug/ColorJitter.py +0 -26
- pyxlpr/ppocr/data/imaug/__init__.py +0 -67
- pyxlpr/ppocr/data/imaug/copy_paste.py +0 -170
- pyxlpr/ppocr/data/imaug/east_process.py +0 -437
- pyxlpr/ppocr/data/imaug/gen_table_mask.py +0 -244
- pyxlpr/ppocr/data/imaug/iaa_augment.py +0 -114
- pyxlpr/ppocr/data/imaug/label_ops.py +0 -789
- pyxlpr/ppocr/data/imaug/make_border_map.py +0 -184
- pyxlpr/ppocr/data/imaug/make_pse_gt.py +0 -106
- pyxlpr/ppocr/data/imaug/make_shrink_map.py +0 -126
- pyxlpr/ppocr/data/imaug/operators.py +0 -433
- pyxlpr/ppocr/data/imaug/pg_process.py +0 -906
- pyxlpr/ppocr/data/imaug/randaugment.py +0 -143
- pyxlpr/ppocr/data/imaug/random_crop_data.py +0 -239
- pyxlpr/ppocr/data/imaug/rec_img_aug.py +0 -533
- pyxlpr/ppocr/data/imaug/sast_process.py +0 -777
- pyxlpr/ppocr/data/imaug/text_image_aug/__init__.py +0 -17
- pyxlpr/ppocr/data/imaug/text_image_aug/augment.py +0 -120
- pyxlpr/ppocr/data/imaug/text_image_aug/warp_mls.py +0 -168
- pyxlpr/ppocr/data/lmdb_dataset.py +0 -115
- pyxlpr/ppocr/data/pgnet_dataset.py +0 -104
- pyxlpr/ppocr/data/pubtab_dataset.py +0 -107
- pyxlpr/ppocr/data/simple_dataset.py +0 -372
- pyxlpr/ppocr/losses/__init__.py +0 -61
- pyxlpr/ppocr/losses/ace_loss.py +0 -52
- pyxlpr/ppocr/losses/basic_loss.py +0 -135
- pyxlpr/ppocr/losses/center_loss.py +0 -88
- pyxlpr/ppocr/losses/cls_loss.py +0 -30
- pyxlpr/ppocr/losses/combined_loss.py +0 -67
- pyxlpr/ppocr/losses/det_basic_loss.py +0 -208
- pyxlpr/ppocr/losses/det_db_loss.py +0 -80
- pyxlpr/ppocr/losses/det_east_loss.py +0 -63
- pyxlpr/ppocr/losses/det_pse_loss.py +0 -149
- pyxlpr/ppocr/losses/det_sast_loss.py +0 -121
- pyxlpr/ppocr/losses/distillation_loss.py +0 -272
- pyxlpr/ppocr/losses/e2e_pg_loss.py +0 -140
- pyxlpr/ppocr/losses/kie_sdmgr_loss.py +0 -113
- pyxlpr/ppocr/losses/rec_aster_loss.py +0 -99
- pyxlpr/ppocr/losses/rec_att_loss.py +0 -39
- pyxlpr/ppocr/losses/rec_ctc_loss.py +0 -44
- pyxlpr/ppocr/losses/rec_enhanced_ctc_loss.py +0 -70
- pyxlpr/ppocr/losses/rec_nrtr_loss.py +0 -30
- pyxlpr/ppocr/losses/rec_sar_loss.py +0 -28
- pyxlpr/ppocr/losses/rec_srn_loss.py +0 -47
- pyxlpr/ppocr/losses/table_att_loss.py +0 -109
- pyxlpr/ppocr/metrics/__init__.py +0 -44
- pyxlpr/ppocr/metrics/cls_metric.py +0 -45
- pyxlpr/ppocr/metrics/det_metric.py +0 -82
- pyxlpr/ppocr/metrics/distillation_metric.py +0 -73
- pyxlpr/ppocr/metrics/e2e_metric.py +0 -86
- pyxlpr/ppocr/metrics/eval_det_iou.py +0 -274
- pyxlpr/ppocr/metrics/kie_metric.py +0 -70
- pyxlpr/ppocr/metrics/rec_metric.py +0 -75
- pyxlpr/ppocr/metrics/table_metric.py +0 -50
- pyxlpr/ppocr/modeling/architectures/__init__.py +0 -32
- pyxlpr/ppocr/modeling/architectures/base_model.py +0 -88
- pyxlpr/ppocr/modeling/architectures/distillation_model.py +0 -60
- pyxlpr/ppocr/modeling/backbones/__init__.py +0 -54
- pyxlpr/ppocr/modeling/backbones/det_mobilenet_v3.py +0 -268
- pyxlpr/ppocr/modeling/backbones/det_resnet_vd.py +0 -246
- pyxlpr/ppocr/modeling/backbones/det_resnet_vd_sast.py +0 -285
- pyxlpr/ppocr/modeling/backbones/e2e_resnet_vd_pg.py +0 -265
- pyxlpr/ppocr/modeling/backbones/kie_unet_sdmgr.py +0 -186
- pyxlpr/ppocr/modeling/backbones/rec_mobilenet_v3.py +0 -138
- pyxlpr/ppocr/modeling/backbones/rec_mv1_enhance.py +0 -258
- pyxlpr/ppocr/modeling/backbones/rec_nrtr_mtb.py +0 -48
- pyxlpr/ppocr/modeling/backbones/rec_resnet_31.py +0 -210
- pyxlpr/ppocr/modeling/backbones/rec_resnet_aster.py +0 -143
- pyxlpr/ppocr/modeling/backbones/rec_resnet_fpn.py +0 -307
- pyxlpr/ppocr/modeling/backbones/rec_resnet_vd.py +0 -286
- pyxlpr/ppocr/modeling/heads/__init__.py +0 -54
- pyxlpr/ppocr/modeling/heads/cls_head.py +0 -52
- pyxlpr/ppocr/modeling/heads/det_db_head.py +0 -118
- pyxlpr/ppocr/modeling/heads/det_east_head.py +0 -121
- pyxlpr/ppocr/modeling/heads/det_pse_head.py +0 -37
- pyxlpr/ppocr/modeling/heads/det_sast_head.py +0 -128
- pyxlpr/ppocr/modeling/heads/e2e_pg_head.py +0 -253
- pyxlpr/ppocr/modeling/heads/kie_sdmgr_head.py +0 -206
- pyxlpr/ppocr/modeling/heads/multiheadAttention.py +0 -163
- pyxlpr/ppocr/modeling/heads/rec_aster_head.py +0 -393
- pyxlpr/ppocr/modeling/heads/rec_att_head.py +0 -202
- pyxlpr/ppocr/modeling/heads/rec_ctc_head.py +0 -88
- pyxlpr/ppocr/modeling/heads/rec_nrtr_head.py +0 -826
- pyxlpr/ppocr/modeling/heads/rec_sar_head.py +0 -402
- pyxlpr/ppocr/modeling/heads/rec_srn_head.py +0 -280
- pyxlpr/ppocr/modeling/heads/self_attention.py +0 -406
- pyxlpr/ppocr/modeling/heads/table_att_head.py +0 -246
- pyxlpr/ppocr/modeling/necks/__init__.py +0 -32
- pyxlpr/ppocr/modeling/necks/db_fpn.py +0 -111
- pyxlpr/ppocr/modeling/necks/east_fpn.py +0 -188
- pyxlpr/ppocr/modeling/necks/fpn.py +0 -138
- pyxlpr/ppocr/modeling/necks/pg_fpn.py +0 -314
- pyxlpr/ppocr/modeling/necks/rnn.py +0 -92
- pyxlpr/ppocr/modeling/necks/sast_fpn.py +0 -284
- pyxlpr/ppocr/modeling/necks/table_fpn.py +0 -110
- pyxlpr/ppocr/modeling/transforms/__init__.py +0 -28
- pyxlpr/ppocr/modeling/transforms/stn.py +0 -135
- pyxlpr/ppocr/modeling/transforms/tps.py +0 -308
- pyxlpr/ppocr/modeling/transforms/tps_spatial_transformer.py +0 -156
- pyxlpr/ppocr/optimizer/__init__.py +0 -61
- pyxlpr/ppocr/optimizer/learning_rate.py +0 -228
- pyxlpr/ppocr/optimizer/lr_scheduler.py +0 -49
- pyxlpr/ppocr/optimizer/optimizer.py +0 -160
- pyxlpr/ppocr/optimizer/regularizer.py +0 -52
- pyxlpr/ppocr/postprocess/__init__.py +0 -55
- pyxlpr/ppocr/postprocess/cls_postprocess.py +0 -33
- pyxlpr/ppocr/postprocess/db_postprocess.py +0 -234
- pyxlpr/ppocr/postprocess/east_postprocess.py +0 -143
- pyxlpr/ppocr/postprocess/locality_aware_nms.py +0 -200
- pyxlpr/ppocr/postprocess/pg_postprocess.py +0 -52
- pyxlpr/ppocr/postprocess/pse_postprocess/__init__.py +0 -15
- pyxlpr/ppocr/postprocess/pse_postprocess/pse/__init__.py +0 -29
- pyxlpr/ppocr/postprocess/pse_postprocess/pse/setup.py +0 -14
- pyxlpr/ppocr/postprocess/pse_postprocess/pse_postprocess.py +0 -118
- pyxlpr/ppocr/postprocess/rec_postprocess.py +0 -654
- pyxlpr/ppocr/postprocess/sast_postprocess.py +0 -355
- pyxlpr/ppocr/tools/__init__.py +0 -14
- pyxlpr/ppocr/tools/eval.py +0 -83
- pyxlpr/ppocr/tools/export_center.py +0 -77
- pyxlpr/ppocr/tools/export_model.py +0 -129
- pyxlpr/ppocr/tools/infer/predict_cls.py +0 -151
- pyxlpr/ppocr/tools/infer/predict_det.py +0 -300
- pyxlpr/ppocr/tools/infer/predict_e2e.py +0 -169
- pyxlpr/ppocr/tools/infer/predict_rec.py +0 -414
- pyxlpr/ppocr/tools/infer/predict_system.py +0 -204
- pyxlpr/ppocr/tools/infer/utility.py +0 -629
- pyxlpr/ppocr/tools/infer_cls.py +0 -83
- pyxlpr/ppocr/tools/infer_det.py +0 -134
- pyxlpr/ppocr/tools/infer_e2e.py +0 -122
- pyxlpr/ppocr/tools/infer_kie.py +0 -153
- pyxlpr/ppocr/tools/infer_rec.py +0 -146
- pyxlpr/ppocr/tools/infer_table.py +0 -107
- pyxlpr/ppocr/tools/program.py +0 -596
- pyxlpr/ppocr/tools/test_hubserving.py +0 -117
- pyxlpr/ppocr/tools/train.py +0 -163
- pyxlpr/ppocr/tools/xlprog.py +0 -748
- pyxlpr/ppocr/utils/EN_symbol_dict.txt +0 -94
- pyxlpr/ppocr/utils/__init__.py +0 -24
- pyxlpr/ppocr/utils/dict/ar_dict.txt +0 -117
- pyxlpr/ppocr/utils/dict/arabic_dict.txt +0 -162
- pyxlpr/ppocr/utils/dict/be_dict.txt +0 -145
- pyxlpr/ppocr/utils/dict/bg_dict.txt +0 -140
- pyxlpr/ppocr/utils/dict/chinese_cht_dict.txt +0 -8421
- pyxlpr/ppocr/utils/dict/cyrillic_dict.txt +0 -163
- pyxlpr/ppocr/utils/dict/devanagari_dict.txt +0 -167
- pyxlpr/ppocr/utils/dict/en_dict.txt +0 -63
- pyxlpr/ppocr/utils/dict/fa_dict.txt +0 -136
- pyxlpr/ppocr/utils/dict/french_dict.txt +0 -136
- pyxlpr/ppocr/utils/dict/german_dict.txt +0 -143
- pyxlpr/ppocr/utils/dict/hi_dict.txt +0 -162
- pyxlpr/ppocr/utils/dict/it_dict.txt +0 -118
- pyxlpr/ppocr/utils/dict/japan_dict.txt +0 -4399
- pyxlpr/ppocr/utils/dict/ka_dict.txt +0 -153
- pyxlpr/ppocr/utils/dict/korean_dict.txt +0 -3688
- pyxlpr/ppocr/utils/dict/latin_dict.txt +0 -185
- pyxlpr/ppocr/utils/dict/mr_dict.txt +0 -153
- pyxlpr/ppocr/utils/dict/ne_dict.txt +0 -153
- pyxlpr/ppocr/utils/dict/oc_dict.txt +0 -96
- pyxlpr/ppocr/utils/dict/pu_dict.txt +0 -130
- pyxlpr/ppocr/utils/dict/rs_dict.txt +0 -91
- pyxlpr/ppocr/utils/dict/rsc_dict.txt +0 -134
- pyxlpr/ppocr/utils/dict/ru_dict.txt +0 -125
- pyxlpr/ppocr/utils/dict/ta_dict.txt +0 -128
- pyxlpr/ppocr/utils/dict/table_dict.txt +0 -277
- pyxlpr/ppocr/utils/dict/table_structure_dict.txt +0 -2759
- pyxlpr/ppocr/utils/dict/te_dict.txt +0 -151
- pyxlpr/ppocr/utils/dict/ug_dict.txt +0 -114
- pyxlpr/ppocr/utils/dict/uk_dict.txt +0 -142
- pyxlpr/ppocr/utils/dict/ur_dict.txt +0 -137
- pyxlpr/ppocr/utils/dict/xi_dict.txt +0 -110
- pyxlpr/ppocr/utils/dict90.txt +0 -90
- pyxlpr/ppocr/utils/e2e_metric/Deteval.py +0 -574
- pyxlpr/ppocr/utils/e2e_metric/polygon_fast.py +0 -83
- pyxlpr/ppocr/utils/e2e_utils/extract_batchsize.py +0 -87
- pyxlpr/ppocr/utils/e2e_utils/extract_textpoint_fast.py +0 -457
- pyxlpr/ppocr/utils/e2e_utils/extract_textpoint_slow.py +0 -592
- pyxlpr/ppocr/utils/e2e_utils/pgnet_pp_utils.py +0 -162
- pyxlpr/ppocr/utils/e2e_utils/visual.py +0 -162
- pyxlpr/ppocr/utils/en_dict.txt +0 -95
- pyxlpr/ppocr/utils/gen_label.py +0 -81
- pyxlpr/ppocr/utils/ic15_dict.txt +0 -36
- pyxlpr/ppocr/utils/iou.py +0 -54
- pyxlpr/ppocr/utils/logging.py +0 -69
- pyxlpr/ppocr/utils/network.py +0 -84
- pyxlpr/ppocr/utils/ppocr_keys_v1.txt +0 -6623
- pyxlpr/ppocr/utils/profiler.py +0 -110
- pyxlpr/ppocr/utils/save_load.py +0 -150
- pyxlpr/ppocr/utils/stats.py +0 -72
- pyxlpr/ppocr/utils/utility.py +0 -80
- pyxlpr/ppstructure/__init__.py +0 -13
- pyxlpr/ppstructure/predict_system.py +0 -187
- pyxlpr/ppstructure/table/__init__.py +0 -13
- pyxlpr/ppstructure/table/eval_table.py +0 -72
- pyxlpr/ppstructure/table/matcher.py +0 -192
- pyxlpr/ppstructure/table/predict_structure.py +0 -136
- pyxlpr/ppstructure/table/predict_table.py +0 -221
- pyxlpr/ppstructure/table/table_metric/__init__.py +0 -16
- pyxlpr/ppstructure/table/table_metric/parallel.py +0 -51
- pyxlpr/ppstructure/table/table_metric/table_metric.py +0 -247
- pyxlpr/ppstructure/table/tablepyxl/__init__.py +0 -13
- pyxlpr/ppstructure/table/tablepyxl/style.py +0 -283
- pyxlpr/ppstructure/table/tablepyxl/tablepyxl.py +0 -118
- pyxlpr/ppstructure/utility.py +0 -71
- pyxlpr/xlai.py +0 -10
- /pyxllib/{ext/autogui → autogui}/virtualkey.py +0 -0
- {pyxllib-0.3.96.dist-info → pyxllib-0.3.197.dist-info/licenses}/LICENSE +0 -0
pyxllib/prog/newbie.py
CHANGED
@@ -28,7 +28,7 @@ class SingletonForEveryClass(type):
|
|
28
28
|
|
29
29
|
|
30
30
|
class SingletonForEveryInitArgs(type):
|
31
|
-
"""Python单例模式(Singleton)的N种实现 - 知乎: https://zhuanlan.zhihu.com/p/37534850
|
31
|
+
""" Python单例模式(Singleton)的N种实现 - 知乎: https://zhuanlan.zhihu.com/p/37534850
|
32
32
|
|
33
33
|
注意!注意!注意!重要的事说三遍!
|
34
34
|
这里的单例类不是传统意义上的单例类。
|
@@ -259,6 +259,9 @@ def human_readable_number(value, base_type='K', precision=4):
|
|
259
259
|
:param str base_type: 进制类型,'K'为1000进制, 'KB'为1024进制(KiB同理), '万'为中文万进制
|
260
260
|
:return: 美化后的字符串
|
261
261
|
"""
|
262
|
+
if value is None:
|
263
|
+
return ''
|
264
|
+
|
262
265
|
if abs(value) < 1:
|
263
266
|
return f'{value:.{precision}g}'
|
264
267
|
|
@@ -267,13 +270,17 @@ def human_readable_number(value, base_type='K', precision=4):
|
|
267
270
|
'K': (['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'], 1000),
|
268
271
|
'KB': (['', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], 1024),
|
269
272
|
'KiB': (['', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'], 1024),
|
270
|
-
'万': (['', '万', '亿', '万亿', '亿亿'], 10000)
|
273
|
+
'万': (['', '万', '亿', '万亿', '亿亿'], 10000),
|
274
|
+
'秒': (['秒', [60, '分'], [60, '时'], [24, '天'], [7, '周'], [4.345, '月'], [12, '年']], 60),
|
271
275
|
}.get(base_type, ([''], 1)) # 默认为空单位和基数1
|
272
276
|
|
273
277
|
x, i = abs(value), 0
|
274
278
|
while x >= base and i < len(units) - 1:
|
275
279
|
x /= base
|
276
280
|
i += 1
|
281
|
+
if isinstance(units[i], list):
|
282
|
+
base = units[i][0]
|
283
|
+
units[i] = units[i][1]
|
277
284
|
|
278
285
|
x = f'{x:.{precision}g}' # 四舍五入到指定精度
|
279
286
|
prefix = '-' if value < 0 else '' # 负数处理
|
pyxllib/prog/pupil.py
CHANGED
@@ -6,10 +6,11 @@
|
|
6
6
|
|
7
7
|
|
8
8
|
""" 封装一些代码开发中常用的功能,工程组件 """
|
9
|
-
|
10
|
-
from collections import Counter
|
9
|
+
|
10
|
+
from collections import Counter
|
11
11
|
from concurrent.futures import ThreadPoolExecutor
|
12
|
-
import
|
12
|
+
from urllib.parse import urlparse
|
13
|
+
import builtins
|
13
14
|
import datetime
|
14
15
|
import functools
|
15
16
|
import hashlib
|
@@ -23,20 +24,22 @@ import os
|
|
23
24
|
import pprint
|
24
25
|
import queue
|
25
26
|
import random
|
27
|
+
import re
|
26
28
|
import signal
|
27
29
|
import socket
|
28
|
-
import socketserver
|
29
30
|
import subprocess
|
30
31
|
import sys
|
31
32
|
import tempfile
|
32
33
|
import threading
|
33
34
|
import time
|
34
35
|
import traceback
|
35
|
-
from urllib.parse import urlparse
|
36
36
|
|
37
37
|
from pyxllib.prog.newbie import classproperty, typename
|
38
38
|
|
39
39
|
|
40
|
+
# from loguru import logger
|
41
|
+
|
42
|
+
|
40
43
|
def system_information():
|
41
44
|
"""主要是测试一些系统变量值,顺便再演示一次Timer用法"""
|
42
45
|
|
@@ -554,6 +557,9 @@ class Timeout:
|
|
554
557
|
return wrapper
|
555
558
|
|
556
559
|
def __enter__(self):
|
560
|
+
if self.seconds == 0: # 可以设置0来关闭超时功能
|
561
|
+
return
|
562
|
+
|
557
563
|
def overtime(signum, frame):
|
558
564
|
raise TimeoutError(f'with 上下文代码块运行超时 > [{self.seconds} 秒]')
|
559
565
|
|
@@ -567,6 +573,9 @@ class Timeout:
|
|
567
573
|
self.alarm.start()
|
568
574
|
|
569
575
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
576
|
+
if self.seconds == 0:
|
577
|
+
return
|
578
|
+
|
570
579
|
# with已经运行完了,马上关闭警告器
|
571
580
|
self.alarm.cancel()
|
572
581
|
|
@@ -595,6 +604,7 @@ def inject_members(from_obj, to_obj, member_list=None, *,
|
|
595
604
|
# 把XlDocxTable的成员方法绑定到docx.table.Table里
|
596
605
|
>> inject_members(XlDocxTable, docx.table.Table)
|
597
606
|
|
607
|
+
240826周一,其他可参考学习的三方现成工具库:from fastcore.foundation import patch
|
598
608
|
"""
|
599
609
|
# 1 整理需要注入的方法清单
|
600
610
|
dst = set(dir(to_obj))
|
@@ -622,20 +632,6 @@ def inject_members(from_obj, to_obj, member_list=None, *,
|
|
622
632
|
logging.warning(f'Conflict of the same name! {to_obj}.{x}')
|
623
633
|
|
624
634
|
|
625
|
-
def find_free_ports(count=1):
|
626
|
-
""" 随机获得可用端口
|
627
|
-
|
628
|
-
:param count: 需要的端口数量(会保证给出的端口号不重复)
|
629
|
-
:return: list
|
630
|
-
"""
|
631
|
-
ports = set()
|
632
|
-
while len(ports) < count:
|
633
|
-
with socketserver.TCPServer(("localhost", 0), None) as s:
|
634
|
-
ports.add(s.server_address[1])
|
635
|
-
|
636
|
-
return list(ports)
|
637
|
-
|
638
|
-
|
639
635
|
def __debug系列():
|
640
636
|
pass
|
641
637
|
|
@@ -972,47 +968,6 @@ class OutputLogger(logging.Logger):
|
|
972
968
|
self.print(msg)
|
973
969
|
|
974
970
|
|
975
|
-
class MultiProcessLauncher:
|
976
|
-
""" 注意这个类暂时只有linux能使用 """
|
977
|
-
|
978
|
-
def __init__(self):
|
979
|
-
self.workers = []
|
980
|
-
|
981
|
-
def add_process(self, cmd, name=None, **kwargs):
|
982
|
-
if name is None:
|
983
|
-
name = cmd.split()[0]
|
984
|
-
|
985
|
-
p = subprocess.Popen(cmd.split(), preexec_fn=self._set_pdeathsig(signal.SIGTERM))
|
986
|
-
worker = {'name': name, 'process': p, 'command': cmd}
|
987
|
-
worker.update(kwargs)
|
988
|
-
self.workers.append(worker)
|
989
|
-
|
990
|
-
def add_python_module(self, module, args='', name=None):
|
991
|
-
""" 添加并启动一个Python模块作为后台进程。
|
992
|
-
|
993
|
-
:param module: 要执行的Python模块名(python -m 后面的部分)
|
994
|
-
:param args: 模块的参数
|
995
|
-
:param name: 进程的名称,默认为模块名
|
996
|
-
"""
|
997
|
-
cmd = f'{sys.executable} -m {module} {args}'
|
998
|
-
self.add_process(cmd, name=name)
|
999
|
-
|
1000
|
-
def stop_all(self):
|
1001
|
-
""" 停止所有后台进程 """
|
1002
|
-
for worker in self.workers:
|
1003
|
-
worker.process.terminate()
|
1004
|
-
self.workers = []
|
1005
|
-
|
1006
|
-
def _set_pdeathsig(self, sig=signal.SIGTERM):
|
1007
|
-
""" 在主服务退出时,这些进程也会全部自动关闭 """
|
1008
|
-
|
1009
|
-
def callable():
|
1010
|
-
libc = ctypes.CDLL("libc.so.6")
|
1011
|
-
return libc.prctl(1, sig)
|
1012
|
-
|
1013
|
-
return callable
|
1014
|
-
|
1015
|
-
|
1016
971
|
def xlmd5(content):
|
1017
972
|
if isinstance(content, str):
|
1018
973
|
content = content.encode('utf-8')
|
@@ -1095,6 +1050,8 @@ def safe_div(a, b):
|
|
1095
1050
|
|
1096
1051
|
def inplace_decorate(parent, func_name, wrapper):
|
1097
1052
|
""" 将指定的函数替换为装饰器版本
|
1053
|
+
允许在运行时动态地将一个函数或方法替换为其装饰版本。通常用于添加日志、性能测试、事务处理等。
|
1054
|
+
(既然可以写成装饰版本,相当于其实要完全替换成另外的函数也是可以的)
|
1098
1055
|
|
1099
1056
|
当然,因为py一切皆对象,这里处理的不是函数,而是其他变量等对象也是可以的
|
1100
1057
|
|
@@ -1126,3 +1083,115 @@ def inplace_decorate(parent, func_name, wrapper):
|
|
1126
1083
|
return wrapper(original_func, *args, **kwargs)
|
1127
1084
|
|
1128
1085
|
parent[func_name] = decorated_func
|
1086
|
+
|
1087
|
+
|
1088
|
+
def check_counter(data, top_n=10):
|
1089
|
+
""" 将一个数据data转为Counter进行频数分析 """
|
1090
|
+
# 1 如果是list、tuple类型,需要转counter
|
1091
|
+
if isinstance(data, (list, tuple)):
|
1092
|
+
data = Counter(data)
|
1093
|
+
if not isinstance(data, Counter):
|
1094
|
+
raise ValueError(f'输入的数据类型不对,应该是Counter类型,而不是{typename(data)}')
|
1095
|
+
|
1096
|
+
# 2 列出出现次数最多的top_n条目
|
1097
|
+
# 打印基本统计信息
|
1098
|
+
total_items = sum(data.values())
|
1099
|
+
print(f"总条目数: {total_items}")
|
1100
|
+
|
1101
|
+
if top_n > 0:
|
1102
|
+
top_items = data.most_common(top_n)
|
1103
|
+
max_n = len(data)
|
1104
|
+
print(f"出现次数最多的{min(top_n, max_n)}/{max_n}条数据(频率):")
|
1105
|
+
for item, count in top_items:
|
1106
|
+
print(f"\t{item}\t{count}")
|
1107
|
+
|
1108
|
+
# 3 打印基本统计信息
|
1109
|
+
# 对原始Counter的计数值进行再计数
|
1110
|
+
count_frequencies = Counter(data.values())
|
1111
|
+
|
1112
|
+
# 打印各计数值出现的次数
|
1113
|
+
print("各计数值出现的次数,频率的频率(频率分布):")
|
1114
|
+
for count, frequency in count_frequencies.most_common():
|
1115
|
+
print(f"\t{count}\t{frequency}")
|
1116
|
+
|
1117
|
+
|
1118
|
+
def tprint(*args, **kwargs):
|
1119
|
+
""" 带时间戳的print """
|
1120
|
+
print(utc_now2(), *args, **kwargs)
|
1121
|
+
|
1122
|
+
|
1123
|
+
def is_valid_identifier(name):
|
1124
|
+
""" 判断是否是合法的标识符 """
|
1125
|
+
return re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', name)
|
1126
|
+
|
1127
|
+
|
1128
|
+
def get_number_width(n):
|
1129
|
+
""" 判断数值n的长度
|
1130
|
+
|
1131
|
+
参考资料:https://jstrieb.github.io/posts/digit-length/
|
1132
|
+
|
1133
|
+
>>> get_number_width(0)
|
1134
|
+
1
|
1135
|
+
>>> get_number_width(9)
|
1136
|
+
1
|
1137
|
+
>>> get_number_width(10)
|
1138
|
+
2
|
1139
|
+
>>> get_number_width(97)
|
1140
|
+
2
|
1141
|
+
"""
|
1142
|
+
# assert n > 0
|
1143
|
+
# return math.ceil(math.log10(n + 1))
|
1144
|
+
|
1145
|
+
return 1 if n == 0 else (math.floor(math.log10(n)) + 1)
|
1146
|
+
|
1147
|
+
|
1148
|
+
def aligned_range(start, stop=None, step=1):
|
1149
|
+
""" 返回按照域宽对齐的数字序列 """
|
1150
|
+
if stop is None:
|
1151
|
+
start, stop = 0, start
|
1152
|
+
|
1153
|
+
max_width = get_number_width(stop - step)
|
1154
|
+
format_str = '{:0' + str(max_width) + 'd}'
|
1155
|
+
|
1156
|
+
return [format_str.format(i) for i in range(start, stop, step)]
|
1157
|
+
|
1158
|
+
|
1159
|
+
def percentage_and_value(numbers, precision=2, *, total=None, sep='.'):
|
1160
|
+
""" 对输入的一串数值,转换成一种特殊的表达格式 "百分比.次数"
|
1161
|
+
|
1162
|
+
:param list numbers: 数值列表
|
1163
|
+
:param int precision: 百分比的精度(小数点后的位数),默认为 2
|
1164
|
+
:param int total: 总数,如果不输入,则默认为输入数值的和
|
1165
|
+
:param str sep: 分隔符
|
1166
|
+
:return: 整数部分是比例,小数部分是原始数值的整数部分
|
1167
|
+
"""
|
1168
|
+
if total is None:
|
1169
|
+
total = sum(numbers)
|
1170
|
+
width = get_number_width(total)
|
1171
|
+
result = []
|
1172
|
+
for num in numbers:
|
1173
|
+
percent = safe_div(num, total) * 10 ** precision
|
1174
|
+
result.append(f"{percent:.0f}{sep}{num:0{width}d}")
|
1175
|
+
return result
|
1176
|
+
|
1177
|
+
|
1178
|
+
def get_local_ip():
|
1179
|
+
""" 获得本地ip,代码由deepseek提供 """
|
1180
|
+
try:
|
1181
|
+
# 使用UDP协议连接到外部服务器
|
1182
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
1183
|
+
s.connect(("8.8.8.8", 80)) # Google的DNS服务器和常用端口
|
1184
|
+
local_ip = s.getsockname()[0] # 获取套接字的本地地址
|
1185
|
+
s.close()
|
1186
|
+
return local_ip
|
1187
|
+
except Exception as e:
|
1188
|
+
# 如果失败,尝试通过主机名获取IP列表
|
1189
|
+
try:
|
1190
|
+
hostname = socket.gethostname()
|
1191
|
+
ips = socket.gethostbyname_ex(hostname)[2] # 获取所有IPv4地址
|
1192
|
+
for ip in ips:
|
1193
|
+
if ip != "127.0.0.1": # 排除回环地址
|
1194
|
+
return ip
|
1195
|
+
return "127.0.0.1" # 默认返回回环地址
|
1196
|
+
except:
|
1197
|
+
raise ValueError("无法获取IP地址")
|
@@ -25,7 +25,7 @@ import requests
|
|
25
25
|
from humanfriendly import parse_size
|
26
26
|
|
27
27
|
from pyxllib.prog.newbie import human_readable_size
|
28
|
-
from pyxllib.prog.pupil import get_installed_packages
|
28
|
+
from pyxllib.prog.pupil import get_installed_packages, aligned_range, percentage_and_value
|
29
29
|
from pyxllib.prog.xlosenv import XlOsEnv
|
30
30
|
from pyxllib.file.specialist import cache_file
|
31
31
|
|
@@ -102,6 +102,7 @@ def distribute_package(root, version=None, repository=None, *,
|
|
102
102
|
:param version_file: 保存版本号的文件,注意看正则规则,需要满足特定的范式,才会自动更新版本号
|
103
103
|
:param delete_dist: 上传完是否自动删除dist目录,要检查上传包是否有遗漏时,要关闭
|
104
104
|
"""
|
105
|
+
import sys
|
105
106
|
from pyxllib.file.specialist import XlPath
|
106
107
|
|
107
108
|
# 1 切换工作目录
|
@@ -114,7 +115,7 @@ def distribute_package(root, version=None, repository=None, *,
|
|
114
115
|
f.write_text(s)
|
115
116
|
|
116
117
|
# 3 打包
|
117
|
-
subprocess.run('
|
118
|
+
subprocess.run(f'{sys.executable} setup.py sdist')
|
118
119
|
|
119
120
|
# 4 上传
|
120
121
|
if upload:
|
@@ -215,3 +216,176 @@ class ProgressBar:
|
|
215
216
|
time.sleep(1)
|
216
217
|
pbar.n = self.progress
|
217
218
|
pbar.refresh()
|
219
|
+
|
220
|
+
|
221
|
+
class BitMaskTool:
|
222
|
+
""" 二进制位掩码工具
|
223
|
+
|
224
|
+
概念术语
|
225
|
+
bitval,每一个位上的具体取值,0或1
|
226
|
+
bitsum,所有位上的取值的和,即二进制数的十进制表示
|
227
|
+
"""
|
228
|
+
|
229
|
+
def __init__(self, bit_names=None, bitsum_counter=None):
|
230
|
+
""" 初始化 BitMaskTool 对象
|
231
|
+
|
232
|
+
:param list bit_names: 每一位功能开启时,显示的标签名。未输入时,填充0,1,2,3...注意总数,要按域宽对齐
|
233
|
+
:param dict/list bitsum_counter: 各种值出现的次数,可以是一个字典或者一个包含出现次数的列表
|
234
|
+
"""
|
235
|
+
# 1 每一位功能开启时,显示的标签名。未输入时,填充0,1,2,3...注意总数,要按域宽对齐
|
236
|
+
if bit_names is None:
|
237
|
+
bit_names = list(aligned_range(len(bit_names)))
|
238
|
+
self.bit_names = bit_names
|
239
|
+
# 2 各种值出现的次数
|
240
|
+
if isinstance(bitsum_counter, (list, tuple)):
|
241
|
+
bitsum_counter = Counter(bitsum_counter)
|
242
|
+
self.bitsum_counter = bitsum_counter or {}
|
243
|
+
|
244
|
+
def get_bitsum_names(self, bitsum):
|
245
|
+
""" 从bitsum得到names的拼接
|
246
|
+
|
247
|
+
>> get_bitsum_names(3)
|
248
|
+
'语义定位,图表'
|
249
|
+
"""
|
250
|
+
if not isinstance(bitsum, int):
|
251
|
+
try:
|
252
|
+
bitsum = int(bitsum)
|
253
|
+
except ValueError:
|
254
|
+
bitsum = 0
|
255
|
+
|
256
|
+
tags = []
|
257
|
+
for i, k in enumerate(self.bit_names):
|
258
|
+
if (1 << i) & bitsum:
|
259
|
+
tags.append(k)
|
260
|
+
return ','.join(tags)
|
261
|
+
|
262
|
+
def count_bitsum_relations(self, target_bitsum, relation='='):
|
263
|
+
""" 计算特定关系的 bitsum 数量
|
264
|
+
|
265
|
+
:param int target_bitsum: 目标 bitsum
|
266
|
+
:param str relation: 关系类型,可以是 '=', '⊂', '⊃'
|
267
|
+
假设bitval对应的bit_names为n1,n2,n3,n4。
|
268
|
+
那么bitsum相当于是bitval的一个集合
|
269
|
+
比如a={n1,n3,n4},b={n1,n3},因为a完全包含b,所以认为a⊃b,或者a⊋b、b⊂a、b⊊a
|
270
|
+
:return int: 符合条件的 bitsum 数量
|
271
|
+
"""
|
272
|
+
count = 0
|
273
|
+
if relation == '=':
|
274
|
+
# 直接计算等于 target_bitsum 的数量
|
275
|
+
count = self.bitsum_counter.get(target_bitsum, 0)
|
276
|
+
elif relation == '⊂':
|
277
|
+
# 计算所有被 target_bitsum 包含的 bitsum 的数量
|
278
|
+
for bitsum, num in self.bitsum_counter.items():
|
279
|
+
if bitsum and bitsum & target_bitsum == bitsum:
|
280
|
+
count += num
|
281
|
+
elif relation == '⊃':
|
282
|
+
# 计算所有包含 target_bitsum 的 bitsum 的数量
|
283
|
+
for bitsum, num in self.bitsum_counter.items():
|
284
|
+
if bitsum & target_bitsum == target_bitsum:
|
285
|
+
count += num
|
286
|
+
return count
|
287
|
+
|
288
|
+
def check_bitflag(self, max_bitsum_len=None, reletion='=',
|
289
|
+
filter_zero=False, sort_by=None, *,
|
290
|
+
min_bitsum_len=0):
|
291
|
+
""" 检查并返回 bitsum 关系的 DataFrame
|
292
|
+
|
293
|
+
:param int max_bitsum_len: 最大 bitsum 长度
|
294
|
+
:param str reletion: 关系类型,可以是 '=', '⊂', '⊃'
|
295
|
+
支持输入多个字符,表示要同时计算多种关系
|
296
|
+
:param bool filter_zero: 是否过滤掉零值
|
297
|
+
:param None|str sort_by: 排序字段
|
298
|
+
None, 默认排序
|
299
|
+
count, 按照数量从大到小排序
|
300
|
+
bitsum, 按照 bitsum 从小到大排序
|
301
|
+
:param int min_bitsum_len: 最小 bitsum 长度
|
302
|
+
:return: 包含 bitsum 关系的 DataFrame
|
303
|
+
"""
|
304
|
+
from itertools import combinations
|
305
|
+
|
306
|
+
total = sum(self.bitsum_counter.values())
|
307
|
+
rows, columns = [], ['类型', '名称', '百分比.次数']
|
308
|
+
rows.append([-1, '总计', total])
|
309
|
+
|
310
|
+
if max_bitsum_len is None:
|
311
|
+
max_bitsum_len = len(self.bit_names)
|
312
|
+
|
313
|
+
bitvals = [(1 << i) for i in range(len(self.bit_names))]
|
314
|
+
for m in range(min_bitsum_len, max_bitsum_len + 1):
|
315
|
+
for comb in combinations(bitvals, m):
|
316
|
+
bitsum = sum(comb)
|
317
|
+
count = self.count_bitsum_relations(bitsum, relation=reletion)
|
318
|
+
if filter_zero and count == 0:
|
319
|
+
continue
|
320
|
+
rows.append([f'{reletion}{bitsum}',
|
321
|
+
self.get_bitsum_names(bitsum),
|
322
|
+
count])
|
323
|
+
|
324
|
+
if sort_by == 'count':
|
325
|
+
rows.sort(key=lambda x: x[2], reverse=True)
|
326
|
+
elif sort_by == 'bitsum':
|
327
|
+
rows.sort(key=lambda x: int(x[0][1:]) if isinstance(x[0], str) else x[0])
|
328
|
+
|
329
|
+
df = pd.DataFrame.from_records(rows, columns=columns)
|
330
|
+
df['百分比.次数'] = percentage_and_value(df['百分比.次数'], 2, total=total)
|
331
|
+
return df
|
332
|
+
|
333
|
+
def report(self):
|
334
|
+
""" 生成统计报告 """
|
335
|
+
html_content = []
|
336
|
+
|
337
|
+
html_content.append('<h1>1 包含每一位bitval特征的数量</h1>')
|
338
|
+
df1 = self.check_bitflag(1, '⊃')
|
339
|
+
html_content.append(df1.to_html())
|
340
|
+
|
341
|
+
html_content.append('<h1>2 每一种具体bitsum组合的数量</h1>')
|
342
|
+
df2 = self.check_bitflag(reletion='=', filter_zero=True, sort_by='bitsum')
|
343
|
+
html_content.append(df2.to_html())
|
344
|
+
|
345
|
+
return '\n'.join(html_content)
|
346
|
+
|
347
|
+
|
348
|
+
def loguru_setup_jsonl_logfile(logger, log_dir, rotation_size="10 MB"):
|
349
|
+
"""
|
350
|
+
给loguru的日志器添加导出文件的功能,使用jsonl格式
|
351
|
+
|
352
|
+
:param logger: 日志记录器,一般是from loguru import logger的logger
|
353
|
+
:param log_dir: 存储日志的目录,因为有多个文件,这里要输入的是所在的目录
|
354
|
+
:param rotation_size: 文件多大后分割
|
355
|
+
:return:
|
356
|
+
"""
|
357
|
+
from datetime import datetime
|
358
|
+
|
359
|
+
os.makedirs(log_dir, exist_ok=True) # 自动创建日志目录
|
360
|
+
|
361
|
+
# 日志文件名匹配的正则表达式,格式为 年月日_时分秒.log
|
362
|
+
log_filename_pattern = re.compile(r"(\d{8}_\d{6})\.jsonl")
|
363
|
+
|
364
|
+
# 找到最新的日志文件
|
365
|
+
def find_latest_log_file(log_dir):
|
366
|
+
log_files = []
|
367
|
+
for file in os.listdir(log_dir):
|
368
|
+
if log_filename_pattern.match(file):
|
369
|
+
log_files.append(file)
|
370
|
+
|
371
|
+
if log_files:
|
372
|
+
# 根据时间排序,选择最新的日志文件
|
373
|
+
log_files.sort(reverse=True)
|
374
|
+
return os.path.join(log_dir, log_files[0])
|
375
|
+
return None
|
376
|
+
|
377
|
+
# 检查是否有未写满的日志文件
|
378
|
+
latest_log_file = find_latest_log_file(log_dir)
|
379
|
+
|
380
|
+
if latest_log_file:
|
381
|
+
log_path = latest_log_file
|
382
|
+
else:
|
383
|
+
# 生成新的日志文件名
|
384
|
+
log_filename = datetime.now().strftime("%Y%m%d_%H%M%S") + ".jsonl"
|
385
|
+
log_path = os.path.join(log_dir, log_filename)
|
386
|
+
|
387
|
+
# 配置 logger,写入日志文件,设置旋转条件,使用 JSON 序列化
|
388
|
+
logger.add(log_path, rotation=rotation_size, serialize=True)
|
389
|
+
|
390
|
+
# 输出初始化成功信息
|
391
|
+
logger.info(f"日志系统已初始化,日志文件路径:{log_path}")
|
pyxllib/prog/specialist/bc.py
CHANGED
@@ -16,7 +16,10 @@ from pyxllib.file.specialist import File, Dir, filesmatch, get_encoding, XlPath
|
|
16
16
|
# BCompare.exe, bcompare函数要用
|
17
17
|
|
18
18
|
class BCompare(Explorer):
|
19
|
-
def __init__(self, app='
|
19
|
+
def __init__(self, app='bcomp', shell=False):
|
20
|
+
"""
|
21
|
+
240512周日20:06,本来写的是BCompare,但是友鑫mac电脑上发现似乎有问题,所以改成bcomp,这种在windows上也能用
|
22
|
+
"""
|
20
23
|
super().__init__(app, shell)
|
21
24
|
|
22
25
|
@classmethod
|
@@ -48,7 +51,7 @@ class BCompare(Explorer):
|
|
48
51
|
default_suffix = None
|
49
52
|
for i, arg in enumerate(args):
|
50
53
|
f = XlPath.safe_init(arg)
|
51
|
-
if f.is_file(): # 是文件对象,且存在
|
54
|
+
if f is not None and f.is_file(): # 是文件对象,且存在
|
52
55
|
new_args.append(f)
|
53
56
|
if not default_suffix:
|
54
57
|
default_suffix = f.suffix
|
@@ -13,6 +13,7 @@ import subprocess
|
|
13
13
|
import sys
|
14
14
|
import datetime
|
15
15
|
import platform
|
16
|
+
import re
|
16
17
|
|
17
18
|
import pandas as pd
|
18
19
|
from bs4 import BeautifulSoup
|
@@ -118,9 +119,12 @@ class Explorer:
|
|
118
119
|
TODO 获得返回值分析
|
119
120
|
"""
|
120
121
|
args = [self.app] + list(args)
|
122
|
+
|
121
123
|
if 'shell' not in kwargs:
|
122
124
|
kwargs.update({'shell': self.shell})
|
123
|
-
|
125
|
+
if re.match(r'open\s', self.app):
|
126
|
+
args = args[0] + ' ' + args[1]
|
127
|
+
kwargs.update({'shell': True})
|
124
128
|
try:
|
125
129
|
if wait:
|
126
130
|
subprocess.run(args, **kwargs)
|
@@ -161,6 +165,7 @@ class Browser(Explorer):
|
|
161
165
|
# 这里默认设置为 'google-chrome',如果你想使用其他的浏览器,例如Firefox,可以修改为 'firefox'
|
162
166
|
app = 'google-chrome'
|
163
167
|
else:
|
168
|
+
app = 'open -a "Google Chrome"'
|
164
169
|
# 其他系统的处理
|
165
170
|
pass
|
166
171
|
super().__init__(app, shell)
|
@@ -225,6 +230,9 @@ class Browser(Explorer):
|
|
225
230
|
file = file.rename(get_etag(str(file)) + file.suffix, if_exists='replace')
|
226
231
|
self.__call__(arg, file, **kwargs)
|
227
232
|
|
233
|
+
def url(self, *args, wait=True, **kwargs):
|
234
|
+
super().__call__(*args, wait=wait, **kwargs)
|
235
|
+
|
228
236
|
def __call__(self, arg, file=None, *, wait=True, clsmsg=True, to_html_args=None,
|
229
237
|
**kwargs): # NOQA Browser的操作跟标准接口略有差异
|
230
238
|
""" 该版本会把arg转存文件重设为文件名
|
@@ -232,7 +240,8 @@ class Browser(Explorer):
|
|
232
240
|
:param file: 默认可以不输入,会按七牛的etag哈希值生成临时文件
|
233
241
|
如果输入,则按照指定的名称生成文件
|
234
242
|
"""
|
235
|
-
|
243
|
+
f = XlPath.safe_init(arg)
|
244
|
+
if f is not None and f.is_file():
|
236
245
|
file = arg
|
237
246
|
else:
|
238
247
|
file = str(self.to_brower_file(arg, file, clsmsg=clsmsg, to_html_args=to_html_args))
|
@@ -16,6 +16,8 @@ a = a - a.isoweekday() + kwargs['weekday'] # 先减去当前星期几,再加
|
|
16
16
|
import datetime
|
17
17
|
import re
|
18
18
|
|
19
|
+
from fastcore.utils import GetAttr
|
20
|
+
|
19
21
|
|
20
22
|
def parse_datetime(*argv):
|
21
23
|
""" 解析字符串日期时间
|
@@ -29,6 +31,9 @@ def parse_datetime(*argv):
|
|
29
31
|
>>> parse_datetime('w200301周日', 'w%y%m%d周日') # 周日必须写全,有缺失会报ValueError
|
30
32
|
datetime.datetime(2020, 3, 1, 0, 0)
|
31
33
|
|
34
|
+
>>> parse_datetime('2019.3.6 22:30:40')
|
35
|
+
datetime.datetime(2019, 3, 6, 22, 30, 40)
|
36
|
+
|
32
37
|
>>> parse_datetime(180213)
|
33
38
|
datetime.datetime(2018, 2, 13, 0, 0)
|
34
39
|
>>> parse_datetime('180213')
|
@@ -99,6 +104,11 @@ def parse_datetime(*argv):
|
|
99
104
|
# 1 没有参数则默认当前运行时间
|
100
105
|
if not argv:
|
101
106
|
dt = datetime.datetime.now()
|
107
|
+
if not dt and isinstance(argv[0], datetime.datetime):
|
108
|
+
dt = argv[0]
|
109
|
+
if not dt and isinstance(argv[0], datetime.date):
|
110
|
+
# 要转成datetime类型,time部分默认00:00:00
|
111
|
+
dt = datetime.datetime.combine(argv[0], datetime.time())
|
102
112
|
if not dt and isinstance(argv[0], float):
|
103
113
|
dt = datetime.datetime.fromtimestamp(argv[0])
|
104
114
|
# 2 如果上述解析不了,且argv恰好为两个参数,则判断为使用strptime初始化
|
@@ -129,3 +139,61 @@ def parse_timedelta(s):
|
|
129
139
|
d = {k: int(v) for k, v in zip(['seconds', 'minutes', 'hours'], parts)}
|
130
140
|
td = datetime.timedelta(**d)
|
131
141
|
return td
|
142
|
+
|
143
|
+
|
144
|
+
class XlWeekTag(GetAttr):
|
145
|
+
""" 个人周标签转换工具 """
|
146
|
+
_default = 'dt'
|
147
|
+
|
148
|
+
def __init__(self, dt=None):
|
149
|
+
"""
|
150
|
+
:param datetime|其他类日期表达 dt: 可以输入一个日期,默认为今天
|
151
|
+
"""
|
152
|
+
if dt is None:
|
153
|
+
dt = datetime.date.today()
|
154
|
+
self.dt = parse_datetime(dt)
|
155
|
+
|
156
|
+
def __1_日期移动(self):
|
157
|
+
pass
|
158
|
+
|
159
|
+
def add_days(self, days):
|
160
|
+
""" 增加指定天数,支持负数 """
|
161
|
+
return self.__class__(self.dt + datetime.timedelta(days=days))
|
162
|
+
|
163
|
+
def monday(self):
|
164
|
+
""" 移动到本周周一 """
|
165
|
+
return self.add_days(-self.dt.weekday())
|
166
|
+
|
167
|
+
def next_week(self):
|
168
|
+
""" 移动到下一周 """
|
169
|
+
return self.add_days(7)
|
170
|
+
|
171
|
+
def prev_week(self):
|
172
|
+
""" 移动到上一周 """
|
173
|
+
return self.add_days(-7)
|
174
|
+
|
175
|
+
def __2_生成标签(self):
|
176
|
+
pass
|
177
|
+
|
178
|
+
def weektag(self):
|
179
|
+
"""
|
180
|
+
:return: 周标签名,例如 'w250414',表示所属周的周一是2025年4月14日
|
181
|
+
"""
|
182
|
+
monday = self.monday() # 获取本周周一
|
183
|
+
tag = 'w' + monday.strftime('%y%m%d')
|
184
|
+
return tag
|
185
|
+
|
186
|
+
def daytag(self):
|
187
|
+
"""
|
188
|
+
:return: 日标签名,例如 '250415周二',表示当天的日期标记,一般是语雀周报中使用
|
189
|
+
"""
|
190
|
+
ch = '一二三四五六日'[self.dt.weekday()]
|
191
|
+
tag = self.dt.strftime('%y%m%d') + '周' + ch
|
192
|
+
return tag
|
193
|
+
|
194
|
+
def week_daytags(self):
|
195
|
+
# 循环获得本周每天的daytag
|
196
|
+
monday = self.monday()
|
197
|
+
tags = [monday.add_days(i).daytag() for i in range(7)]
|
198
|
+
return tags
|
199
|
+
|