-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
2888 lines (2602 loc) · 389 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
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
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>Deepin启用ll命令</title>
<url>/2024/12/16/Deepin%E5%90%AF%E7%94%A8ll%E5%91%BD%E4%BB%A4/</url>
<content><![CDATA[<p>ll是个很方便的快捷命令,默认Deepin未启用。启用方法很简单,编辑下面的文件,去掉以下内容前面的注释即可。</p>
<p>文件:~/.bashrc</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">alias</span> ll=<span class="string">'ls -l'</span></span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category>操作系统</category>
<category>Deepin</category>
</categories>
<tags>
<tag>Linux</tag>
<tag>Deepin</tag>
</tags>
</entry>
<entry>
<title>Hexo Next主题本地搜索功能不可用问题解决</title>
<url>/2024/12/18/Hexo-Next%E4%B8%BB%E9%A2%98%E6%9C%AC%E5%9C%B0%E6%90%9C%E7%B4%A2%E5%8A%9F%E8%83%BD%E4%B8%8D%E5%8F%AF%E7%94%A8%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3/</url>
<content><![CDATA[<p>按照Next主题官网配置步骤(<a href="https://theme-next.js.org/docs/third-party-services/search-services#Local-Search">Local Search</a>)配置后,站点的“搜索”菜单点击无响应。</p>
<p>查看Next主题源代码({Next主题根目录}/hexo-theme-next/layout/_partials/search/index.njk),发现站点优先使用Algolia搜索。如下19行代码:</p>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line">{%- if theme.algolia_search.enable or theme.local_search.enable %}</span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"search-pop-overlay"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"popup search-popup"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"search-header"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">class</span>=<span class="string">"search-icon"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">i</span> <span class="attr">class</span>=<span class="string">"fa fa-search"</span>></span><span class="tag"></<span class="name">i</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"search-input-container"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">autocomplete</span>=<span class="string">"off"</span> <span class="attr">autocapitalize</span>=<span class="string">"off"</span> <span class="attr">maxlength</span>=<span class="string">"80"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">placeholder</span>=<span class="string">"{{ __('search.placeholder') }}"</span> <span class="attr">spellcheck</span>=<span class="string">"false"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">type</span>=<span class="string">"search"</span> <span class="attr">class</span>=<span class="string">"search-input"</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">class</span>=<span class="string">"popup-btn-close"</span> <span class="attr">role</span>=<span class="string">"button"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">i</span> <span class="attr">class</span>=<span class="string">"fa fa-times-circle"</span>></span><span class="tag"></<span class="name">i</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"search-result-container"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"search-result-icon"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">i</span> <span class="attr">class</span>=<span class="string">"{% if theme.algolia_search.enable %}fab fa-algolia{% elif theme.local_search.enable %}fa fa-spinner fa-pulse{% endif %} fa-5x"</span>></span><span class="tag"></<span class="name">i</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line">{%- endif %}</span><br></pre></td></tr></table></figure>
<p>查看主题配置,Algolia和Local Searchs默认是同时启用的。修改配置禁用Algolia,如下:</p>
<figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="attr">algolia_search:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">false</span></span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category>开发框架</category>
<category>Hexo</category>
</categories>
<tags>
<tag>Hexo</tag>
</tags>
</entry>
<entry>
<title>Hexo Next主题集成百度统计</title>
<url>/2024/12/20/Hexo-Next%E4%B8%BB%E9%A2%98%E9%9B%86%E6%88%90%E7%99%BE%E5%BA%A6%E7%BB%9F%E8%AE%A1/</url>
<content><![CDATA[<p>首先,需要在百度统计控制台新增自己的站点。</p>
<p><img src="/images/202412/01.png"></p>
<p>点击“新增网站”按钮:</p>
<p><img src="/images/202412/02.png"></p>
<p>按照要求输入相关信息并保存,页面跳转至代码获取页面。从代码页面中拷贝网站的ID:</p>
<p><img src="/images/202412/03.png"></p>
<p>打开Next主题配置文件,并找到百度统计配置,参数值为上一步百度统计分类的网站ID。</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"># Baidu Analytics</span><br><span class="line"># See: https://tongji.baidu.com</span><br><span class="line">baidu_analytics: 5c46************************e1de</span><br></pre></td></tr></table></figure>
<p>重新部署网站。</p>
<p>以上操作完成后,在百度统计控制台进行验证。</p>
<p><img src="/images/202412/04.png"></p>
<p>如下图,出现“代码安装正确”提示,则说明成功。</p>
<p><img src="/images/202412/05.png"></p>
]]></content>
<categories>
<category>开发框架</category>
<category>Hexo</category>
</categories>
<tags>
<tag>Hexo</tag>
</tags>
</entry>
<entry>
<title>Hexo博客生成标签和分类页</title>
<url>/2024/12/17/Hexo%E5%8D%9A%E5%AE%A2%E7%94%9F%E6%88%90%E6%A0%87%E7%AD%BE%E5%92%8C%E5%88%86%E7%B1%BB%E9%A1%B5/</url>
<content><![CDATA[<h1 id="标签页"><a href="#标签页" class="headerlink" title="标签页"></a>标签页</h1><p>默认情况下,Hexo站点创建后,需手动生成标签页。如不生成,在点击“标签”菜单时会出现以下错误:</p>
<figure class="highlight text"><table><tr><td class="code"><pre><span class="line">Cannot GET /tags/</span><br></pre></td></tr></table></figure>
<p>执行以下命令创建标签页:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">hexo new page tags</span><br></pre></td></tr></table></figure>
<p>以上命令会在站点下生成页面source/tags/index.md。打开并编辑该页面,内容如下:</p>
<figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line">---</span><br><span class="line">title: 标签</span><br><span class="line">date: 2024-12-17 23:27:10</span><br><span class="line"><span class="section">type: tags</span></span><br><span class="line"><span class="section">---</span></span><br></pre></td></tr></table></figure>
<p>以上内容的重点是设置页面type的值为tags。</p>
<h1 id="分类页"><a href="#分类页" class="headerlink" title="分类页"></a>分类页</h1><p>同标签页,创建命令如下:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">hexo new page categories</span><br></pre></td></tr></table></figure>
<p>编辑source/categories/index.md内容如下:</p>
<figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line">---</span><br><span class="line">title: 分类</span><br><span class="line">date: 2024-12-17 23:54:38</span><br><span class="line"><span class="section">type: categories</span></span><br><span class="line"><span class="section">---</span></span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category>开发框架</category>
<category>Hexo</category>
</categories>
<tags>
<tag>Hexo</tag>
</tags>
</entry>
<entry>
<title>Python 版本管理</title>
<url>/2024/12/17/Python-%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86/</url>
<content><![CDATA[<p>Python的版本管理是在实践中一定会面对的问题。Pyenv是一个简单的Python版本管理程序。使用Pyenv可以在多个Python版本间进行切换。本文主要参考项目文档进行翻译和编写。大概的使用效果如下图所示:</p>
<p><img src="/images/202412/06.png"></p>
<h1 id="Pyenv可以做什么…"><a href="#Pyenv可以做什么…" class="headerlink" title="Pyenv可以做什么…"></a>Pyenv可以做什么…</h1><ul>
<li>允许您根据每个用户更改全局Python版本。</li>
<li>提供对每个项目Python版本的支持。</li>
<li>允许用环境变量覆盖Python版本。</li>
<li>一次从多个版本的Python中搜索命令。这有助于使用<a href="https://pypi.python.org/pypi/tox">tox</a>跨Python版本进行测试。</li>
</ul>
<h1 id="与pythonbrew和pythonz相比,Pyenv不能做什么…"><a href="#与pythonbrew和pythonz相比,Pyenv不能做什么…" class="headerlink" title="与pythonbrew和pythonz相比,Pyenv不能做什么…"></a>与pythonbrew和pythonz相比,Pyenv不能做什么…</h1><ul>
<li>取决于Python本身。Pyenv是由纯shell脚本构成的。Python不存在引导问题。</li>
<li>需要加载到shell中。相反,Pyenv的接入通过在PATH中添加一个目录来工作。</li>
<li>当然,可以创建viralenv,或者创建pyenv-viralenv来自动化这个过程。但管理会比较繁琐,并容易产生混乱。</li>
</ul>
<h1 id="Pyenv如何工作"><a href="#Pyenv如何工作" class="headerlink" title="Pyenv如何工作"></a>Pyenv如何工作</h1><p>在高级别上,Pyenv使用注入到PATH中的可执行文件拦截Python命令,确定应用程序指定了哪个Python版本,并将命令传递到正确的Python安装。</p>
<h2 id="理解Pyenv的Shims"><a href="#理解Pyenv的Shims" class="headerlink" title="理解Pyenv的Shims"></a>理解Pyenv的Shims</h2><p>Pyenv的工作原理是在PATH前面插入一个shims目录来工作:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$(pyenv root)/shims:/usr/local/bin:/usr/bin:/bin</span><br></pre></td></tr></table></figure>
<p>通过一个称为重拼凑的过程,Pyenv在该目录中维护shims,以匹配每个已安装版本Python的命令,python、pip等。</p>
<p>Shims是轻量级的可执行文件,只需将命令传递给Pyenv即可。因此,安装了Pyenv之后,当运行pip时,操作系统将执行以下操作:</p>
<ul>
<li>在PATH中搜索名为pip的可执行文件。</li>
<li>在PATH的开头找到名为pip的Pyenv shim。</li>
<li>运行名为pip的shim,然后将命令传递给Pyenv。</li>
</ul>
<h2 id="理解Python版本选择"><a href="#理解Python版本选择" class="headerlink" title="理解Python版本选择"></a>理解Python版本选择</h2><p>当执行一个shim时,Pyenv通过从以下来源读取它来决定使用哪个Python版本,顺序如下:</p>
<ol>
<li>PYENV_VERION环境变量(如果指定)。您可以使用<a href="https://github.com/pyenv/pyenv/blob/master/COMMANDS.md#pyenv-shell">Pyenv shell</a> 命令在当前shell会话中设置这个环境变量。</li>
<li>当前目录中应用指定的 .python-version 文件(如果存在)。可以使用 <a href="https://github.com/pyenv/pyenv/blob/master/COMMANDS.md#pyenv-shell">Pyenv shell</a> 命令修改当前目录的 .python-version 文件。</li>
<li>通过搜索每个父目录找到第一个 .python-version 文件(如果有的话),直到文件系统的根目录。</li>
<li>全局 $(Pyenv root)/version 文件。可以使用 <a href="https://github.com/pyenv/pyenv/blob/master/COMMANDS.md#pyenv-global">Pyenv global</a>命令修改这个文件。如果没有全局版本文件,pyenv 假设想要使用“系统” Python (见下文)。</li>
</ol>
<p>一个特殊的版本名称“system”意味着在PATH中shims的条目之后找到的任何Python(换句话说,如果Pyenv shims不在PATH中,将运行PATH中找到的Python)。请注意,Pyenv认为这些安装不在其控制范围之内,并不试图以任何方式检查或区分它们。因此,例如,如果你在MacOS上并且有OS绑定的Python 3.8.9和Homebrew安装的Python 3.9.12和3.10.2 – 对于Pyenv来说,这仍然是一个单独的“system”版本,并且无论哪一个是在PATH指定的第一个可执行文件名,它都将被运行。</p>
<p><strong>注意</strong>: 可以同时激活多个版本,包括同时激活Python2或Python3的多个版本。这允许并行使用Python2和Python3,并且是类似tox这样的工具所需要的。例如,要指示Pyenv首先使用系统Python和Python3(例如2.7.9和3.4.2),但同时使用Python 3.3.6、3.2.1和2.5.2,首先执行pyenv install安装缺少的版本,然后设置pyenv global system 3.3.6 3.2.1 2.5.2。然后就可以使用适当的pythonX或pythonX.Y名称来调用这些版本中的任何一个。也可以手动在 .python-version 文件中指定多个版本,用换行符分隔。以 # 开头的行将被忽略。</p>
<p><a href="https://github.com/pyenv/pyenv/blob/master/COMMANDS.md#pyenv-which">pyenv which <command></a>显示通过一个shim调用<command>时将运行哪个真正的可执行文件。例如,如果已经安装了3.3.6、3.2.1和2.5.2,其中选择了3.3.6和2.5.2,并且系统Python是3.2.5,pyenv which python2.5应该显示$(Pyenv root)/version/2.5.2/bin/python2.5,pyenv which python3 – $(Pyenv root)/version/3.3.6/bin/python3和pyenv which python3.2 – 由于穿透指向系统Python(见下文)。</p>
<p>如果在所选的Python安装中没有出现相应的可执行文件,那么会穿透到在PATH中相关的可执行文件。这允许使用系统上其他地方安装的任何程序,只要它们没有被选定的Python安装遮蔽。</p>
<h2 id="由Pyenv提供安装Python的位置"><a href="#由Pyenv提供安装Python的位置" class="headerlink" title="由Pyenv提供安装Python的位置"></a>由Pyenv提供安装Python的位置</h2><p>一旦pyenv确定了应用程序指定的Python版本,它就会将命令传递给相应的Python。<br>每个Python版本都安装到$(Pyenv root)/versions下自己的目录中。<br>例如,可能已经安装了以下版本:</p>
<ul>
<li>$(pyenv root)/versions/2.7.8/</li>
<li>$(pyenv root)/versions/3.4.2/</li>
<li>$(pyenv root)/versions/pypy-2.4.0/</li>
</ul>
<p>就Pyenv而言,版本名就是$(Pyenv root)/version下的目录。</p>
<h1 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h1><p>在Linux类系统下用自动安装程序可以很简单的完成安装:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">curl https://pyenv.run | bash</span><br></pre></td></tr></table></figure>
<p>更多详情请访问项目:<a href="https://github.com/pyenv/pyenv-installer">https://github.com/pyenv/pyenv-installer</a>。</p>
<blockquote>
<p>注意,出于某种原因,自动安装程序可能会出现好像卡死的现象,多尝试几次即可。</p>
</blockquote>
<h2 id="设置Pyenv的shell环境"><a href="#设置Pyenv的shell环境" class="headerlink" title="设置Pyenv的shell环境"></a>设置Pyenv的shell环境</h2><p><strong>对于bash:</strong></p>
<p>Bash启动文件在不同的发行版之间差异很大,它们的来源、在什么情况下、以什么顺序,以及它们执行什么附加配置。因此,在所有环境中获取Pyenv的最可靠方法是将Pyenv配置命令追加到 .bashrc(用于交互式shell)和Bash将使用的配置文件(用于登录shell)。</p>
<p>首先,通过在终端中运行以下命令将命令添加到 ~/.bashrc:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">echo</span> <span class="string">'export PYENV_ROOT="$HOME/.pyenv"'</span> >> ~/.bashrc</span><br><span class="line"><span class="built_in">echo</span> <span class="string">'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"'</span> >> ~/.bashrc</span><br><span class="line"><span class="built_in">echo</span> <span class="string">'eval "$(pyenv init -)"'</span> >> ~/.bashrc</span><br></pre></td></tr></table></figure>
<p>然后,如果有 ~/.profile、~/.bash_profile或 ~/.bash_login,也将命令添加到那里。如果没有这些文件,请将它们添加到 ~/.profile。</p>
<ul>
<li>添加到 ~/.profile:</li>
</ul>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">echo</span> <span class="string">'export PYENV_ROOT="$HOME/.pyenv"'</span> >> ~/.profile</span><br><span class="line"><span class="built_in">echo</span> <span class="string">'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"'</span> >> ~/.profile</span><br><span class="line"><span class="built_in">echo</span> <span class="string">'eval "$(pyenv init -)"'</span> >> ~/.profile</span><br></pre></td></tr></table></figure>
<ul>
<li>添加到 ~/.bash_ profile:</li>
</ul>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">echo</span> <span class="string">'export PYENV_ROOT="$HOME/.pyenv"'</span> >> ~/.bash_profile</span><br><span class="line"><span class="built_in">echo</span> <span class="string">'[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"'</span> >> ~/.bash_profile</span><br><span class="line"><span class="built_in">echo</span> <span class="string">'eval "$(pyenv init -)"'</span> >> ~/.bash_profile</span><br></pre></td></tr></table></figure>
<h2 id="重启shell"><a href="#重启shell" class="headerlink" title="重启shell"></a>重启shell</h2><p>使PATH更改生效。</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">exec</span> <span class="string">"<span class="variable">$SHELL</span>"</span></span><br></pre></td></tr></table></figure>
<h2 id="安装Python构建依赖"><a href="#安装Python构建依赖" class="headerlink" title="安装Python构建依赖"></a>安装Python构建依赖</h2><p>在尝试安装新的Python版本之前需要先安装构建Python的依赖。</p>
<p><strong>对于Ubuntu/Debian:</strong></p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt update; <span class="built_in">sudo</span> apt install build-essential libssl-dev zlib1g-dev \</span><br><span class="line">libbz2-dev libreadline-dev libsqlite3-dev curl git \</span><br><span class="line">libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev</span><br></pre></td></tr></table></figure>
<h1 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h1><h2 id="安装其他Python版本"><a href="#安装其他Python版本" class="headerlink" title="安装其他Python版本"></a>安装其他Python版本</h2><p>要安装其他Python版本,请使用<a href="https://github.com/pyenv/pyenv/blob/master/COMMANDS.md#pyenv-install">pyenv install</a>。<br>运行pyenv install -l提供所有可用版本的列表。<br>例如,要下载并安装Python 3.13.1,运行:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">pyenv install 3.13.1</span><br></pre></td></tr></table></figure>
<blockquote>
<p>注意: 大多数Pyenv提供的Python版本都是源代码版本,并且是从源代码构建的,作为安装的一部分(这就是为什么需要预安装Python构建依赖)。你可以给Python的configure和编译器标志传递选项来自定义构建,详细信息请参阅<a href="https://github.com/pyenv/pyenv/blob/master/plugins/python-build/README.md#special-environment-variables">Special environment variables in Python-Build’s README</a>。</p>
</blockquote>
<blockquote>
<p>注意: 如果在安装Python版本时遇到困难,请访问wiki页面:<a href="https://github.com/pyenv/pyenv/wiki/Common-build-problems">Common Build Problems</a>。</p>
</blockquote>
<blockquote>
<p>注意: 如果希望以较长的构建时间为代价获得更快的解释器,请参阅<a href="https://github.com/pyenv/pyenv/blob/master/plugins/python-build/README.md#building-for-maximum-performance">Building for maximum performance in Python-Build’s README</a>。</p>
</blockquote>
<h3 id="自动解析最新版本的前缀"><a href="#自动解析最新版本的前缀" class="headerlink" title="自动解析最新版本的前缀"></a>自动解析最新版本的前缀</h3><p>除uninstall外,所有Pyenv子命令都会自动解析相应版本行中最新版本的完整前缀。<br>pyenv install选择已知的最新版本,而其他子命令选择已安装的最新版本。例如,安装,然后切换到最新的3.13.1版本:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">pyenv install 3.13.1</span><br><span class="line">pyenv global 3.13.1</span><br></pre></td></tr></table></figure>
<p>可以运行<a href="https://github.com/pyenv/pyenv/blob/master/COMMANDS.md#pyenv-latest">pyenv latest -k <prefix></a>来查看pyenv install将如何解析特定的前缀,或者运行<a href="https://github.com/pyenv/pyenv/blob/master/COMMANDS.md#pyenv-latest">pyenv latest <prefix></a>来查看其他子命令将如何解析它。<br>有关详细信息,请参阅<a href="https://github.com/pyenv/pyenv/blob/master/COMMANDS.md#pyenv-latest">pyenv最新文档</a>。</p>
<h3 id="具有扩展支持的Python版本"><a href="#具有扩展支持的Python版本" class="headerlink" title="具有扩展支持的Python版本"></a>具有扩展支持的Python版本</h3><p>对于以下Python版本,Pyenv应用了用户提供的补丁,这些补丁为一些较新的环境添加了支持。虽然没有积极地维护这些补丁,因为现有的版本从来没有更改过,但可以肯定的是,它们将继续工作,直到这些环境的后续版本中出现更多不兼容的更改。</p>
<ul>
<li>3.7.8-3.7.15、3.8.4-3.8.12、3.9.0-3.9.7:XCode 13.3</li>
<li>3.5.10、3.6.15:MacOS 11+和XCode 13.3</li>
<li>2.7.18:MacOS 10.15+和Apple Silicon</li>
</ul>
<h2 id="在Python版本之间切换"><a href="#在Python版本之间切换" class="headerlink" title="在Python版本之间切换"></a>在Python版本之间切换</h2><p>要选择Pyenv安装的Python作为要使用的版本,请运行以下命令之一:</p>
<ul>
<li><a href="https://github.com/pyenv/pyenv/blob/master/COMMANDS.md#pyenv-shell">pyenv shell <version></a>仅为当前shell会话选择。</li>
<li><a href="https://github.com/pyenv/pyenv/blob/master/COMMANDS.md#pyenv-local">pyenv local <version></a>无论何时,只要在工作目录(或者它的子目录)中,就会自动选择。</li>
<li><a href="https://github.com/pyenv/pyenv/blob/master/COMMANDS.md#pyenv-shell">pyenv global <version></a>为用户帐户全局选择。</li>
</ul>
<p>例如,选择上面提到的新安装的Python 3.13.1作为首选版本:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">pyenv global 3.13.1</span><br></pre></td></tr></table></figure>
<p>现在,无论何时调用python、 pip等,都将运行来自Pyenv提供的3.13.1安装的可执行文件,而不是系统Python。</p>
<p>使用“system”作为版本名称将把选择重置为系统提供的Python。</p>
<p>请参阅<a href="https://github.com/pyenv/pyenv?tab=readme-ov-file#understanding-shims">Understanding shims</a>和<a href="https://github.com/pyenv/pyenv?tab=readme-ov-file#understanding-python-version-selection">Understanding Python version selection</a>,以获得有关选择如何工作的更多细节和有关其用法的更多信息。</p>
<h2 id="卸载Python版本"><a href="#卸载Python版本" class="headerlink" title="卸载Python版本"></a>卸载Python版本</h2><p>随着时间的推移,将在$(Pyenv root)/versions目录中累积Python版本。<br>要删除旧的Python版本,使用<a href="https://github.com/pyenv/pyenv/blob/master/COMMANDS.md#pyenv-uninstall">pyenv uninstall <versions></a>。<br>或者,可以简单地rm -rf要删除的版本的目录。可以使用pyenv prefix命令找到特定Python版本的目录,例如:pyenv prefix 2.6.8。但是请注意,插件可能会在卸载时运行额外的操作,这也需要手动完成。例如,Pyenv-Virtualenv 还删除了与正在卸载的版本相关的所有虚拟环境。</p>
<h2 id="其他操作"><a href="#其他操作" class="headerlink" title="其他操作"></a>其他操作</h2><p>运行pyenv commands获取所有可用子命令的列表。使用–help运行一个子命令以获得有关它的帮助,或者查看<a href="https://github.com/pyenv/pyenv/blob/master/COMMANDS.md">Commands Reference</a>。<br>注意,安装的Pyenv插件可以添加它们自己的子命令。</p>
<h1 id="使用安装程序升级"><a href="#使用安装程序升级" class="headerlink" title="使用安装程序升级"></a>使用安装程序升级</h1><p>如果用Pyenv安装程序安装了Pyenv,就已经有了可以升级Pyenv和所有已安装插件的Pyenv-Update插件:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">pyenv update</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category>编程语言</category>
<category>Python</category>
</categories>
<tags>
<tag>Python</tag>
<tag>Pyenv</tag>
</tags>
</entry>
<entry>
<title>git bash中文显示问题</title>
<url>/2024/12/20/git-bash%E4%B8%AD%E6%96%87%E6%98%BE%E7%A4%BA%E9%97%AE%E9%A2%98/</url>
<content><![CDATA[<p>默认情况下git bash中文以ASCII编码,不方便查看,如下:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ git status</span><br><span class="line">位于分支 master</span><br><span class="line"></span><br><span class="line">尚无提交</span><br><span class="line"></span><br><span class="line">要提交的变更:</span><br><span class="line"> (使用 <span class="string">"git rm --cached <文件>..."</span> 以取消暂存)</span><br><span class="line"></span><br><span class="line"> 新文件: <span class="string">"source/_posts/Airflow\345\210\235\344\275\223\351\252\214.md"</span></span><br></pre></td></tr></table></figure>
<p>要显示正确的中文,执行以下命令:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ git config --global core.quotepath <span class="literal">false</span></span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category>开发工具</category>
<category>Git</category>
</categories>
<tags>
<tag>Git</tag>
</tags>
</entry>
<entry>
<title>Hexo自动生成摘要</title>
<url>/2024/12/21/Hexo%E8%87%AA%E5%8A%A8%E7%94%9F%E6%88%90%E6%91%98%E8%A6%81/</url>
<content><![CDATA[<p>Hexo首页默认展示整篇内容,会导致首页非常冗长。虽然可以在头设置中指定摘要 excerpt: 值,或者在文章中使用 <!-- more -->,那么 <!-- more --> 之前的文字将会作为摘要。如下:</p>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</span><br><span class="line"><span class="comment"><!-- more --></span></span><br><span class="line">Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</span><br></pre></td></tr></table></figure>
<p>更好的选项是,使用摘要自动生成插件 hexo-excerpt。</p>
<h1 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h1><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm install hexo-excerpt --save</span><br></pre></td></tr></table></figure>
<h1 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h1><p>可以通过在配置中设置深度(默认为10)来指定摘录的大小。</p>
<p>还可以使用 css 选择器从生成的摘录中排除某些标记。与任何选择器匹配的标记将被排除在外。</p>
<p>默认行为是只显示一个摘录,如果它不会是整篇文章。将 hideWholePostExcerpts 设置为 false 以覆盖它并显示整个文章摘要。</p>
<p>在站点配置文件中添加以下配置:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">excerpt:</span><br><span class="line"> depth: 5</span><br><span class="line"> excerpt_excludes: []</span><br><span class="line"> more_excludes: []</span><br><span class="line"> hideWholePostExcerpts: true</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category>开发框架</category>
<category>Hexo</category>
</categories>
<tags>
<tag>Hexo</tag>
</tags>
</entry>
<entry>
<title>Deepin安装完成后无线网无法使用问题</title>
<url>/2024/12/22/Deepin%E5%AE%89%E8%A3%85%E5%AE%8C%E6%88%90%E5%90%8E%E6%97%A0%E7%BA%BF%E7%BD%91%E6%97%A0%E6%B3%95%E4%BD%BF%E7%94%A8%E9%97%AE%E9%A2%98/</url>
<content><![CDATA[<p>在安装完Deepin 23后,无线网络用不了。自己安装无线网卡驱动后,重启电脑,登陆系统时电脑一直黑屏状态。重新安装Deepin 20.9也是相同的情况。</p>
<p>后来发现是因为Deepin系统安装时,需要选择高版本内核,对于Deepin 20.9和Deepin 23版本都一样:</p>
<p><strong>Deepin 20.9</strong></p>
<p><img src="/images/202412/07.png"></p>
<p><strong>Deepin 23</strong></p>
<p><img src="/images/202412/08.png"></p>
]]></content>
<categories>
<category>操作系统</category>
<category>Deepin</category>
</categories>
<tags>
<tag>Linux</tag>
<tag>Deepin</tag>
</tags>
</entry>
<entry>
<title>Python虚拟环境管理</title>
<url>/2024/12/22/Python%E8%99%9A%E6%8B%9F%E7%8E%AF%E5%A2%83%E7%AE%A1%E7%90%86/</url>
<content><![CDATA[<p>在另外一篇文章已经讲了<a href="https://www.zhangjc.com/2024/12/17/Python-%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86/">Python版本管理</a>。本文主要讲Python虚拟环境管理。有了这两个方面的工具,就可以实现在Python的不同版本不同虚拟环境间方便地切换。</p>
<p>pyenv-virtualenv一般会随pyenv一起安装,也可以通过下文方式独立安装。</p>
<h1 id="什么是虚拟环境"><a href="#什么是虚拟环境" class="headerlink" title="什么是虚拟环境"></a>什么是虚拟环境</h1><p>一种采用协作式隔离的运行时环境,允许Python用户和应用程序在安装和升级Python分发包时不会干扰到同一系统上运行的其他Python应用程序的行为。</p>
<p>虚拟环境其实是一个目录树,其中安装有特定Python版本,以及许多其他包。</p>
<h1 id="为什么需要虚拟环境"><a href="#为什么需要虚拟环境" class="headerlink" title="为什么需要虚拟环境"></a>为什么需要虚拟环境</h1><p>Python应用程序通常会使用非标准库内的软件包和模块。因为可能需要修复特定的错误,或者使用库的过时版本的接口,应用程序有时需要特定版本的库。</p>
<p>这意味着一个Python安装可能无法满足每个应用程序的要求。如果应用程序A需要特定模块的1.0版本,但应用程序B需要2.0版本,则需求存在冲突,安装版本1.0或2.0将导致某一个应用程序无法运行。</p>
<p>这个问题的解决方案就是创建一个虚拟环境。然后,不同的应用将可以使用不同的虚拟环境。 要解决先前需求相冲突的例子,应用程序A可以拥有自己的安装了1.0版本的虚拟环境,而应用程序B则拥有安装了2.0版本的另一个虚拟环境。如果应用程序B要求将某个库升级到3.0版本,也不会影响应用程序A的环境。</p>
<h1 id="pyenv-virtualenv"><a href="#pyenv-virtualenv" class="headerlink" title="pyenv-virtualenv"></a>pyenv-virtualenv</h1><p>pyenv-viralenv是一个pyenv插件,它提供了在类UNIX系统上管理虚拟环境和conda环境的特性。</p>
<blockquote>
<p>pyenv-virtualenvwrapper是pyenv的另外一个管理虚拟环境的插件。选择pyenv-virtualenv的原因是,pyenv-virtualenvwrapper有助于与virtualenvwrapper进行交互,但是pyenv-virtualenv提供了更方便的命令,其中virtualenvs是第一种pyenv版本,可以(反)激活。也就是说,pyenv和virtualenvwrapper仍然是分开的,而pyenv-virtualenv是一个很好的组合。</p>
</blockquote>
<h1 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h1><p>下面将把最新开发版本的pyenv-virtualenv安装到$(pyenv root)/plugins/pyenv-virtualenv目录中。</p>
<blockquote>
<p><strong>重要提示</strong>: 如果将pyenv安装到一个非标准目录中,请确保将该仓库克隆到安装位置的“plugins”目录中。</p>
</blockquote>
<p>在这个目录中可以:</p>
<ul>
<li>检出特定的发布版标签。</li>
<li>通过运行git pull来下载最新的更改,从而获得最新的开发版本。<ol>
<li>检出pyenv-virtualenv至插件目录<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">git clone https://github.com/pyenv/pyenv-virtualenv.git $(pyenv root)/plugins/pyenv-virtualenv</span><br></pre></td></tr></table></figure></li>
<li>(可选)添加pyenv virtualenv-init至shell来自动激活虚拟环境。这完全是可选的,但是非常有用。请参阅下面的“<a href="https://www.zhangjc.com/2024/12/22/Python%E8%99%9A%E6%8B%9F%E7%8E%AF%E5%A2%83%E7%AE%A1%E7%90%86/#%E6%BF%80%E6%B4%BB%E8%99%9A%E6%8B%9F%E7%8E%AF%E5%A2%83">激活虚拟环境</a>”。<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bashrc</span><br></pre></td></tr></table></figure></li>
<li>重启shell以启用pyenv-virtualenv<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">exec "$SHELL"</span><br></pre></td></tr></table></figure></li>
</ol>
</li>
</ul>
<h1 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h1><h2 id="与pyenv一起使用pyenv-virtualenv"><a href="#与pyenv一起使用pyenv-virtualenv" class="headerlink" title="与pyenv一起使用pyenv virtualenv"></a>与pyenv一起使用pyenv virtualenv</h2><p>要与pyenv一起使用创建Python版本的虚拟环境,运行pyenv virtualenv,指定所需的Python版本和虚拟环境目录的名称。例如:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">$ pyenv virtualenv 3.13.1 scrat</span><br></pre></td></tr></table></figure>
<p>将在$(pyenv root)/versions目录下一个名为scrat的文件夹中,创建一个基于Python 3.13.1的虚拟环境。</p>
<p>pyenv virtualenv将任何选项转发给实际创建虚拟环境的底层命令(conda、 virtualenv或python -m venv)。有关详细信息,请参阅pyenv virtualenv –help的输出。</p>
<h2 id="从当前版本创建虚拟环境"><a href="#从当前版本创建虚拟环境" class="headerlink" title="从当前版本创建虚拟环境"></a>从当前版本创建虚拟环境</h2><p>如果只有一个参数提供给pyenv virtualenv,那么将根据当前pyenv的Python版本并使用给定的名称创建虚拟环境。</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">$ pyenv version</span><br><span class="line">3.13.1 (set by /home/zhangjc/.pyenv/version)</span><br><span class="line">$ pyenv virtualenv venv3.13.1</span><br></pre></td></tr></table></figure>
<h2 id="列出已有的虚拟环境"><a href="#列出已有的虚拟环境" class="headerlink" title="列出已有的虚拟环境"></a>列出已有的虚拟环境</h2><p>pyenv virtualenvs显示已有的虚拟环境和conda环境列表。</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">$ pyenv virtualenvs</span><br><span class="line"> 3.13.1/envs/scrat (created from /home/zhangjc/.pyenv/versions/3.13.1)</span><br><span class="line"> scrat (created from /home/zhangjc/.pyenv/versions/3.13.1)</span><br></pre></td></tr></table></figure>
<p>每个虚拟环境有两个条目,较短的条目只是一个符号链接。</p>
<h2 id="激活虚拟环境"><a href="#激活虚拟环境" class="headerlink" title="激活虚拟环境"></a>激活虚拟环境</h2><p>一些外部工具(例如<a href="https://github.com/davidhalter/jedi">jedi</a>)可能需要您激活虚拟环境和conda环境。</p>
<p>如果eval “$(pyenv virtualenv-init -)”在shell中配置,当进入/离开包含.python-version文件时会自动激活对应的虚拟环境,前提是.python-version中的虚拟环境的名称是有效的。使用pyenv virtualenvs命令查看有效虚拟环境清单,如上例中的scrat。pyenv使用.python-version文件来表示本地Python版本,可以使用pyenv local命令创建和删除这些文件。</p>
<p>可以手动激活和停用一个pyenv虚拟环境:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">pyenv activate <name></span><br><span class="line">pyenv deactivate</span><br></pre></td></tr></table></figure>
<h2 id="删除已有的虚拟环境"><a href="#删除已有的虚拟环境" class="headerlink" title="删除已有的虚拟环境"></a>删除已有的虚拟环境</h2><p>删除$(pyenv root)/versions和$(pyenv root)/versions/{version}/envs中的目录将删除虚拟环境,或者您可以运行:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">pyenv uninstall my-virtual-env</span><br></pre></td></tr></table></figure>
<p>也可以使用virtualenv-delete命令删除已有的虚拟环境,例如,可以运行:</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">pyenv virtualenv-delete my-virtual-env</span><br></pre></td></tr></table></figure>
<p>这将删除名为my-virtual-env的虚拟环境。</p>
<h2 id="特殊环境变量"><a href="#特殊环境变量" class="headerlink" title="特殊环境变量"></a>特殊环境变量</h2><p>可以设置某些环境变量来控制pyenv-virtualenv。</p>
<ul>
<li>PYENV_VIRTUALENV_CACHE_PATH:如果设置了,则指定一个目录用于缓存下载的包文件。</li>
<li>VIRTUALENV_VERSION:如果设置了,则强制pyenv-virtualenv安装所需的virtualenv版本。如果没有安装virtualenv,pyenv-virtualenv将尝试安装给定版本的virtualenv。</li>
<li>GET_PIP:如果设置了并且venv优于virtualenv,则从指定位置使用get_pip.py。</li>
<li>GET_PIP_URL:如果设置了并且venv优于virtualenv,则从指定的URL下载get_pip.py。</li>
<li>PIP_VERSION:如果设置了并且venv优于virtualenv,则安装指定版本的pip。</li>
<li>PYENV_VIRTUALENV_VERBOSE_ACTIVATE:如果设置了,则显示一些关于激活和停用的详细输出。</li>
</ul>
]]></content>
<categories>
<category>编程语言</category>
<category>Python</category>
</categories>
<tags>
<tag>Python</tag>
</tags>
</entry>
<entry>
<title>Python日志库:Loguru</title>
<url>/2024/12/23/Python%E6%97%A5%E5%BF%97%E5%BA%93%EF%BC%9ALoguru/</url>
<content><![CDATA[<p>Loguru是一个旨在为Python带来愉快的日志记录的库。使用Loguru,没有理由不从一开始就使用日志记录,这就像从Loguru导入日志一样简单from loguru import logger。此外,这个库通过添加一系列有用的功能来解决使用标准日志记录库的痛苦。在应用程序中使用日志应该是自动的,Loguru试图使其既令人愉快又强大。</p>
<h1 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h1><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">pip install loguru</span><br></pre></td></tr></table></figure>
<h1 id="特性"><a href="#特性" class="headerlink" title="特性"></a>特性</h1><h2 id="开箱即用"><a href="#开箱即用" class="headerlink" title="开箱即用"></a>开箱即用</h2><p>Loguru的主要概念是有且只需要一个<a href="https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger">Logger</a>。为了方便起见,它预先配置从输出到stderr开始(但是这是完全可配置的)。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> loguru <span class="keyword">import</span> logger</span><br><span class="line"></span><br><span class="line">logger.debug(<span class="string">"That's it, beautiful and simple logging!"</span>)</span><br></pre></td></tr></table></figure>
<p><a href="https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger">Logger</a>只是一个将日志消息分发给已配置的处理程序的接口。</p>
<h2 id="无处理程序、无格式化、无过滤器:一个函数来规定所有"><a href="#无处理程序、无格式化、无过滤器:一个函数来规定所有" class="headerlink" title="无处理程序、无格式化、无过滤器:一个函数来规定所有"></a>无处理程序、无格式化、无过滤器:一个函数来规定所有</h2><p>如何添加处理程序? 如何设置日志格式? 如何过滤消息? 如何设置级别?答案是:<a href="https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.add">add()</a>函数。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">logger.add(sys.stderr, <span class="built_in">format</span>=<span class="string">"{time} {level} {message}"</span>, <span class="built_in">filter</span>=<span class="string">"my_module"</span>, level=<span class="string">"INFO"</span>)</span><br></pre></td></tr></table></figure>
<p>此函数用来注册使用<a href="https://loguru.readthedocs.io/en/stable/api/logger.html#record">record dict</a>来管理<a href="https://loguru.readthedocs.io/en/stable/api/logger.html#message">log messages</a>上下文的<a href="https://loguru.readthedocs.io/en/stable/api/logger.html#sink">sinks</a>。sink可以有多种形式:一个简单的函数、一个字符串路径、一个类似文件的对象、一个协程函数或一个内置的Handler。</p>
<p>注意,还可以通过使用在添加处理程序时返回的标识符来remove()以前添加的处理程序。如果希望取代默认的stderr处理程序,这尤其有用:只需调用logger.remove()来重新开始。</p>
<h2 id="更容易的旋转-保留-压缩日志文件"><a href="#更容易的旋转-保留-压缩日志文件" class="headerlink" title="更容易的旋转/保留/压缩日志文件"></a>更容易的旋转/保留/压缩日志文件</h2><p>如果将记录的消息发送到文件,只需使用字符串路径作为接收器。为了方便起见,它还可以自动计时:</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">logger.add(<span class="string">"file_{time}.log"</span>)</span><br></pre></td></tr></table></figure>
<p>如果需要旋转日志文件,或者删除较旧的日志文件,或者在关闭时压缩日志文件,也是很容易配置的。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">logger.add(<span class="string">"file_1.log"</span>, rotation=<span class="string">"500 MB"</span>) <span class="comment"># Automatically rotate too big file</span></span><br><span class="line">logger.add(<span class="string">"file_2.log"</span>, rotation=<span class="string">"12:00"</span>) <span class="comment"># New file is created each day at noon</span></span><br><span class="line">logger.add(<span class="string">"file_3.log"</span>, rotation=<span class="string">"1 week"</span>) <span class="comment"># Once the file is too old, it's rotated</span></span><br><span class="line"></span><br><span class="line">logger.add(<span class="string">"file_X.log"</span>, retention=<span class="string">"10 days"</span>) <span class="comment"># Cleanup after some time</span></span><br><span class="line"></span><br><span class="line">logger.add(<span class="string">"file_Y.log"</span>, compression=<span class="string">"zip"</span>) <span class="comment"># Save some loved space</span></span><br></pre></td></tr></table></figure>
<h2 id="大括号样式的现代字符串格式设置"><a href="#大括号样式的现代字符串格式设置" class="headerlink" title="大括号样式的现代字符串格式设置"></a>大括号样式的现代字符串格式设置</h2><p>Loguru喜欢更优雅和强大的{}格式化而不是%,日志记录函数实际上等效于str.format ()。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">logger.info(<span class="string">"If you're using Python {}, prefer {feature} of course!"</span>, <span class="number">3.6</span>, feature=<span class="string">"f-strings"</span>)</span><br></pre></td></tr></table></figure>
<h2 id="在线程或主线程中捕获异常"><a href="#在线程或主线程中捕获异常" class="headerlink" title="在线程或主线程中捕获异常"></a>在线程或主线程中捕获异常</h2><p>您是否曾经遇到过程序意外崩溃,而在日志文件中没有看到任何信息?您是否注意到线程中发生的异常没有被记录?可以使用catch ()装饰器/上下文管理器解决这个问题,该管理器确保任何错误都被正确地传递到logger。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">@logger.catch</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">my_function</span>(<span class="params">x, y, z</span>):</span><br><span class="line"> <span class="comment"># An error? It's caught anyway!</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span> / (x + y + z)</span><br></pre></td></tr></table></figure>
<h2 id="优美的彩色日志"><a href="#优美的彩色日志" class="headerlink" title="优美的彩色日志"></a>优美的彩色日志</h2><p>如果终端兼容,Loguru会自动为日志添加颜色。可以在接收器格式中用markup tags来定义喜欢的样式。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">logger.add(sys.stdout, colorize=<span class="literal">True</span>, <span class="built_in">format</span>=<span class="string">"<green>{time}</green> <level>{message}</level>"</span>)</span><br></pre></td></tr></table></figure>
<h2 id="异步、线程安全、多进程安全"><a href="#异步、线程安全、多进程安全" class="headerlink" title="异步、线程安全、多进程安全"></a>异步、线程安全、多进程安全</h2><p>默认情况下,添加到<a href="https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger">logger</a>的所有接收器都是线程安全的。它们不是多进程安全的,但是可以对消息进行enqueue以确保日志的完整性。如果需要异步日志记录,也可以使用同样的参数。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">logger.add(<span class="string">"somefile.log"</span>, enqueue=<span class="literal">True</span>)</span><br></pre></td></tr></table></figure>
<p>也支持作为接收器的协同函数,并且应该使用complete()进行等待。</p>
<h2 id="完全描述例外"><a href="#完全描述例外" class="headerlink" title="完全描述例外"></a>完全描述例外</h2><p>记录代码中发生的异常对于跟踪bug非常重要,但是如果不知道为什么会失败,那么记录异常就完全没有用处。Loguru允许显示整个堆栈跟踪,包括变量的值,从而帮助识别问题(感谢<a href="https://github.com/Qix-/better-exceptions">better_exception</a>!)。</p>
<p>代码如下:</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># Caution, "diagnose=True" is the default and may leak sensitive data in prod</span></span><br><span class="line">logger.add(<span class="string">"out.log"</span>, backtrace=<span class="literal">True</span>, diagnose=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">func</span>(<span class="params">a, b</span>):</span><br><span class="line"> <span class="keyword">return</span> a / b</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">nested</span>(<span class="params">c</span>):</span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> func(<span class="number">5</span>, c)</span><br><span class="line"> <span class="keyword">except</span> ZeroDivisionError:</span><br><span class="line"> logger.exception(<span class="string">"What?!"</span>)</span><br><span class="line"></span><br><span class="line">nested(<span class="number">0</span>)</span><br></pre></td></tr></table></figure>
<p>结果如下:</p>
<figure class="highlight text"><table><tr><td class="code"><pre><span class="line">2018-07-17 01:38:43.975 | ERROR | __main__:nested:10 - What?!</span><br><span class="line">Traceback (most recent call last):</span><br><span class="line"></span><br><span class="line"> File "test.py", line 12, in <module></span><br><span class="line"> nested(0)</span><br><span class="line"> └ <function nested at 0x7f5c755322f0></span><br><span class="line"></span><br><span class="line">> File "test.py", line 8, in nested</span><br><span class="line"> func(5, c)</span><br><span class="line"> │ └ 0</span><br><span class="line"> └ <function func at 0x7f5c79fc2e18></span><br><span class="line"></span><br><span class="line"> File "test.py", line 4, in func</span><br><span class="line"> return a / b</span><br><span class="line"> │ └ 0</span><br><span class="line"> └ 5</span><br><span class="line"></span><br><span class="line">ZeroDivisionError: division by zero</span><br></pre></td></tr></table></figure>
<p>注意,由于帧数据不可用,这个特性不能在默认的Python REPL上工作。参考:<a href="https://loguru.readthedocs.io/en/stable/resources/recipes.html#security-considerations-when-using-loguru">Security considerations when using Loguru</a>。</p>
<h2 id="根据需要结构化日志"><a href="#根据需要结构化日志" class="headerlink" title="根据需要结构化日志"></a>根据需要结构化日志</h2><p>是否希望将日志序列化以便于解析或传递它们?使用serialize序列化参数,每个日志消息在发送到配置的接收器之前都将转换为JSON字符串。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">logger.add(custom_sink_function, serialize=<span class="literal">True</span>)</span><br></pre></td></tr></table></figure>
<p>通过使用bind(),可以通过修改额外的record属性添加消息上下文。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">logger.add(<span class="string">"file.log"</span>, <span class="built_in">format</span>=<span class="string">"{extra[ip]} {extra[user]} {message}"</span>)</span><br><span class="line">context_logger = logger.bind(ip=<span class="string">"192.168.0.1"</span>, user=<span class="string">"someone"</span>)</span><br><span class="line">context_logger.info(<span class="string">"Contextualize your logger easily"</span>)</span><br><span class="line">context_logger.bind(user=<span class="string">"someone_else"</span>).info(<span class="string">"Inline binding of extra attribute"</span>)</span><br><span class="line">context_logger.info(<span class="string">"Use kwargs to add context during formatting: {user}"</span>, user=<span class="string">"anybody"</span>)</span><br></pre></td></tr></table></figure>
<p>可以使用<a href="https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.contextualize">contextualize()</a>临时修改上下文局部状态:</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">with</span> logger.contextualize(task=task_id):</span><br><span class="line"> do_something()</span><br><span class="line"> logger.info(<span class="string">"End of task"</span>)</span><br></pre></td></tr></table></figure>
<p>通过组合bind()和filter,可以对日志进行更细粒度的控制:</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">logger.add(<span class="string">"special.log"</span>, <span class="built_in">filter</span>=<span class="keyword">lambda</span> record: <span class="string">"special"</span> <span class="keyword">in</span> record[<span class="string">"extra"</span>])</span><br><span class="line">logger.debug(<span class="string">"This message is not logged to the file"</span>)</span><br><span class="line">logger.bind(special=<span class="literal">True</span>).info(<span class="string">"This message, though, is logged to the file!"</span>)</span><br></pre></td></tr></table></figure>
<p>最后,<a href="https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.patch">patch()</a>方法允许将动态值附加到每条新消息记录:</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">logger.add(sys.stderr, <span class="built_in">format</span>=<span class="string">"{extra[utc]} {message}"</span>)</span><br><span class="line">logger = logger.patch(<span class="keyword">lambda</span> record: record[<span class="string">"extra"</span>].update(utc=datetime.utcnow()))</span><br></pre></td></tr></table></figure>
<h2 id="延迟求值代价函数"><a href="#延迟求值代价函数" class="headerlink" title="延迟求值代价函数"></a>延迟求值代价函数</h2><p>有时希望在生产环境中记录详细信息而不会影响性能,可以使用<a href="https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.opt">opt()</a>方法来实现这一点。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">logger.opt(lazy=<span class="literal">True</span>).debug(<span class="string">"If sink level <= DEBUG: {x}"</span>, x=<span class="keyword">lambda</span>: expensive_function(<span class="number">2</span>**<span class="number">64</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># By the way, "opt()" serves many usages</span></span><br><span class="line">logger.opt(exception=<span class="literal">True</span>).info(<span class="string">"Error stacktrace added to the log message (tuple accepted too)"</span>)</span><br><span class="line">logger.opt(colors=<span class="literal">True</span>).info(<span class="string">"Per message <blue>colors</blue>"</span>)</span><br><span class="line">logger.opt(record=<span class="literal">True</span>).info(<span class="string">"Display values from the record (eg. {record[thread]})"</span>)</span><br><span class="line">logger.opt(raw=<span class="literal">True</span>).info(<span class="string">"Bypass sink formatting\n"</span>)</span><br><span class="line">logger.opt(depth=<span class="number">1</span>).info(<span class="string">"Use parent stack context (useful within wrapped functions)"</span>)</span><br><span class="line">logger.opt(capture=<span class="literal">False</span>).info(<span class="string">"Keyword arguments not added to {dest} dict"</span>, dest=<span class="string">"extra"</span>)</span><br></pre></td></tr></table></figure>
<h2 id="自定义级别"><a href="#自定义级别" class="headerlink" title="自定义级别"></a>自定义级别</h2><p>Loguru提供了所有添加trace()和success()的标准日志记录级别。是否需要更多?然后,使用level()函数创建它。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">new_level = logger.level(<span class="string">"SNAKY"</span>, no=<span class="number">38</span>, color=<span class="string">"<yellow>"</span>, icon=<span class="string">"🐍"</span>)</span><br><span class="line"></span><br><span class="line">logger.log(<span class="string">"SNAKY"</span>, <span class="string">"Here we go!"</span>)</span><br></pre></td></tr></table></figure>
<h2 id="更好的处理日期时间"><a href="#更好的处理日期时间" class="headerlink" title="更好的处理日期时间"></a>更好的处理日期时间</h2><p>标准日志记录中充斥着诸如datefmt或msecs、%(asctime)s和%(created)s之类的参数,没有时区信息的朴素日期时间,没有直观的格式设置等等。Loguru解决了这个问题:</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">logger.add(<span class="string">"file.log"</span>, <span class="built_in">format</span>=<span class="string">"{time:YYYY-MM-DD at HH:mm:ss} | {level} | {message}"</span>)</span><br></pre></td></tr></table></figure>
<h2 id="适用于脚本和库"><a href="#适用于脚本和库" class="headerlink" title="适用于脚本和库"></a>适用于脚本和库</h2><p>在脚本中使用日志记录器很容易,并且可以在开始时configure()。要在库中使用Loguru,请记住永远不要调用add(),而是使用disable(),这样日志记录函数就变为no-op。如果开发人员希望查看库的日志,他们可以再次enable()。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># For scripts</span></span><br><span class="line">config = {</span><br><span class="line"> <span class="string">"handlers"</span>: [</span><br><span class="line"> {<span class="string">"sink"</span>: sys.stdout, <span class="string">"format"</span>: <span class="string">"{time} - {message}"</span>},</span><br><span class="line"> {<span class="string">"sink"</span>: <span class="string">"file.log"</span>, <span class="string">"serialize"</span>: <span class="literal">True</span>},</span><br><span class="line"> ],</span><br><span class="line"> <span class="string">"extra"</span>: {<span class="string">"user"</span>: <span class="string">"someone"</span>}</span><br><span class="line">}</span><br><span class="line">logger.configure(**config)</span><br><span class="line"></span><br><span class="line"><span class="comment"># For libraries, should be your library's `__name__`</span></span><br><span class="line">logger.disable(<span class="string">"my_library"</span>)</span><br><span class="line">logger.info(<span class="string">"No matter added sinks, this message is not displayed"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># In your application, enable the logger in the library</span></span><br><span class="line">logger.enable(<span class="string">"my_library"</span>)</span><br><span class="line">logger.info(<span class="string">"This message however is propagated to the sinks"</span>)</span><br></pre></td></tr></table></figure>
<p>为了更加方便,还可以使用loguru-config库直接从配置文件设置日志记录器。</p>
<h2 id="与标准日志记录完全兼容"><a href="#与标准日志记录完全兼容" class="headerlink" title="与标准日志记录完全兼容"></a>与标准日志记录完全兼容</h2><p>是否希望使用内置的日志Handler作为Loguru接收器?</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">handler = logging.handlers.SysLogHandler(address=(<span class="string">'localhost'</span>, <span class="number">514</span>))</span><br><span class="line">logger.add(handler)</span><br></pre></td></tr></table></figure>
<p>是否需要将Loguru消息传播到标准日志?</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">PropagateHandler</span>(logging.Handler):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">emit</span>(<span class="params">self, record: logging.LogRecord</span>) -> <span class="literal">None</span>:</span><br><span class="line"> logging.getLogger(record.name).handle(record)</span><br><span class="line"></span><br><span class="line">logger.add(PropagateHandler(), <span class="built_in">format</span>=<span class="string">"{message}"</span>)</span><br></pre></td></tr></table></figure>
<p>是否想要拦截到Loguru接收器的标准日志消息?</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">InterceptHandler</span>(logging.Handler):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">emit</span>(<span class="params">self, record: logging.LogRecord</span>) -> <span class="literal">None</span>:</span><br><span class="line"> <span class="comment"># Get corresponding Loguru level if it exists.</span></span><br><span class="line"> level: <span class="built_in">str</span> | <span class="built_in">int</span></span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> level = logger.level(record.levelname).name</span><br><span class="line"> <span class="keyword">except</span> ValueError:</span><br><span class="line"> level = record.levelno</span><br><span class="line"></span><br><span class="line"> <span class="comment"># Find caller from where originated the logged message.</span></span><br><span class="line"> frame, depth = inspect.currentframe(), <span class="number">0</span></span><br><span class="line"> <span class="keyword">while</span> frame <span class="keyword">and</span> (depth == <span class="number">0</span> <span class="keyword">or</span> frame.f_code.co_filename == logging.__file__):</span><br><span class="line"> frame = frame.f_back</span><br><span class="line"> depth += <span class="number">1</span></span><br><span class="line"></span><br><span class="line"> logger.opt(depth=depth, exception=record.exc_info).log(level, record.getMessage())</span><br><span class="line"></span><br><span class="line">logging.basicConfig(handlers=[InterceptHandler()], level=<span class="number">0</span>, force=<span class="literal">True</span>)</span><br></pre></td></tr></table></figure>
<h2 id="通过环境变量的个性化默认值"><a href="#通过环境变量的个性化默认值" class="headerlink" title="通过环境变量的个性化默认值"></a>通过环境变量的个性化默认值</h2><p>是否不喜欢默认的日志记录器格式?喜欢另一种DEBUG颜色?没问题:</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># Linux / OSX</span></span><br><span class="line">export LOGURU_FORMAT=<span class="string">"{time} | <lvl>{message}</lvl>"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Windows</span></span><br><span class="line">setx LOGURU_DEBUG_COLOR <span class="string">"<green>"</span></span><br></pre></td></tr></table></figure>
<h2 id="方便的解析器"><a href="#方便的解析器" class="headerlink" title="方便的解析器"></a>方便的解析器</h2><p>从生成的日志中提取特定信息通常很有用,这就是Loguru提供parse()方法帮助处理日志和正则表达式的原因。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">pattern = <span class="string">r"(?P<time>.*) - (?P<level>[0-9]+) - (?P<message>.*)"</span> <span class="comment"># Regex with named groups</span></span><br><span class="line">caster_dict = <span class="built_in">dict</span>(time=dateutil.parser.parse, level=<span class="built_in">int</span>) <span class="comment"># Transform matching groups</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> groups <span class="keyword">in</span> logger.parse(<span class="string">"file.log"</span>, pattern, cast=caster_dict):</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"Parsed:"</span>, groups)</span><br><span class="line"> <span class="comment"># {"level": 30, "message": "Log example", "time": datetime(2018, 12, 09, 11, 23, 55)}</span></span><br></pre></td></tr></table></figure>
<h2 id="详尽的通知"><a href="#详尽的通知" class="headerlink" title="详尽的通知"></a>详尽的通知</h2><p>Loguru可以很容易地与一流的<a href="https://github.com/notifiers/notifiers">notifiers</a>库(必须单独安装)组合在一起,以便在程序意外失败时接收电子邮件或发送许多其他类型的通知。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> notifiers</span><br><span class="line"></span><br><span class="line">params = {</span><br><span class="line"> <span class="string">"username"</span>: <span class="string">"[email protected]"</span>,</span><br><span class="line"> <span class="string">"password"</span>: <span class="string">"abc123"</span>,</span><br><span class="line"> <span class="string">"to"</span>: <span class="string">"[email protected]"</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment"># Send a single notification</span></span><br><span class="line">notifier = notifiers.get_notifier(<span class="string">"gmail"</span>)</span><br><span class="line">notifier.notify(message=<span class="string">"The application is running!"</span>, **params)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Be alerted on each error message</span></span><br><span class="line"><span class="keyword">from</span> notifiers.logging <span class="keyword">import</span> NotificationHandler</span><br><span class="line"></span><br><span class="line">handler = NotificationHandler(<span class="string">"gmail"</span>, defaults=params)</span><br><span class="line">logger.add(handler, level=<span class="string">"ERROR"</span>)</span><br></pre></td></tr></table></figure>
]]></content>
<categories>
<category>开发框架</category>
<category>Loguru</category>
</categories>
<tags>
<tag>Python</tag>
</tags>
</entry>
<entry>
<title>从PyPI安装Superset</title>
<url>/2024/12/25/%E4%BB%8EPyPI%E5%AE%89%E8%A3%85Superset/</url>
<content><![CDATA[<p>本文主要记录本人从PyPI安装Superset的过程和遇到的问题。从PyPI安装Superset,首先应先创建虚拟环境。可以参考我的另外两篇博文,学习Python和虚拟环境的管理:</p>
<ul>
<li><a href="https://www.zhangjc.com/2024/12/17/Python-%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86/">Python版本管理</a></li>
<li><a href="https://www.zhangjc.com/2024/12/22/Python%E8%99%9A%E6%8B%9F%E7%8E%AF%E5%A2%83%E7%AE%A1%E7%90%86/">Python虚拟环境管理</a></li>
</ul>
<p>Superset目前不支持Python3.12。具体错误信息和原因见我的另外一篇博客:<a href="https://www.zhangjc.com/2024/12/25/Superset%E6%9A%82%E4%B8%8D%E6%94%AF%E6%8C%81Python3-12/">Superset暂不支持Python 3.12</a>。</p>
<h1 id="操作系统依赖"><a href="#操作系统依赖" class="headerlink" title="操作系统依赖"></a>操作系统依赖</h1><p>Superset 在其元数据库中存储数据库连接信息。为此,我们使用加密 Python 库对连接密码进行加密。不幸的是,这个库具有操作系统级别的依赖。</p>
<h2 id="Debian-and-Ubuntu"><a href="#Debian-and-Ubuntu" class="headerlink" title="Debian and Ubuntu"></a>Debian and Ubuntu</h2><p>下面的命令将确保安装所需的依赖:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt-get install build-essential libssl-dev libffi-dev python-dev python-pip libsasl2-dev libldap2-dev default-libmysqlclient-dev</span><br></pre></td></tr></table></figure>
<p>在Ubuntu 20.04中,以下命令将确保安装所需的依赖:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt-get install build-essential libssl-dev libffi-dev python3-dev python3-pip libsasl2-dev libldap2-dev default-libmysqlclient-dev</span><br></pre></td></tr></table></figure>
<h1 id="Python-虚拟环境"><a href="#Python-虚拟环境" class="headerlink" title="Python 虚拟环境"></a>Python 虚拟环境</h1><p>创建并激活虚拟环境:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 使用 Python 3.11版本</span></span><br><span class="line">$ python -V</span><br><span class="line">Python 3.11.9</span><br><span class="line">$ pyenv virtualenv superset</span><br><span class="line">created virtual environment CPython3.11.9.final.0-64 <span class="keyword">in</span> 268ms</span><br><span class="line"> creator CPython3Posix(dest=/home/zhangjc/.pyenv/versions/3.11.9/envs/superset, clear=False, no_vcs_ignore=False, global=False)</span><br><span class="line"> seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/home/zhangjc/.local/share/virtualenv)</span><br><span class="line"> added seed packages: pip==24.2, setuptools==72.2.0, wheel==0.44.0</span><br><span class="line"> activators BashActivator,CShellActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator</span><br><span class="line">$ pyenv activate superset</span><br><span class="line">(superset) zhangjc@frin:~$ python -V</span><br><span class="line">Python 3.11.9</span><br></pre></td></tr></table></figure>
<h1 id="安装和初始化Superset"><a href="#安装和初始化Superset" class="headerlink" title="安装和初始化Superset"></a>安装和初始化Superset</h1><blockquote>
<p>如果选择使用MySQL作为Superset的元数据库,参见我的另外一篇博客进行配置:<a href="https://www.zhangjc.com/2024/12/25/Superset%E9%85%8D%E7%BD%AE/">Superset配置</a>。连接 MySQL 时可能会出现异常,见我的另外一篇博客:<a href="https://www.zhangjc.com/2024/12/25/Superset%E8%BF%9E%E6%8E%A5MySQL%E5%BC%82%E5%B8%B8/">连接MySQL异常</a>。</p>
</blockquote>
<p>首先,从安装 apache-superset 开始:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">pip install apache-superset</span><br></pre></td></tr></table></figure>
<p>然后,首先配置以下三项:</p>
<ul>
<li>FLASK_APP</li>
<li>SECRET_KEY。注意,环境变量名称是SUPERSET_SECRET_KEY,配置文件中的名称是SECRET_KEY。可以使用命令openssl rand -base64 42生成SECRET_KEY。</li>
<li>配置MySQL连接串。</li>
</ul>
<p>不设置FLASK_APP会提示以下错误信息:</p>
<figure class="highlight text"><table><tr><td class="code"><pre><span class="line">$ superset db upgrade</span><br><span class="line">Usage: superset [OPTIONS] COMMAND [ARGS]...</span><br><span class="line">Try 'superset --help' for help.</span><br><span class="line"></span><br><span class="line">Error: Could not locate a Flask application. Use the 'flask --app' option, 'FLASK_APP' environment variable, or a 'wsgi.py' or 'app.py' file in the current directory.</span><br></pre></td></tr></table></figure>
<p>不设置SECRET_KEY会提示以下错误信息:</p>
<figure class="highlight text"><table><tr><td class="code"><pre><span class="line">$ superset db upgrade</span><br><span class="line">--------------------------------------------------------------------------------</span><br><span class="line"> WARNING</span><br><span class="line">--------------------------------------------------------------------------------</span><br><span class="line">A Default SECRET_KEY was detected, please use superset_config.py to override it.</span><br><span class="line">Use a strong complex alphanumeric string and use a tool to help you generate </span><br><span class="line">a sufficiently random sequence, ex: openssl rand -base64 42</span><br><span class="line">--------------------------------------------------------------------------------</span><br><span class="line">--------------------------------------------------------------------------------</span><br><span class="line">Refusing to start due to insecure SECRET_KEY</span><br></pre></td></tr></table></figure>
<p>以下是设置和初始化数据库过程:</p>
<figure class="highlight text"><table><tr><td class="code"><pre><span class="line">$ export FLASK_APP=superset</span><br><span class="line">$ openssl rand -base64 42</span><br><span class="line">exxaWNURDIvZuqF8O75ngU+rgIXpAxkwO5AwWSKOzrIeHFwY2EvA9OPt</span><br><span class="line">$ export SUPERSET_SECRET_KEY='exxaWNURDIvZuqF8O75ngU+rgIXpAxkwO5AwWSKOzrIeHFwY2EvA9OPt'</span><br><span class="line">$ export SUPERSET_CONFIG_PATH=/home/zhangjc/github/superset/superset_config.py</span><br></pre></td></tr></table></figure>
<p>superset_config.py文件内容如下:</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">SQLALCHEMY_DATABASE_URI = <span class="string">'mysql://superset:Superset123@localhost/superset?unix_socket=/tmp/mysql.sock'</span></span><br></pre></td></tr></table></figure>
<p>在连接串中的密码部分带有特殊字符“@”时,执行superset db upgrade时会出现错误。这是因为<a href="https://www.zhangjc.com/2024/12/28/Superset%E9%85%8D%E7%BD%AE%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5%E4%B8%B2%E5%AF%86%E7%A0%81%E4%B8%8D%E8%83%BD%E5%8C%85%E5%90%AB/">Superset配置数据库连接串密码不能包含@</a>。</p>
<p>安装MySQL驱动并初始化数据库:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ pip install mysqlclient</span><br><span class="line">$ superset db upgrade</span><br></pre></td></tr></table></figure>
<p>通过运行以下命令完成安装:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 在元数据库中创建一个管理用户(使用“admin”作为用户名加载示例),根据提示输入信息即可创建完毕。</span></span><br><span class="line">superset fab create-admin</span><br><span class="line"></span><br><span class="line"><span class="comment"># 加载样例</span></span><br><span class="line">superset load_examples</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建默认的角色和权限</span></span><br><span class="line">superset init</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动调试模式的服务器</span></span><br><span class="line">superset run -p 8088 --with-threads --reload --debugger</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category>大数据</category>
<category>数据可视化</category>
</categories>
<tags>
<tag>Python</tag>
<tag>Superset</tag>
<tag>大数据</tag>
<tag>数据可视化</tag>
</tags>
</entry>
<entry>
<title>Superset暂不支持Python3.12</title>
<url>/2024/12/25/Superset%E6%9A%82%E4%B8%8D%E6%94%AF%E6%8C%81Python3-12/</url>
<content><![CDATA[<p>版本如下:</p>
<ul>
<li>Superset 4.0.2</li>
<li>Python 3.12.5</li>
</ul>
<p>报错信息如下:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ pip install apache-superset</span><br><span class="line"><span class="comment"># 安装过程信息省略</span></span><br><span class="line">......</span><br><span class="line"><span class="comment"># 以下是错误信息部分,内容也很多</span></span><br><span class="line">ERROR: Exception:</span><br><span class="line">Traceback (most recent call last): </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_internal/cli/base_command.py"</span>, line 105, <span class="keyword">in</span> _run_wrapper </span><br><span class="line"> status = _inner_run() </span><br><span class="line"> ^^^^^^^^^^^^ </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_internal/cli/base_command.py"</span>, line 96, <span class="keyword">in</span> _inner_run </span><br><span class="line"> <span class="built_in">return</span> self.run(options, args) </span><br><span class="line"> ^^^^^^^^^^^^^^^^^^^^^^^ </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_internal/cli/req_command.py"</span>, line 67, <span class="keyword">in</span> wrapper </span><br><span class="line"> <span class="built_in">return</span> func(self, options, args) </span><br><span class="line"> ^^^^^^^^^^^^^^^^^^^^^^^^^ </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_internal/commands/install.py"</span>, line 379, <span class="keyword">in</span> run </span><br><span class="line"> requirement_set = resolver.resolve( </span><br><span class="line"> ^^^^^^^^^^^^^^^^^ </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/resolver.py"</span>, line 95, <span class="keyword">in</span> resolve </span><br><span class="line"> result = self._result = resolver.resolve( </span><br><span class="line"> ^^^^^^^^^^^^^^^^^ </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers.py"</span>, line 546, <span class="keyword">in</span> resolve </span><br><span class="line"> state = resolution.resolve(requirements, max_rounds=max_rounds) </span><br><span class="line"> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers.py"</span>, line 427, <span class="keyword">in</span> resolve </span><br><span class="line"> failure_causes = self._attempt_to_pin_criterion(name) </span><br><span class="line"> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers.py"</span>, line 239, <span class="keyword">in</span> _attempt_to_pin_criterion </span><br><span class="line"> criteria = self._get_updated_criteria(candidate) </span><br><span class="line"> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers.py"</span>, line 230, <span class="keyword">in</span> _get_updated_criteria </span><br><span class="line"> self._add_to_criteria(criteria, requirement, parent=candidate) </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers.py"</span>, line 173, <span class="keyword">in</span> _add_to_criteria </span><br><span class="line"> <span class="keyword">if</span> not criterion.candidates: </span><br><span class="line"> ^^^^^^^^^^^^^^^^^^^^ </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_vendor/resolvelib/structs.py"</span>, line 156, <span class="keyword">in</span> __bool__ </span><br><span class="line"> <span class="built_in">return</span> bool(self._sequence) </span><br><span class="line"> ^^^^^^^^^^^^^^^^^^^^ </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py"</span>, line 174, <span class="keyword">in</span> __bool__ </span><br><span class="line"> <span class="built_in">return</span> any(self) </span><br><span class="line"> ^^^^^^^^^ </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py"</span>, line 162, <span class="keyword">in</span> <genexpr> </span><br><span class="line"> <span class="built_in">return</span> (c <span class="keyword">for</span> c <span class="keyword">in</span> iterator <span class="keyword">if</span> <span class="built_in">id</span>(c) not <span class="keyword">in</span> self._incompatible_ids) </span><br><span class="line"> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py"</span>, line 53, <span class="keyword">in</span> _iter_built </span><br><span class="line"> candidate = func() </span><br><span class="line"> ^^^^^^ </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/factory.py"</span>, line 186, <span class="keyword">in</span> _make_candidate_from_link </span><br><span class="line"> base: Optional[BaseCandidate] = self._make_base_candidate_from_link( </span><br><span class="line"> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/factory.py"</span>, line 232, <span class="keyword">in</span> _make_base_candidate_from_link </span><br><span class="line"> self._link_candidate_cache[<span class="built_in">link</span>] = LinkCandidate( </span><br><span class="line"> ^^^^^^^^^^^^^^ </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/candidates.py"</span>, line 303, <span class="keyword">in</span> __init__ </span><br><span class="line"> super().__init__( </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/candidates.py"</span>, line 158, <span class="keyword">in</span> __init__ </span><br><span class="line"> self.dist = self._prepare() </span><br><span class="line"> ^^^^^^^^^^^^^^^ </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/candidates.py"</span>, line 235, <span class="keyword">in</span> _prepare </span><br><span class="line"> dist = self._prepare_distribution() </span><br><span class="line"> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/candidates.py"</span>, line 314, <span class="keyword">in</span> _prepare_distribution </span><br><span class="line"> <span class="built_in">return</span> preparer.prepare_linked_requirement(self._ireq, parallel_builds=True) </span><br><span class="line"> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_internal/operations/prepare.py"</span>, line 527, <span class="keyword">in</span> prepare_linked_requirement </span><br><span class="line"> <span class="built_in">return</span> self._prepare_linked_requirement(req, parallel_builds) </span><br><span class="line"> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_internal/operations/prepare.py"</span>, line 642, <span class="keyword">in</span> _prepare_linked_requirement </span><br><span class="line"> dist = _get_prepared_distribution( </span><br><span class="line"> ^^^^^^^^^^^^^^^^^^^^^^^^^^^ </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_internal/operations/prepare.py"</span>, line 72, <span class="keyword">in</span> _get_prepared_distribution </span><br><span class="line"> abstract_dist.prepare_distribution_metadata( </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_internal/distributions/sdist.py"</span>, line 56, <span class="keyword">in</span> prepare_distribution_metadata </span><br><span class="line"> self._install_build_reqs(finder) </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_internal/distributions/sdist.py"</span>, line 126, <span class="keyword">in</span> _install_build_reqs </span><br><span class="line"> build_reqs = self._get_build_requires_wheel() </span><br><span class="line"> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_internal/distributions/sdist.py"</span>, line 103, <span class="keyword">in</span> _get_build_requires_wheel </span><br><span class="line"> <span class="built_in">return</span> backend.get_requires_for_build_wheel() </span><br><span class="line"> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_internal/utils/misc.py"</span>, line 706, <span class="keyword">in</span> get_requires_for_build_wheel </span><br><span class="line"> <span class="built_in">return</span> super().get_requires_for_build_wheel(config_settings=cs) </span><br><span class="line"> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_impl.py"</span>, line 166, <span class="keyword">in</span> get_requires_for_build_wheel </span><br><span class="line"> <span class="built_in">return</span> self._call_hook(<span class="string">'get_requires_for_build_wheel'</span>, { </span><br><span class="line"> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_impl.py"</span>, line 321, <span class="keyword">in</span> _call_hook </span><br><span class="line"> raise BackendUnavailable(data.get(<span class="string">'traceback'</span>, <span class="string">''</span>)) </span><br><span class="line">pip._vendor.pyproject_hooks._impl.BackendUnavailable: Traceback (most recent call last): </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/envs/superset3.12/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py"</span>, line 77, <span class="keyword">in</span> _build_backend </span><br><span class="line"> obj = import_module(mod_path) </span><br><span class="line"> ^^^^^^^^^^^^^^^^^^^^^^^ </span><br><span class="line"> File <span class="string">"/home/zhangjc/.pyenv/versions/3.12.5/lib/python3.12/importlib/__init__.py"</span>, line 90, <span class="keyword">in</span> import_module </span><br><span class="line"> <span class="built_in">return</span> _bootstrap._gcd_import(name[level:], package, level) </span><br><span class="line"> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ </span><br><span class="line"> File <span class="string">"<frozen importlib._bootstrap>"</span>, line 1387, <span class="keyword">in</span> _gcd_import </span><br><span class="line"> File <span class="string">"<frozen importlib._bootstrap>"</span>, line 1360, <span class="keyword">in</span> _find_and_load </span><br><span class="line"> File <span class="string">"<frozen importlib._bootstrap>"</span>, line 1310, <span class="keyword">in</span> _find_and_load_unlocked </span><br><span class="line"> File <span class="string">"<frozen importlib._bootstrap>"</span>, line 488, <span class="keyword">in</span> _call_with_frames_removed </span><br><span class="line"> File <span class="string">"<frozen importlib._bootstrap>"</span>, line 1387, <span class="keyword">in</span> _gcd_import </span><br><span class="line"> File <span class="string">"<frozen importlib._bootstrap>"</span>, line 1360, <span class="keyword">in</span> _find_and_load </span><br><span class="line"> File <span class="string">"<frozen importlib._bootstrap>"</span>, line 1331, <span class="keyword">in</span> _find_and_load_unlocked </span><br><span class="line"> File <span class="string">"<frozen importlib._bootstrap>"</span>, line 935, <span class="keyword">in</span> _load_unlocked </span><br><span class="line"> File <span class="string">"<frozen importlib._bootstrap_external>"</span>, line 995, <span class="keyword">in</span> exec_module </span><br><span class="line"> File <span class="string">"<frozen importlib._bootstrap>"</span>, line 488, <span class="keyword">in</span> _call_with_frames_removed </span><br><span class="line"> File <span class="string">"/tmp/pip-build-env-u9f30fu2/overlay/lib/python3.12/site-packages/setuptools/__init__.py"</span>, line 10, <span class="keyword">in</span> <module> </span><br><span class="line"> import distutils.core </span><br><span class="line">ModuleNotFoundError: No module named <span class="string">'distutils'</span></span><br></pre></td></tr></table></figure>
<p>上面的报错信息很长,其实核心问题就出在“distutils”模块上。根据Python官方文档,<a href="https://docs.python.org/zh-cn/3.11/library/distutils.html#module-distutils">distutils</a>已被弃用并计划在Python 3.12中移除。</p>
<p>Superset 4.0.2在Python 3.11下可以正常使用,参见我的另外一篇博客:<a href="https://www.zhangjc.com/2024/12/25/%E4%BB%8EPyPI%E5%AE%89%E8%A3%85Superset/">从PyPI安装Superset</a>。</p>
]]></content>
<categories>
<category>大数据</category>
<category>数据可视化</category>
</categories>
<tags>
<tag>Python</tag>
<tag>Superset</tag>
<tag>大数据</tag>
<tag>数据可视化</tag>
</tags>
</entry>
<entry>
<title>Superset配置</title>
<url>/2024/12/25/Superset%E9%85%8D%E7%BD%AE/</url>
<content><![CDATA[<h1 id="superset-config-py"><a href="#superset-config-py" class="headerlink" title="superset_config.py"></a>superset_config.py</h1><p>通过config.py模块,Superset对外暴露上百个配置参数。在这个模块中,可以找到这些参数,和这些参数的默认值。以注释的方式,这个模块也可以作为一个丰富的文档。</p>
<p>为了配置自己的应用,需要创建自己的配置模块,用来重载改写这些参数的默认值。为了不改变核心模块,一般需要创建自己的配置模块(通常是一个名称为superset_config.py的文件)。添加这个文件到PYTHONPATH或创建环境变量SUPERSET_CONFIG_PATH并指定superset_config.py的完整路径。</p>
<p>例如,在Linux系统下部署Superset,superset_config.py文件在/app目录下,环境变量如下:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">export</span> SUPERSET_CONFIG_PATH=/app/superset_config.py</span><br></pre></td></tr></table></figure>
<blockquote>
<p>通常只拷贝核心模块<a href="https://github.com/apache/superset/blob/master/superset/config.py">superset/config.py</a>中需要修改的参数及注释到自己的superset_config.py文件中。</p>
</blockquote>
<p><a href="https://github.com/apache/superset/blob/master/superset/config.py">superset/config.py</a>中定义的所有参数和默认值都可以在自己的superset_config.py中修改。</p>
<p>因为superset_config.py作为一个Flask的配置模块,它也可以用来修改Flask本身的设置,包括Superset绑定的Flask扩展,如flask-wtf、flask-caching、flask-migrate、和flask-appbuilder。每个扩展都提供了复杂的配置项。Flask App Builder是Superset使用的Web框架,也提供了很多配置。可以查阅<a href="https://flask-appbuilder.readthedocs.org/en/latest/config.html">Flask App Builder</a>文档获取如何配置的信息。</p>
<h1 id="指定SECRET-KEY"><a href="#指定SECRET-KEY" class="headerlink" title="指定SECRET_KEY"></a>指定SECRET_KEY</h1><h2 id="初始化SECRET-KEY"><a href="#初始化SECRET-KEY" class="headerlink" title="初始化SECRET_KEY"></a>初始化SECRET_KEY</h2><p>Superset需要一个用户设置的SECRET_KEY值才可以启动。这个必要条件是在 <a href="https://preset.io/blog/superset-security-update-default-secret_key-vulnerability/">2.1.0 版本的强制安全配置中添加的。</a>在superset_config.py中添加一个强SECRET_KEY,如:</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">SECRET_KEY = <span class="string">'YOUR_OWN_RANDOM_GENERATED_SECRET_KEY'</span></span><br></pre></td></tr></table></figure>
<p>用以下命令生成一个强安全key值:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">openssl rand -<span class="built_in">base64</span> 42</span><br></pre></td></tr></table></figure>
<blockquote>
<p><strong>使用强安全key值</strong><br>这个key值被用来为会话的cookies签名,以及把存储在Superset应用元数据库中的敏感信息加密。自己的部署必须使用一个复杂、唯一的key值。</p>
</blockquote>
<h2 id="更换SECRET-KEY"><a href="#更换SECRET-KEY" class="headerlink" title="更换SECRET_KEY"></a>更换SECRET_KEY</h2><p>如果想修改SECRET_KEY的值,添加已经已经存在的SECRET_KEY值到superset_config.py文件中的PREVIOUS_SECRET_KEY=中,并且赋新值给SECRET_KEY=。可以用下面的命令查看SECRET_KEY的当前值:</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">superset shell</span><br><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> current_app; <span class="built_in">print</span>(current_app.config[<span class="string">"SECRET_KEY"</span>])</span><br></pre></td></tr></table></figure>
<p>设置完后保存superset_config.py文件,并执行命令superset re-encrypt-secrets。</p>
<h1 id="配置元数据库"><a href="#配置元数据库" class="headerlink" title="配置元数据库"></a>配置元数据库</h1><p>Superset需要数据库来存储它管理的信息,如图表、面板及其他很多东西的定义。</p>
<p>默认的,Superset使用SQLite。SQLite是一个独立的、单个文件的数据库,提供简单且快速开始使用Superset的方式(不需要任何额外安装)。但是,由于安全、扩展性及数据完整性的原因,SQLite不推荐用作生产环境。在生产环境中使用支持的数据库非常重要。</p>
<p>Superset支持以下数据库引擎/版本:</p>
<table>
<thead>
<tr>
<th>数据库引擎</th>
<th>支持的版本</th>
</tr>
</thead>
<tbody><tr>
<td><a href="https://www.postgresql.org/">PostgreSQL</a></td>
<td>10.X, 11.X, 12.X, 13.X, 14.X, 15.X</td>
</tr>
<tr>
<td><a href="https://www.mysql.com/">MySQL</a></td>
<td>5.7, 8.X</td>
</tr>
</tbody></table>
<p>使用下面的数据库驱动和连接设置:</p>
<table>
<thead>
<tr>
<th>数据库</th>
<th>PyPI包</th>
<th>连接串</th>
</tr>
</thead>
<tbody><tr>
<td><a href="https://www.postgresql.org/">PostgreSQL</a></td>
<td>pip install psycopg2</td>
<td>postgresql://<UserName>:<DBPassword>@<Database Host>/<Database Name></td>
</tr>
<tr>
<td><a href="https://www.mysql.com/">MySQL</a></td>
<td>pip install mysqlclient</td>
<td>mysql://<UserName>:<DBPassword>@<Database Host>/<Database Name></td>
</tr>
</tbody></table>
<p>要设置Superset的元数据库,在superset_config.py配置文件中设置SQLALCHEMY_DATABASE_URI配置项。</p>
]]></content>
<categories>
<category>大数据</category>
<category>数据可视化</category>
</categories>
<tags>
<tag>Python</tag>
<tag>Superset</tag>
<tag>大数据</tag>
<tag>数据可视化</tag>
</tags>
</entry>
<entry>
<title>无法连接本地MySQL</title>
<url>/2024/12/26/%E6%97%A0%E6%B3%95%E8%BF%9E%E6%8E%A5%E6%9C%AC%E5%9C%B0MySQL/</url>
<content><![CDATA[<p>在Linux系统下,MySQL客户端有两种不同的方法连接本地<strong>mysqld</strong>服务:</p>
<ul>
<li>通过Unix socket文件。这是一个在文件系统中的文件,默认/tmp/mysql.sock。</li>
<li>使用TCP/IP连接。TCP/IP需要个网络端口,默认3306。</li>
</ul>
<p>Unix socket文件比TCP/IP连接速度快,但只能连接客户端所在的同一台服务器上的MySQL。连接MySQL时不指定主机名,或者指定特定的主机名<strong>localhost</strong>,则使用Unix socket文件连接。</p>
<p>error (2002) <code>Can't connect to ...</code>错误一般可能是以下原因导致的:</p>
<ul>
<li>MySQL服务未启动。</li>
<li>连接使用了错误的Unix socket文件。</li>
<li>连接使用了错误的TCP/IP端口号。</li>
</ul>
<p>原因知道了,排查起来也就简单了。</p>
<p>首先检查服务器上是否有<strong>mysqld</strong>进程,确定MySQL服务已启动。如果没有这个进程,则说明MySQL服务未启动。Linux系统查看方式如下:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ ps xa | grep mysqld</span><br><span class="line"> 13974 ? S 0:01 /bin/sh /usr/bin/mysqld_safe</span><br><span class="line"> 14051 ? Sl 112:17 /opt/mysql/bin/mysqld --basedir=/opt/mysql --datadir=/opt/mysql/data --plugin-dir=/opt/mysql/lib/plugin --user=mysql --log-error=frin.err --pid-file=frin.pid</span><br><span class="line"> 151147 pts/4 S+ 0:00 grep mysqld</span><br></pre></td></tr></table></figure>
<p>如果<strong>mysqld</strong>进程在运行,可以用下面两种连接MySQL的方式,使用<strong>mysqladmin</strong>工具查看MySQL端口或Unix socket文件信息。</p>
<p>执行以下命令,用户是superset,可以看出连接方式是通过Unix socket(Localhost via UNIX socket),Unix socket文件是/tmp/mysql.sock。</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ mysqladmin -hlocalhost -usuperset -p version</span><br><span class="line">Enter password: </span><br><span class="line">mysqladmin Ver 8.0.34 <span class="keyword">for</span> Linux on x86_64 (MySQL Community Server - GPL)</span><br><span class="line">Copyright (c) 2000, 2023, Oracle and/or its affiliates.</span><br><span class="line"></span><br><span class="line">Oracle is a registered trademark of Oracle Corporation and/or its</span><br><span class="line">affiliates. Other names may be trademarks of their respective</span><br><span class="line">owners.</span><br><span class="line"></span><br><span class="line">Server version 8.0.34</span><br><span class="line">Protocol version 10</span><br><span class="line">Connection Localhost via UNIX socket</span><br><span class="line">UNIX socket /tmp/mysql.sock</span><br><span class="line">Uptime: 31 min 49 sec</span><br><span class="line"></span><br><span class="line">Threads: 2 Questions: 3537 Slow queries: 0 Opens: 191 Flush tables: 3 Open tables: 110 Queries per second avg: 1.852</span><br></pre></td></tr></table></figure>
<blockquote>
<p>在Linux中临时目录/tmp下的文件会被定期清理,所以/tmp/mysql.sock也会被清理掉,导致MySQL无法访问。关于如何保护或修改MySQL的Unix socket文件以后的文章再分项。</p>
</blockquote>
<p>执行以下命令,用户同样是superset,可以看出连接方式是TCP/IP(127.0.0.1 via TCP/IP),端口是3306。</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ mysqladmin -h127.0.0.1 -usuperset -p version</span><br><span class="line">Enter password: </span><br><span class="line">mysqladmin Ver 8.0.34 <span class="keyword">for</span> Linux on x86_64 (MySQL Community Server - GPL)</span><br><span class="line">Copyright (c) 2000, 2023, Oracle and/or its affiliates.</span><br><span class="line"></span><br><span class="line">Oracle is a registered trademark of Oracle Corporation and/or its</span><br><span class="line">affiliates. Other names may be trademarks of their respective</span><br><span class="line">owners.</span><br><span class="line"></span><br><span class="line">Server version 8.0.34</span><br><span class="line">Protocol version 10</span><br><span class="line">Connection 127.0.0.1 via TCP/IP</span><br><span class="line">TCP port 3306</span><br><span class="line">Uptime: 38 min 34 sec</span><br><span class="line"></span><br><span class="line">Threads: 2 Questions: 4252 Slow queries: 0 Opens: 191 Flush tables: 3 Open tables: 110 Queries per second avg: 1.837</span><br></pre></td></tr></table></figure>
<p>理解了MySQL连接的两种方式,出现连接不上的问题基本可以从以下几个方面排查:</p>
<ul>
<li>MySQL服务:<ul>
<li>MySQL服务是否运行正常。</li>
<li>MySQL启动没有指定<a href="https://dev.mysql.com/doc/refman/8.4/en/server-system-variables.html#sysvar_skip_networking">skip_networking</a>参数,这会导致MySQL不会接受任何TCP/IP连接。</li>
<li>MySQL服务绑定的IP地址,如果设置了127.0.0.1,则只允许本机访问,不接受远程访问。</li>
</ul>
</li>
<li>Unix socket文件相关:<ul>
<li>文件是否存在。</li>
<li>文件权限是否适当。</li>
<li>客户端用的Unix socket文件是否正确。</li>
</ul>
</li>
<li>网络相关:<ul>
<li>检查网络是否连通,可以用ping命令。</li>
<li>检查端口是否可访问,可以用telnet命令。</li>
<li>检查是否防火墙屏蔽了访问。</li>
</ul>
</li>
</ul>
<p>参考文档:<a href="https://dev.mysql.com/doc/refman/8.4/en/can-not-connect-to-server.html">Can’t connect to [local] MySQL server</a></p>
]]></content>
<categories>
<category>数据库</category>
<category>MySQL</category>
</categories>
<tags>
<tag>MySQL</tag>
<tag>数据库</tag>
</tags>
</entry>
<entry>
<title>Superset连接本地MySQL异常</title>
<url>/2024/12/25/Superset%E8%BF%9E%E6%8E%A5%E6%9C%AC%E5%9C%B0MySQL%E5%BC%82%E5%B8%B8/</url>
<content><![CDATA[<p>将Superset的元数据库配置为MySQL,配置方法见<a href="https://www.zhangjc.com/2024/12/25/Superset%E9%85%8D%E7%BD%AE/">Superset配置</a>。配置完成后,在启动Superset的时候,出现以下异常信息:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">2024-09-01 16:28:36,614:ERROR:flask_appbuilder.security.sqla.manager:DB Creation and initialization failed: (MySQLdb.OperationalError) (2002, <span class="string">"Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)"</span>)</span><br><span class="line">(Background on this error at: https://sqlalche.me/e/14/e3q8)</span><br><span class="line">Loaded your LOCAL configuration at [/home/zhangjc/github/superset/superset_config.py]</span><br></pre></td></tr></table></figure>
<p>这个错误跟连接MySQL的方式有关,详细信息见<a href="https://www.zhangjc.com/2024/12/26/%E6%97%A0%E6%B3%95%E8%BF%9E%E6%8E%A5%E6%9C%AC%E5%9C%B0MySQL/">无法连接本地MySQL</a>。我的连接配置信息如下:</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">SQLALCHEMY_DATABASE_URI = <span class="string">'mysql://superset:superset%4001@localhost/superset'</span></span><br></pre></td></tr></table></figure>
<p>如果想快速解决问题,可将连接串中的<strong>localhost</strong>改为<strong>127.0.0.1</strong>。但作为技术人,还是需要深究下。</p>
<p>根据<a href="https://www.zhangjc.com/2024/12/25/Superset%E9%85%8D%E7%BD%AE/">Superset配置</a>中的描述,连接MySQL用的是<strong>mysqlclient</strong>驱动,问题可能出现在该驱动上。用以下方式进行验证:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ python</span><br><span class="line">Python 3.11.9 (main, Aug 28 2024, 23:13:21) [GCC 8.3.0] on linux</span><br><span class="line">Type <span class="string">"help"</span>, <span class="string">"copyright"</span>, <span class="string">"credits"</span> or <span class="string">"license"</span> <span class="keyword">for</span> more information.</span><br><span class="line">>>> import MySQLdb</span><br><span class="line">>>> from MySQLdb import _mysql</span><br><span class="line">>>> db=_mysql.connect(host=<span class="string">"localhost"</span>,user=<span class="string">"superset"</span>, password=<span class="string">"superset@01"</span>, database=<span class="string">"superset"</span>)</span><br><span class="line">Traceback (most recent call last):</span><br><span class="line"> File <span class="string">"<stdin>"</span>, line 1, <span class="keyword">in</span> <module></span><br><span class="line">MySQLdb.OperationalError: (2002, <span class="string">"Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)"</span>)</span><br><span class="line">>>> db=_mysql.connect(host=<span class="string">"localhost"</span>,user=<span class="string">"superset"</span>, password=<span class="string">"superset@01"</span>, database=<span class="string">"superset"</span>, unix_socket=<span class="string">'/tmp/mysql.sock'</span>)</span><br><span class="line">>>></span><br></pre></td></tr></table></figure>
<p>从上面的验证可以看出,问题出在<strong>mysqlclient</strong>默认使用的<strong>Unix socket</strong>文件与MySQL服务的不同,在指定正确的<strong>Unix socket</strong>文件后,问题解决。</p>
<p>如何在superset中配置指定MySQL连接的Unix socket文件呢?查看MySQL官方文档,可以看到在连接串中通过<strong>socket</strong>参数设置。</p>
<p>参见:<a href="https://dev.mysql.com/doc/refman/8.4/en/connecting-using-uri-or-key-value-pairs.html#connection-parameters-base">Base Connection Parameters</a></p>
<p>修改Superset元数据库连接串配置,如下:</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">SQLALCHEMY_DATABASE_URI = <span class="string">'mysql://superset:superset%4001@localhost/superset?socket=(/tmp/mysql.sock)'</span></span><br></pre></td></tr></table></figure>
<p>启动Superset出现以下错误:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ superset run -p 8088 --with-threads --reload --debugger</span><br><span class="line">Loaded your LOCAL configuration at [/home/zhangjc/github/superset/superset_config.py]</span><br><span class="line">logging was configured successfully</span><br><span class="line">2024-09-05 11:45:16,426:INFO:superset.utils.logging_configurator:logging was configured successfully</span><br><span class="line">2024-09-05 11:45:16,433:INFO:root:Configured event logger of <span class="built_in">type</span> <class <span class="string">'superset.utils.log.DBEventLogger'</span>></span><br><span class="line">/home/zhangjc/frin/python/venvs/superset/lib/python3.11/site-packages/flask_limiter/extension.py:333: UserWarning: Using the in-memory storage <span class="keyword">for</span> tracking rate limits as no storage was explicitly specified. This is not recommended <span class="keyword">for</span> production use. See: https://flask-limiter.readthedocs.io#configuring-a-storage-backend <span class="keyword">for</span> documentation about configuring the storage backend.</span><br><span class="line"> warnings.warn(</span><br><span class="line">2024-09-05 11:45:16,438:ERROR:flask_appbuilder.security.sqla.manager:DB Creation and initialization failed: <span class="string">'socket'</span> is an invalid keyword argument <span class="keyword">for</span> connect()</span><br></pre></td></tr></table></figure>
<p>说明mysqlclient模块使用的参数名与MySQL文档中的不一致。但查询mysqlclient文档并未发现Unix socket文件参数的说明😂。没办法,只能使用码农基本能力 - 读源代码了😁。原来mysqlclient中的名称是unix_socket:</p>
<p><img src="/images/202412/09.png"></p>
<p>修改连接串如下:</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">SQLALCHEMY_DATABASE_URI = <span class="string">'mysql://superset:superset%4001@localhost/superset?unix_socket=/tmp/mysql.sock'</span></span><br></pre></td></tr></table></figure>
<p>再次启动Superset,Done!✌️✌️✌️</p>
]]></content>
<categories>
<category>数据库</category>
<category>MySQL</category>
</categories>
<tags>
<tag>Python</tag>
<tag>Superset</tag>
<tag>大数据</tag>
<tag>数据可视化</tag>
<tag>MySQL</tag>
<tag>数据库</tag>
</tags>
</entry>
<entry>
<title>Superset配置数据库连接串密码不能包含@</title>
<url>/2024/12/28/Superset%E9%85%8D%E7%BD%AE%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5%E4%B8%B2%E5%AF%86%E7%A0%81%E4%B8%8D%E8%83%BD%E5%8C%85%E5%90%AB/</url>
<content><![CDATA[<p>如果Superset元数据库连接配置SQLALCHEMY_DATABASE_URI中密码部分包含@符,则在执行superset db upgrade时异常,即使对@做转义也不能解决问题。例如,元数据配置参数SQLALCHEMY_DATABASE_URI如下:</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">SQLALCHEMY_DATABASE_URI = <span class="string">'mysql://superset1:superset%4001@localhost/superset1?unix_socket=/tmp/mysql.sock&charset=utf8'</span></span><br></pre></td></tr></table></figure>
<p>异常信息如下:</p>
<figure class="highlight text"><table><tr><td class="code"><pre><span class="line"> File "/home/zhangjc/.pyenv/versions/3.11.9/envs/superset/lib/python3.11/site-packages/MySQLdb/connections.py", line 195, in __init__</span><br><span class="line"> super().__init__(*args, **kwargs2)</span><br><span class="line">sqlalchemy.exc.OperationalError: (MySQLdb.OperationalError) (2005, "Unknown MySQL server host '01@localhost' (-2)")</span><br><span class="line">(Background on this error at: https://sqlalche.me/e/14/e3q8)</span><br></pre></td></tr></table></figure>
<p>通过以上信息可以看出,所以进行了转义,但仍然将密码部分的@符作为密码和主机的分隔符。</p>
<p>该问题目前唯一的解决方法是修改元数据库访问密码,不要包含@符。这是因为Superset Server与数据库迁移组件Alembic之间对连接串的处理存在冲突。</p>
<p>Superset Server会将连接串转换为sqlalchemy.engine.url.URL进行处理,所以不会导致解析错误,参考代码如下:</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">get_uri</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="keyword">if</span> <span class="variable language_">self</span>._bind <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">self</span>._app.config[<span class="string">'SQLALCHEMY_DATABASE_URI'</span>]</span><br><span class="line"> binds = <span class="variable language_">self</span>._app.config.get(<span class="string">'SQLALCHEMY_BINDS'</span>) <span class="keyword">or</span> ()</span><br><span class="line"> <span class="keyword">assert</span> <span class="variable language_">self</span>._bind <span class="keyword">in</span> binds, \</span><br><span class="line"> <span class="string">'Bind %r is not specified. Set it in the SQLALCHEMY_BINDS '</span> \</span><br><span class="line"> <span class="string">'configuration variable'</span> % <span class="variable language_">self</span>._bind</span><br><span class="line"> <span class="keyword">return</span> binds[<span class="variable language_">self</span>._bind]</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_engine</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="keyword">with</span> <span class="variable language_">self</span>._lock:</span><br><span class="line"> uri = <span class="variable language_">self</span>.get_uri()</span><br><span class="line"> echo = <span class="variable language_">self</span>._app.config[<span class="string">'SQLALCHEMY_ECHO'</span>]</span><br><span class="line"> <span class="keyword">if</span> (uri, echo) == <span class="variable language_">self</span>._connected_for:</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">self</span>._engine</span><br><span class="line"></span><br><span class="line"> sa_url = make_url(uri)</span><br><span class="line"> sa_url, options = <span class="variable language_">self</span>.get_options(sa_url, echo)</span><br><span class="line"> <span class="variable language_">self</span>._engine = rv = <span class="variable language_">self</span>._sa.create_engine(sa_url, options)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> _record_queries(<span class="variable language_">self</span>._app):</span><br><span class="line"> _EngineDebuggingSignalEvents(<span class="variable language_">self</span>._engine,</span><br><span class="line"> <span class="variable language_">self</span>._app.import_name).register()</span><br><span class="line"></span><br><span class="line"> <span class="variable language_">self</span>._connected_for = (uri, echo)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> rv</span><br></pre></td></tr></table></figure>
<p>Alembic使用Python内置配置文件解析器<a href="https://docs.python.org/zh-cn/3.11/library/configparser.html#module-configparser">configparser</a>处理配置信息,处理时只能用字符串,对SQLALCHEMY_DATABASE_URI处理前会先解码,所以导致解析不正确。参考代码如下:</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">DATABASE_URI = current_app.config[<span class="string">"SQLALCHEMY_DATABASE_URI"</span>]</span><br><span class="line"><span class="keyword">if</span> <span class="string">"sqlite"</span> <span class="keyword">in</span> DATABASE_URI:</span><br><span class="line"> logger.warning(</span><br><span class="line"> <span class="string">"SQLite Database support for metadata databases will \</span></span><br><span class="line"><span class="string"> be removed in a future version of Superset."</span></span><br><span class="line"> )</span><br><span class="line">decoded_uri = urllib.parse.unquote(DATABASE_URI)</span><br><span class="line">config.set_main_option(<span class="string">"sqlalchemy.url"</span>, decoded_uri)</span><br></pre></td></tr></table></figure>
<blockquote>
<p>虽然Alembic中可以用%%对%进行转义,但这样做会导致Superset Server解析配置异常。</p>
</blockquote>
<p>可参考的链接如下:</p>
<ul>
<li><a href="https://github.com/sqlalchemy/alembic/issues/1279">https://github.com/sqlalchemy/alembic/issues/1279</a></li>
<li><a href="https://github.com/sqlalchemy/alembic/discussions/1280">https://github.com/sqlalchemy/alembic/discussions/1280</a></li>
</ul>
]]></content>
<categories>
<category>大数据</category>
<category>数据可视化</category>
</categories>
<tags>
<tag>Python</tag>
<tag>Superset</tag>
<tag>大数据</tag>
<tag>数据可视化</tag>
<tag>MySQL</tag>
</tags>
</entry>
<entry>
<title>Windows PySide6 MySQL驱动问题</title>
<url>/2025/01/01/Windows-PySide6-MySQL%E9%A9%B1%E5%8A%A8%E9%97%AE%E9%A2%98/</url>
<content><![CDATA[<h1 id="环境信息"><a href="#环境信息" class="headerlink" title="环境信息"></a>环境信息</h1><ul>
<li>Windows 11 家庭中文版 64 位操作系统 23H2</li>
<li>Python 3.12.4</li>
<li>PySide 6.6.0</li>
<li>MySQL 8.0.38</li>
</ul>
<h1 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a>问题描述</h1><p>安装PySide 6.6.0后连接MySQL时出现以下错误信息:</p>
<figure class="highlight text"><table><tr><td class="code"><pre><span class="line">D:\gitee\Scrat> & d:/gitee/Scrat/.venv/Scripts/python.exe d:/gitee/Scrat/main.py</span><br><span class="line">QSqlDatabase: QMYSQL driver not loaded</span><br><span class="line">QSqlDatabase: available drivers: QSQLITE QMIMER QODBC QPSQL</span><br></pre></td></tr></table></figure>
<p>根据错误信息可以清晰看出,在可获取的驱动中中没有MySQL,这是因为PySide6没有内置MySQL的驱动。官方或网上通常的解决方案是自行编译。</p>
<h1 id="问题解决"><a href="#问题解决" class="headerlink" title="问题解决"></a>问题解决</h1><p>这里介绍一个不需要自行编译的解决方法,参考网址:<a href="https://github.com/thecodemonkey86/qt_mysql_driver">https://github.com/thecodemonkey86/qt_mysql_driver</a>。注意,一定要下载跟自己安装的PySide一致的版本。</p>
<p>按照说明将驱动文件拷贝至个人安装目录,如我在虚拟环境下安装的位置:<img src="/images/202501/01.png"></p>
<p>完成后,再次运行出现以下错误:</p>
<figure class="highlight text"><table><tr><td class="code"><pre><span class="line">D:\gitee\Scrat> & d:/gitee/Scrat/.venv/Scripts/python.exe d:/gitee/Scrat/main.py</span><br><span class="line">QSqlDatabase: QMYSQL driver not loaded</span><br><span class="line">QSqlDatabase: available drivers: QSQLITE QMIMER QMARIADB QMYSQL QODBC QPSQL</span><br></pre></td></tr></table></figure>
<p>可以看出MySQL驱动已经存在,未加载成功是因为缺少其他库。从MySQL安装目录下,拷贝以下3个动态库到PySide安装目录,如下图:<br><img src="/images/202501/02.png"><br><img src="/images/202501/03.png"></p>
<p>拷贝后的目录截图:<img src="/images/202501/04.png"></p>
]]></content>
<categories>
<category>开发框架</category>
<category>Qt</category>
</categories>
<tags>
<tag>Python</tag>
<tag>MySQL</tag>
<tag>PySide6</tag>
<tag>Qt</tag>
</tags>
</entry>
<entry>
<title>VS Code中Python代码调试</title>
<url>/2025/01/05/VS-Code%E4%B8%ADPython%E4%BB%A3%E7%A0%81%E8%B0%83%E8%AF%95/</url>
<content><![CDATA[<p>Python扩展通过<a href="https://marketplace.visualstudio.com/items?itemName=ms-python.debugpy">Python调试器扩展</a>支持多种类型的Python应用调试。简短了解基本调试,见<a href="https://code.visualstudio.com/docs/python/python-tutorial#_configure-and-run-the-debugger">Tutorial - Configure and run the debugger</a>。此外也查看<a href="https://code.visualstudio.com/docs/python/tutorial-flask">Flask tutorial</a>。两个手册展示了像设置断点和步入代码核心技能。</p>
<p>学习通用的调试特性,像检查变量、设置断点和其他不依赖语言的活动,回顾<a href="https://code.visualstudio.com/docs/editor/debugging">VS Code debugging</a>。</p>
<p>本文主要讲述Python特定的调试配置,包含特定应用类型和远程调试必要的步骤。</p>
<h1 id="Python调试器扩展"><a href="#Python调试器扩展" class="headerlink" title="Python调试器扩展"></a>Python调试器扩展</h1><p>在VS Code中,<a href="https://marketplace.visualstudio.com/items?itemName=ms-python.debugpy">Python调试器扩展</a>会随着<a href="https://marketplace.visualstudio.com/items?itemName=ms-python.python">Python扩展</a>一起安装。Python调试器基于实现了Python调试适配器协议的<a href="https://pypi.org/project/debugpy/">debugpy</a>模块,可以调试多种类型的Python应用,包括脚本、Web应用、远程进程等。</p>
<p>验证上面插件安装的方法是,打开<strong>扩展面板</strong>(<code>Ctrl+Shift+X</code>),在搜索框中输入<code>@installed python debugger</code>,在结果列表中可以看到Python和Python调试器插件。</p>
<p><img src="/images/202501/05.png"></p>
<h1 id="初始配置"><a href="#初始配置" class="headerlink" title="初始配置"></a>初始配置</h1><p>VS Code通过配置控制调试行为。这些配置在<code>launch.json</code>文件中进行定义。<code>launch.json</code>文件存储在工作空间的<code>.vscode</code>目录中。</p>
<p>初始化调试配置,首先打开<strong>运行和调试面板</strong>(<code>Ctrl+Shift+D</code>)。如果还未做配置,会看到<strong>运行和调试</strong>按钮和创建launch.json文件的超链接。</p>
<p>按照以下步骤创建<code>launch.json</code>文件:</p>
<ol>
<li><p>点击创建launch.json文件链接。</p>
</li>
<li><p>从调试选项列表中选择<strong>Python Debugger</strong>。</p>
</li>
</ol>
<p><img src="/images/202501/06.png"></p>
<ol start="3">
<li>从弹出的配置类型选择列表中,根据需要选择:如果调试单个Python脚本,选择<strong>Python文件</strong>。其他类型及选项在列表中有说明,如下:</li>
</ol>
<p><img src="/images/202501/07.png"></p>
<ol start="4">
<li>这时候Python调试器扩展会创建并打开<code>launch.json</code>文件。根据上一步骤选择的类型,文件中已经预先定义了一些配置,如本例中选择的<strong>Python文件</strong>。可以根据需要修改文件内容添加配置(如添加参数)。</li>
</ol>
<p><img src="/images/202501/08.png"></p>
<blockquote>
<p>下文会讲解配置细节和不同类型应用的配置。</p>
</blockquote>
<h1 id="附加配置"><a href="#附加配置" class="headerlink" title="附加配置"></a>附加配置</h1><p>默认的,VS Code只展示Python调试器扩展最通用的配置。可点击其他配置下拉框中选择<strong>添加配置</strong>命令或点击<code>launch.json</code>文件编辑器中的<strong>添加配置</strong>按钮,VS Code会展示所有可选配置的列表(注意选择<strong>Python调试程序</strong>选项):</p>
<p><img src="/images/202501/09.png"></p>
<p>选择带有参数的Python文件后,效果如下:</p>
<p><img src="/images/202501/10.png"></p>
<p>调试的时候,状态栏会显示当前的配置和当前的解释器。点击状态栏上的配置,可以选择其他配置。</p>
<p><img src="/images/202501/11.png"></p>
<p>在工作空间下,默认调试器跟其他扩展一样使用相同的解释器。如果调试器要使用不同的解释器,设置<code>launch.json</code>中的<code>python</code>值,或者使用状态栏的Python解释器指示器。</p>
<h1 id="基础调试"><a href="#基础调试" class="headerlink" title="基础调试"></a>基础调试</h1><p>如果只是调试Python脚本,最简单的方式是脚本编辑器运行下拉列表中选择<strong>Python调试程序:调试Python文件</strong>。</p>
<p><img src="/images/202501/12.png"></p>
<p>如果调试使用Flask、Django或FastAPI开发的web应用,Python调试器扩展在<strong>运行和调试面板</strong>中,提供了根据项目结构提供动态调试配置的选项。</p>
<p><img src="/images/202501/13.png"></p>
<p>如果调试其他类型应用,可以通过点击<strong>运行和调试面板</strong>中的<strong>运行和调试</strong>按钮。</p>
<p><img src="/images/202501/14.png"></p>
<p>当没有设置配置时,VS Code会给出调试选项,选择适合的选项快速进入调试。有两个常用的选项,<strong>Python文件</strong>配置运行当前打开的Python文件,或者<strong>使用进程ID进行附加</strong>配置连结调试器到一个运行中的进程。添加配置后,就可以在列表中选择,并使用<strong>开始调试(F5)</strong>进行调试。</p>
<p><img src="/images/202501/15.png"></p>
<h1 id="命令行调试"><a href="#命令行调试" class="headerlink" title="命令行调试"></a>命令行调试</h1><p>如果安装了<code>debugpy</code>也可以在命令行运行调试器。</p>
<h2 id="安装debugpy"><a href="#安装debugpy" class="headerlink" title="安装debugpy"></a>安装debugpy</h2><p>使用以下命令安装<a href="https://pypi.org/project/debugpy/">debugpy</a>:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ pip install --upgrade debugpy</span><br><span class="line">Looking <span class="keyword">in</span> indexes: https://mirrors.aliyun.com/pypi/simple/</span><br><span class="line">Collecting debugpy</span><br><span class="line"> Downloading https://mirrors.aliyun.com/pypi/packages/fd/b6/ee71d5e73712daf8307a9e85f5e39301abc8b66d13acd04dfff1702e672e/debugpy-1.8.5-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.4 MB)</span><br><span class="line"> ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.4/1.4 MB 11.6 MB/s eta 0:00:00</span><br><span class="line">Installing collected packages: debugpy</span><br><span class="line">Successfully installed debugpy-1.8.5</span><br><span class="line"></span><br><span class="line">[notice] A new release of pip is available: 24.0 -> 24.2</span><br><span class="line">[notice] To update, run: pip install --upgrade pip</span><br></pre></td></tr></table></figure>
<blockquote>
<p>虽然不是必须的,但使用虚拟环境仍然是推荐的最佳实践。在VS Code中,可以打开命令面板(<code>Ctrl+Shift+P</code>),运行**Python: 创建环境…**命令创建虚拟环境。</p>
</blockquote>
<h2 id="命令行语法"><a href="#命令行语法" class="headerlink" title="命令行语法"></a>命令行语法</h2><p>调试器命令行语法如下:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">python -m debugpy</span><br><span class="line"> --listen | --connect</span><br><span class="line"> [<host>:]<port></span><br><span class="line"> [--wait-for-client]</span><br><span class="line"> [--configure-<name> <value>]...</span><br><span class="line"> [--log-to <path>] [--log-to-stderr]</span><br><span class="line"> <filename> | -m <module> | -c <code> | --pid <pid></span><br><span class="line"> [<arg>]...</span><br></pre></td></tr></table></figure>
<h2 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h2><p>在命令行,可以使用一个特定的端口(5678)启动要调试脚本的调试器。如下:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">python -m debugpy --listen 5678 ./myscript.py</span><br></pre></td></tr></table></figure>
<p>这个示例假设这个脚本是长期运行且忽略<code>--wait-for-client</code>标识,意味着脚本不会等待客户端连结。</p>
<p>然后,VS Code调试器插件使用下面的配置连结。</p>
<figure class="highlight text"><table><tr><td class="code"><pre><span class="line">{</span><br><span class="line"> "name": "Python Debugger: Attach",</span><br><span class="line"> "type": "debugpy",</span><br><span class="line"> "request": "attach",</span><br><span class="line"> "connect": {</span><br><span class="line"> "host": "localhost",</span><br><span class="line"> "port": 5678</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<blockquote>
<p><strong>提示</strong>:可以指定<strong>监听</strong>的主机,默认使用127.0.0.1。</p>
</blockquote>
<p>如果调试远程机器上的远程代码或运行在docker容器中的代码,需要修改前文的 CLI 命令指定一台机器。</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">python -m debugpy --listen 0.0.0.0:5678 ./myscript.py</span><br></pre></td></tr></table></figure>
<p>关联的配置文件看起来如下:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="string">"name"</span>: <span class="string">"Attach"</span>,</span><br><span class="line"> <span class="string">"type"</span>: <span class="string">"debugpy"</span>,</span><br><span class="line"> <span class="string">"request"</span>: <span class="string">"attach"</span>,</span><br><span class="line"> <span class="string">"connect"</span>: {</span><br><span class="line"> <span class="string">"host"</span>: <span class="string">"remote-machine-name"</span>, // replace this with remote machine name</span><br><span class="line"> <span class="string">"port"</span>: 5678</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<blockquote>
<p><strong>提示</strong>:注意,如果指定了<code>127.0.0.1</code>或<code>localhost</code>以外的机器,就打开了一个从任何机器允许访问的一个端口,这将带来安全风险。在远程调试时,需确保已采取了适合的预防措施,例如SSH隧道。</p>
</blockquote>
<h2 id="命令行选项"><a href="#命令行选项" class="headerlink" title="命令行选项"></a>命令行选项</h2><table>
<thead>
<tr>
<th><strong>标识</strong></th>
<th><strong>选项</strong></th>
<th><strong>描述</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>–listen</strong> or <strong>–connect</strong></td>
<td><code>[<host>:]<port></code></td>
<td><strong>必须</strong>。为调试适配器服务器指定主机地址和端口号,来等待连接(–listen)或连接一个等待连接的客户端(–connect) 。这跟VS Code调试配置使用的地址相同。默认,主机地址是<code>localhost (127.0.0.1)</code>。</td>
</tr>
<tr>
<td><strong>–wait-for-client</strong></td>
<td>none</td>
<td><strong>可选</strong>。 指定从调试服务器有一个连接连入时才运行代码。这个设置允许从第一行代码开始调试。</td>
</tr>
<tr>
<td><strong>–log-to</strong></td>
<td><code><path></code></td>
<td><strong>可选</strong>。指定保存日志的一个已存在目录。</td>
</tr>
<tr>
<td><strong>–log-to-stderr</strong></td>
<td>none</td>
<td><strong>可选</strong>。启用 debugpy 写日志到标准错误输出。</td>
</tr>
<tr>
<td><strong>–pid</strong></td>
<td><code><pid></code></td>
<td><strong>可选</strong>。指定一个已经运行中待调试服务器进入的进程。</td>
</tr>
<tr>
<td><strong>–configure-<name></strong></td>
<td><code><value></code></td>
<td><strong>可选</strong>。设置在客户端连接前调试服务器必须知道的调试属性。这些属性可以直接在<em>调用</em>配置中直接使用,但是必须以这种方式设置的<em>连结</em>配置。例如,不想调试服务器自动引入连结的进程创建的子进程,使用<code>--configure-subProcess false</code>。</td>
</tr>
</tbody></table>
<blockquote>
<p><strong>提示</strong>:可以使用<code>[<arg>]</code>将命令行参数传递给已启动的应用程序。</p>
</blockquote>
<h1 id="通过网络连结调试"><a href="#通过网络连结调试" class="headerlink" title="通过网络连结调试"></a>通过网络连结调试</h1><h2 id="本地脚本调试"><a href="#本地脚本调试" class="headerlink" title="本地脚本调试"></a>本地脚本调试</h2><p>在某些实例中,需要调试由另外一个进程在本地调用的Python脚本。例如,调试一个web服务器运行的一些处理特定任务的脚本。在这个例子中,一旦这个脚本被调用则需要VS Code调试器就连结它:</p>
<ol>
<li>运行VS Code,打开这个脚本所在的文件夹或工作空间,如果<code>launch.json</code>不存在则创建它。</li>
<li>在脚本代码中,添加下面的代码并保存:</li>
</ol>
<figure class="highlight text"><table><tr><td class="code"><pre><span class="line">import debugpy</span><br><span class="line"></span><br><span class="line"># 5678 is the default attach port in the VS Code debug configurations. Unless a host and port are specified, host defaults to 127.0.0.1</span><br><span class="line">debugpy.listen(5678)</span><br><span class="line">print("Waiting for debugger attach")</span><br><span class="line">debugpy.wait_for_client()</span><br><span class="line">debugpy.breakpoint()</span><br><span class="line">print('break on this line')</span><br></pre></td></tr></table></figure>
<ol start="3">
<li>使用<strong>终端:创建新的终端</strong>命令创建一个终端,终端会自动激活脚本选择的环境。</li>
<li>在终端中安装debugpy(见<strong>安装debugpy</strong>一节)。</li>
<li>在终端中,用该脚本开启Python,如<code>python3 myscript.py</code>。会看到包含在代码中的“Waiting for debugger attach”信息,脚本会在<code>debugpy.wait_for_client()</code>处等待调用。</li>
<li>切换至<strong>运行和调试(<code>Ctrl+Shift+D</code>)</strong>面板,从调试器下拉列表中选择合适的配置进入调试器。</li>
<li>调试器在<code>debugpy.breakpoint()</code>处等待调用,在该处可以正常的使用调试器。也可以使用UI界面设置其他断点,而不是用<code>debugpy.breakpoint()</code>。</li>
</ol>
<h2 id="用SSH调试远程脚本"><a href="#用SSH调试远程脚本" class="headerlink" title="用SSH调试远程脚本"></a>用SSH调试远程脚本</h2><p>远程调试能力允许在本地VS Code内单步调试远程服务器上运行的程序,而不需要在远程服务器安装VS Code。为了安全,在连接远程服务器调试时需要使用安全连接,如SSH。</p>
<blockquote>
<p><strong>提示</strong>:在Windows系统上,需要安装<a href="https://learn.microsoft.com/windows-server/administration/openssh/openssh_install_firstuse">Windows 10 OpenSSH</a>才可以使用<code>ssh</code>命令。</p>
</blockquote>
<p>下面的步骤简要列出了设置SSH隧道的步骤。与开放一个可公共访问的端口比,SSH隧道允许安全的在本地连接远程机器进行工作。</p>
<p><strong>在远程主机上:</strong></p>
<ol>
<li>要启用端口,打开<code>sshd_config</code>配置文件(Linux服务器在<code>/etc/ssh/</code>下,Windows在<code>%programfiles(x86)%/openssh/etc</code>下),添加或修改以下内容:</li>
</ol>
<figure class="highlight text"><table><tr><td class="code"><pre><span class="line">AllowTcpForwarding yes</span><br></pre></td></tr></table></figure>
<blockquote>
<p><strong>提示</strong>:AllowTcpForwarding默认值为yes,可能不需要修改。</p>
</blockquote>
<ol start="2">
<li>如果必须添加<code>AllowTcpForwarding</code>,则需要重启SSH服务。在Linux/macOS上,运行<code>sudo service ssh restart</code>;在Windows上,运行<code>services.msc</code>,在服务列表中选择OpenSSH或sshd。</li>
</ol>
<p><strong>在本地主机上:</strong></p>
<ol>
<li>执行<code>ssh -2 -L sourceport:localhost:destinationport -i identityfile user@remoteaddress</code>创建一个SSH隧道,为<code>destinationport</code>选择一个端口,并在<code>user@remoteaddress</code>中指定合适的用户名和远程主机的IP。例如,在IP地址1.2.3.4上使用端口5678,命令为<code>ssh -2 -L 5678:localhost:5678 -i identityfile [email protected]</code>。可以使用<code>-i</code>标识指定认证文件的路径。</li>
<li>确定在SSH会话中可以看到提示信息。</li>
<li>在VS Code工作空间中,在<code>launch.json</code>文件中为远程调试创建配置项,设置匹配<code>ssh</code>命令使用的端口号,并设置主机为<code>localhost</code>。使用<code>localhost</code>是因为已经设置了SSH隧道。</li>
</ol>
<figure class="highlight text"><table><tr><td class="code"><pre><span class="line">{</span><br><span class="line"> "name": "Python Debugger: Attach",</span><br><span class="line"> "type": "debugpy",</span><br><span class="line"> "request": "attach",</span><br><span class="line"> "port": 5678,</span><br><span class="line"> "host": "localhost",</span><br><span class="line"> "pathMappings": [</span><br><span class="line"> {</span><br><span class="line"> "localRoot": "${workspaceFolder}", // Maps C:\Users\user1\project1</span><br><span class="line"> "remoteRoot": "." // To current working directory ~/project1</span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>开始调试</strong></p>
<p>现在已经设置了连接远程主机的SSH隧道,可以开始调试了。</p>
<ol>
<li>远程和本地主机:保证可以获取相同的代码。</li>
<li>远程和本地主机:安装debugpy(见<strong>安装debugpy</strong>一节)。</li>
<li>远程主机:有两种指定连结远程主机的方式。</li>
</ol>
<p> a. 在源代码中,添加下面的代码,<code>address</code>替换为远程主机的IP和端口(这里用1.2.3.4作为示例)。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> debugpy</span><br><span class="line"></span><br><span class="line"><span class="comment"># Allow other computers to attach to debugpy at this IP address and port.</span></span><br><span class="line">debugpy.listen((<span class="string">'1.2.3.4'</span>, <span class="number">5678</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># Pause the program until a remote debugger is attached</span></span><br><span class="line">debugpy.wait_for_client()</span><br></pre></td></tr></table></figure>
<p> 在<code>listen</code>中应该使用远程主机的私有IP。然后可以正常启动程序,直到调试器连结使其暂停。</p>
<p> b. 通过debugpy启动远程进程,例如:</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">python3 -m debugpy --listen 1.2.3.4:5678 --wait-for-client -m myproject</span><br></pre></td></tr></table></figure>
<p> 以上命令使用<code>python3</code>启动<code>myproject</code>,使用远程主机的私有IP<code>1.2.3.4</code>,并监听端口<code>5678</code>(也可以不使用<code>-m</code>,而是指定文件路径启动远程Python进程,例如<code>./hello.py</code>)。</p>
<ol start="4">
<li>本地主机:<strong>只有在上面列出的远程主机上的代码被修改</strong>,然后在远程主机的源代码中,拷贝添加注释的相同代码。添加这些代码确保远程和本地主机上的代码每行都是匹配的。</li>
</ol>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment">#import debugpy</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Allow other computers to attach to debugpy at this IP address and port.</span></span><br><span class="line"><span class="comment">#debugpy.listen(('1.2.3.4', 5678))</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Pause the program until a remote debugger is attached</span></span><br><span class="line"><span class="comment">#debugpy.wait_for_client()</span></span><br></pre></td></tr></table></figure>
<ol start="5">
<li>本地机器:切换至<strong>运行和调试(Ctrl+Shift+D)</strong>面板,选择<strong>Python Debugger: 远程附加</strong>配置。</li>
<li>本地机器:在打算调试的代码位置设置一个断点。</li>
<li>本地机器:使用修改过的<strong>Python Debugger:附加</strong>配置文件和启动调试按钮启动VS Code调试器。VS Code会停在本地设置的断点位置,允许单步调试代码、检查变量并执行其他调试动作。在<strong>Debug Console</strong>中输入的表达式也会在远程主机上运行。</li>
</ol>
<blockquote>
<p>像来自<code>print</code>表达式的内容,输出到标准输出的文本,会出现在两边的主机上。但是其他输出,像来自matplotlib包的图像,只会出现在远程主机上。</p>
</blockquote>
<ol start="8">
<li>在远程调试时,调试工具栏如下面:</li>
</ol>
<p><img src="/images/202501/16.png"></p>
<p> 在工具栏上,断开连接按钮(<code>Shift+F5</code>)可以停止调试器,并允许远程程序运行完成。重启按钮(<code>Ctrl+Shift+F5</code>)重启调试器本地主机上的调试器,但不会重启远程程序。只有在已经重启远程程序并需要重新连结调试器时使用重启按钮。</p>
<h1 id="设置配置项"><a href="#设置配置项" class="headerlink" title="设置配置项"></a>设置配置项</h1><p>当第一次创建<code>launch.json</code>时,有两种标准配置可以在集成终端(VS Code内)或外部终端(VS Code外部)运行编辑器中活动的文件:</p>
<figure class="highlight text"><table><tr><td class="code"><pre><span class="line">{</span><br><span class="line"> "configurations": [</span><br><span class="line"> {</span><br><span class="line"> "name": "Python Debugger: Current File (Integrated Terminal)",</span><br><span class="line"> "type": "debugpy",</span><br><span class="line"> "request": "launch",</span><br><span class="line"> "program": "${file}",</span><br><span class="line"> "console": "integratedTerminal"</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> "name": "Python Debugger: Current File (External Terminal)",</span><br><span class="line"> "type": "debugpy",</span><br><span class="line"> "request": "launch",</span><br><span class="line"> "program": "${file}",</span><br><span class="line"> "console": "externalTerminal"</span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>具体设置将在下面的章节中描述。也可以添加其他不在标准配置中的设置,如<code>args</code>。</p>
<blockquote>
<p><strong>提示</strong>:在项目中,创建一个运行特定启动文件的配置会很有帮助。例如,启动调试器时总是使用参数<code>--port 1593</code>调用<code>startup.py</code>,如下创建一个配置:</p>
<figure class="highlight text"><table><tr><td class="code"><pre><span class="line">{</span><br><span class="line"> "name": "Python Debugger: startup.py",</span><br><span class="line"> "type": "debugpy",</span><br><span class="line"> "request": "launch",</span><br><span class="line"> "program": "${workspaceFolder}/startup.py",</span><br><span class="line"> "args" : ["--port", "1593"]</span><br><span class="line">},</span><br></pre></td></tr></table></figure>
</blockquote>
<p><strong>name</strong></p>
<p>给展示在VS Code调试配置下拉列表中的配置命名。</p>
<p><strong>type</strong></p>
<p>标识调试器的类型;保持该设置为<code>debugpy</code>来调试Python代码。</p>
<p><strong>request</strong></p>
<p>指定开始调试的模式:</p>
<ul>
<li>launch:在指定为<code>program</code>的文件上启动调试器。</li>
<li>attach:附加调试器至已运行的一个进程。参见“通过网络连结调试”一节。</li>
</ul>
<p><strong>program</strong></p>
<p>提供Python程序入口模块(启动文件)的全路径。在默认配置中经常使用<code>${file}</code>,用作编辑器当前活动的文件。指定一个特定的启动文件时,需要确保不管打开的是哪个文件,都用同一个入口调用程序。例如:</p>
<figure class="highlight text"><table><tr><td class="code"><pre><span class="line">"program": "/Users/Me/Projects/MyProject/src/event_handlers/__init__.py",</span><br></pre></td></tr></table></figure>
<p>也可基于工作空间根目录设置相对路径。例如,工作空间根目录是<code>/Users/Me/Projects/MyProject</code>,则可以使用下面的配置:</p>
<figure class="highlight text"><table><tr><td class="code"><pre><span class="line">"program": "${workspaceFolder}/src/event_handlers/__init__.py",</span><br></pre></td></tr></table></figure>
<p><strong>module</strong></p>
<p>提供给被调试模块指定名称的能力,跟命令行运行时的<code>-m</code>参数类似。更多信息见<a href="https://docs.python.org/3/using/cmdline.html#cmdoption-m">Python.org</a>。</p>
<p><strong>python</strong></p>
<p>指向调试使用的Python解释器的全路径。</p>
<p>如果不指定,这个设置默认指向工作空间选择的解释器,跟<code>${command:python.interpreterPath}</code>等同。使用其他解释器,则指定<code>python</code>值为解释器的路径。</p>
<p>可选的,可以在不同平台上使用包含使用的Python解释器路径的环境变量,因此就不再需要目录路径了。</p>
<p>如果需要传递参数给Python解释器,可以使用<code>pythonArgs</code>属性。</p>
<p><strong>pythonArgs</strong></p>
<p>用语法<code>"pythonArgs": ["<arg 1>", "<arg 2>",...]</code>指定传递给Python解释器的参数。</p>
<p><strong>args</strong></p>
<p>指定传递给Python程序的参数。每个用空格分开的参数字符串需在引号内,如:</p>
<figure class="highlight text"><table><tr><td class="code"><pre><span class="line">"args": ["--quiet", "--norepeat", "--port", "1593"],</span><br></pre></td></tr></table></figure>
<p>如果想每次调试运行提供不同的参数,可以设置<code>args</code>为<code>${command:pickArgs}</code>。每次启动调试时,将提示输入变量。</p>
<p><strong>stopOnEntry</strong></p>
<p>设置为true时,在程序第一行代码被调试时中断调试器。如果忽略(默认)或设置为false,调试器运行程序至第一个断点。</p>
<p><strong>console</strong></p>
<p>在<code>redirectOutput</code>默认值没被修改时,指定程序输出如何展示。</p>
<table>