python多程序debug

calvincalvin發表於2024-10-03

程式碼除錯

問題闡述

最近遇到一個python debug多程序的問題

有一個程序A,這個程序會fork出8個程序B,fork join結束後,又會fork出8個程序A。

假設按時間有序,我就只想斷fork出的第一個B和第一個程序A,怎麼做?(breakpoint just break only once)

類似於java多執行緒除錯的意思,只斷一個執行緒,all->thread,make default

斷點命中時不會阻塞其他程序

https://blog.csdn.net/u011781521/article/details/79251819

主要幾個問題點

  • 多個程序會同時刷log,特別亂,沒辦法過濾(加pid過濾,還算好解決)
  • 使用pdb斷點時,會同時block所有其他程序

除錯主流方式

首先python主流有兩類debug方式

第一類是透過ide,比如vscode、pycharm。

比較無腦、介面化的操作方式,但缺點也很明顯,很依賴一個可以控制的函式入口(例如main,ut入口)。

第二類是透過pdb,在相關程式碼片段中加breakpoint語句,等程序執行時自動觸發斷點,進行攔截。

適合一些比較複雜的程序衍生模式,不太依賴特定的函式入口。這種相對來說更精確一點,對依賴外部環境依賴小。

需要對pdb、gdb類似的東西有一定的瞭解。其次,多程序下並不太友好;pdb本身功能過於單一。

找了一下pdb的平替有pdb++、ipdb、rpdb。

rpdb就是remote,前兩個其實差不多,但是pdb的好處是,基本你不需要去改原來的pdb斷點,開箱即用。

pdb++還有啥好處見引文,但是還是存在多程序不友好的問題,會多次觸發斷點,一個命令語句會有較多的副作用,尤其在Smart command parsing加持下,基本就是不可除錯的狀態。

TL; DR

pdbpp(又稱 pdb++)是 Python 標準偵錯程式 pdb 的一個增強版本,為除錯體驗提供了許多改進和新功能。在功能上,它完全向後相容 pdb,但增加了許多便利的特性,簡化了除錯過程。以下是 pdbpp 相比於 pdb 提供的主要增強功能:

1. 彩色語法高亮(Syntax Highlighting)

pdbpp 使用 pygments 庫為程式碼和輸出提供彩色語法高亮顯示,使得偵錯程式輸出更加易讀,特別是在檢視複雜表示式和程式碼塊時。變數名、關鍵字、字串、數字等會有不同的顏色,幫助使用者快速定位資訊。

  • pdb:沒有顏色,輸出是純文字。
  • pdbpp:提供語法高亮,顏色區分不同型別的內容。

2. 更強大的自動補全(Tab Completion)

pdbpp 中,按下 Tab 鍵可以自動補全變數名、函式名、類名等,類似於現代 IDE 中的補全功能。這大大提高了除錯時的效率,特別是在處理複雜的物件或長命名時。

  • pdb:有限的自動補全功能。
  • pdbpp:增強的自動補全,支援更多命令、變數、物件屬性和方法。

3. 可配置的顯示選項(Sticky Mode)

pdbpp 提供一個非常強大的 "sticky mode",它可以在除錯過程中始終顯示當前程式碼的上下文。每次執行下一步時,pdbpp 會自動重新整理並顯示當前程式碼段的最新狀態,而不需要手動檢視程式碼上下文。

  • pdb:手動使用 listlonglist 檢視程式碼上下文。
  • pdbppsticky mode 持續顯示程式碼上下文,使用者更容易跟蹤執行的程式碼。

4. 更智慧的除錯命令

pdbpp 增強了原始 pdb 的一些常用命令,並增加了新的命令:

  • interact:允許你在偵錯程式中啟動一個互動式 Python shell,直接操作除錯時的上下文和變數。
  • longlist (ll):顯示當前函式的完整程式碼,而不僅僅是當前行的上下文。
  • sticky:啟用或禁用 sticky mode,在該模式下會持續顯示程式碼的上下文。
  • track:跟蹤異常,並顯示其完整的追溯資訊。
  • source:檢視某個函式或方法的完整原始碼。

5. 更靈活的配置和擴充套件

pdbpp 提供了更多的配置選項,允許你透過 .pdbrc 檔案自定義偵錯程式的行為,例如顏色、補全方式、命令別名等。

  • pdb:有限的配置選項。
  • pdbpp:支援使用者自定義配置檔案,靈活設定偵錯程式的行為。

6. 更好的 Python 3 支援

pdbpp 針對 Python 3 做了更多最佳化,支援 Python 3 的新語法和特性,如 asyncawait,並能更好地處理 Python 3 中的一些新特性。

7. 友好的錯誤資訊

pdbpp 提供了更加直觀的錯誤資訊格式,增強了顯示異常和回溯的方式,使除錯複雜的錯誤更加容易。

8. 可自定義的偵錯程式提示符

你可以透過 pdbpp 自定義偵錯程式的提示符(prompt)。這對某些開發者來說是個很方便的功能,特別是當你在長時間除錯時,能夠清楚知道當前的除錯狀態。

9. 顯示變數值的懸停功能

當你在 pdbpp 中檢視程式碼時,懸停在變數上會顯示其當前的值。這對於快速檢視變數的狀態非常有用,無需輸入變數名來檢視當前值。


小結

pdbpppdb 的一個增強版本,帶來了彩色高亮、自動補全、sticky mode、自定義配置等功能,大大提升了除錯體驗。如果你經常使用 pdb 進行除錯,pdbpp 將讓你的除錯過程更高效、愉快。

問題場景

現在我往我關心的程式碼裡,加入了斷點import pdb; pdb.set_trace()但是他連續觸發了8次,導致我沒辦法繼續除錯了,怎麼辦?

按了一次c直接所有其他程序都相應,我有沒有辦法只斷某一個程序(隨便哪個)?

比較直覺的會想到條件斷點,但是觸發條件的唯一值有哪些呢?

方案思路

天然唯一值,比如pid、ppid、deviceid、特定配置引數,process相關引數,持有的一些特殊資源(fd描述符)。

非天然唯一值,比如特定環境變數。

也可以考慮鎖,誰先搶佔到鎖,誰就一直持有這把鎖(至少能保證只有一個程序斷到),這個可以往一個特定檔案寫入pid實現。封裝一個get函式,這個函式嘗試讀取一個檔案,如果這個檔案不存在,先建立並寫入當前的程序id,再返回該id,如果這個檔案存在,直接返回檔案內容轉成int,上層只需要比較id和自身os.getpid()的一致性。

方案細節

方案A 判斷ppid

由於最開始只有一個父程序,所以用他作為ppid去判定。然而並不正確,子程序fork了8個,8個同時命中無效。

方案B 判斷pid

pid對10取餘,用一個magic number(比如0),判斷相等性,進行斷點。

(有效且粗暴的方式,但具有一定隨機性。這塊是由於程序pid的分配演算法,程序pid並不一定具有連續性,並且有一定隨機性,有可能出現無法命中或者多次命中的機率。)

方案C 父程序傳變數給子程序

只需要在子程序fork一開始傳入一個唯一值即可,這裡用到了環境變數。

首先最開始的父程序A程序是一個前臺程序,ctrl+c打斷時,會觸發一些multiprocessing和task相關的堆疊,其實可能A在執行過程中fork了其他所有程序(事實上並不對)。

嘗試檢索了一下相關庫,檢索multiprocessing關鍵字,確實能找到一句

ctx = multiprocessing.get_context("spawn") 
...
for i in range(5):
  item = ctx.Process(...)
  item.start()
...

嘗試在loop中,先設定os.environ['DEBUG']=i,fork出來的子程序會繼承(unix機制,子程序會繼承父程序的一些屬性)環境變數,嘗試用該環境變數做判斷,發現斷點斷不上,這個方案是有問題的。

原因是父程序A fork出了8個B,我們關心的斷點其實在B的程序中,fork完8個B程序,父程序A完成join,繼續開始fork8個A子程序,理論上這些斷點會生效,但是我們miss掉了斷點出現的時機。

方案D 父程序B讀取一些特定配置值(比如deviceid 0-8)做判斷

D.A 嘗試讀取一個config值裡的特定欄位,但發現這個config是動態生成的,他也不是一個單例(python中的單例不太一樣,強行弄意義不大),並且這個值載入的時機不明確,所以不能用

D.B 嘗試獲取該程序持有的一些資源、檔案描述符(如/dev/xxx),感覺有點麻煩,但是理論上是可行的

D.C 透過ps -ef觀察,其實程序B在啟動時有一些特定的唯一值入參,程序的啟動命令大概長process_b device_id 8 deivce_id xxxx,能明顯看出,第二個引數就是我們需要的唯一值。

嘗試透過sys.argv獲取第一個引數,直接判斷。發現sys.argv竟然是空的,那麼這些引數來自哪裡呢?

只有透過python命令列啟動的程序,才會有sys.argv,如果不是這種方式,比如用c啟動的命令列,他就不會有這個引數。

那如何獲取這個引數?

兩種方式,第一種取讀取/proc/{pid}/cmdline下的一些屬性,找到這個值,第二種

import psutil
p = psutil.Process()
cmdline = p.cmdline()

採用了第二種方式,再pdb++獲取了cmdline[1],並且和'1'比較測試了一下,是成功的。至此,完成了條件斷點。

小結

  1. 要注意觀察程序的建立方式和建立的流程(ps -ef, pstree)
  2. 明確需要斷點問題的界限
  3. 尋找一切與問題相關的上下文,儘可能將問題縮小在一個區間

TL;DR

程式碼閱讀&修改

container內python程式碼除錯

現在的ide普遍都支援ssh連線,理論上只要配置好了ssh服務,就可以直接連container,直接對container內的程式碼進行除錯。(直接用開發機的開發機環境是最好的,程式碼可以隨意點,都不會報錯和飄紅

ssh

安裝ssh服務(能用apt直接安的可以省這一步)

  1. 從網站隨意下一下原始碼包

https://www.openssh.com/portable.html

  1. 解壓進入到程式碼目錄
  2. ./configure 生成編譯需要的配置檔案
  3. make $(nproc) 編譯
  4. make install 安裝

配置

vim /etc/sshd/sshd_config

PermitRootLogin改成yes,password-auth也改成yes,埠隨便換一個,不要和宿主機撞

啟動

service ssh start(這塊不能用systemctl)

宿主機要把docker這個埠主動的暴露出去(這個在啟動的時候就要完成)

pycharm ssh配置連線

包太大,下載不了的問題參考

https://blog.csdn.net/qq_42534403/article/details/135969955

連線完效果大概像

基本和本地開發效果一致,比較惱火的是,他有時候不會儲存專案開啟前後的上下文,可能你做的一些mark或者pin都會丟失。

如果對library進行除錯,額外注意需要標註

pdb++ cheatsheet (via gpt4)

個人感覺比較好用的就是ll pp sticky,最好禁用Smart command parsing(真是自作聰明的行為),基本用著和解釋性語言的感覺差不多,像是直接在python中執行一樣

下面是一個方便參考的 pdb++ 除錯命令 Cheatsheet,它包含了 pdb++ 中常用的除錯命令以及增強功能,幫助你快速上手除錯。


pdb++ Cheatsheet

基本命令

命令 說明
h/help 顯示幫助資訊,輸入 help <command> 檢視某個命令的詳細幫助。
n/next 執行下一行程式碼,跳過函式呼叫。
s/step 進入下一行程式碼,如果是函式呼叫,則進入函式內部。
r/return 執行到當前函式的返回處。
c/continue 繼續執行程式,直到遇到下一個斷點。
q/quit 退出偵錯程式,終止程式。
l/list 顯示當前程式碼的上下文(預設 11 行),可指定範圍,如 list 10, 20
ll/longlist 顯示當前函式的完整程式碼。
p/print 列印表示式的值,例如 p my_var
pp 使用漂亮的格式(pretty print)列印表示式的值。
w/where 顯示當前的呼叫棧資訊。
bt where,顯示完整的回溯資訊。
up/u 移動到上一層呼叫棧。
down/d 移動到下一層呼叫棧。

斷點管理

命令 說明
b/break 設定斷點。用法:break <lineno>break <filename>:<lineno>
tbreak 設定臨時斷點,命中一次後自動刪除。
cl/clear 清除所有斷點。
disable 禁用斷點。
enable 啟用斷點。
ignore 忽略斷點,指定命中多少次後才啟用。
condition 為斷點設定條件。例如:condition 1 x > 10

變數和表示式

命令 說明
p/print 列印表示式的值,例如 p my_var
pp 使用漂亮的格式(pretty print)列印表示式的值。
whatis 顯示錶達式的型別,例如 whatis my_var
display 每次停止時自動顯示一個表示式的值,例如 display my_var
undisplay 取消顯示某個表示式的值。
a/args 列印當前函式的引數及其值。
retval 顯示最後一個返回值。
locals() 檢視當前作用域中的區域性變數。
globals() 檢視全域性變數。

程式碼執行

命令 說明
! 執行 Python 表示式。例如 !my_var = 5
interact 啟動一個互動式 Python shell,可以在當前上下文中執行任意程式碼。
run 重新執行指令碼。例如:run arg1 arg2

特殊功能

命令 說明
sticky 啟用/禁用 粘性模式,在除錯時持續顯示程式碼上下文。
source 顯示某個函式或類的原始碼,例如 source my_function
track 跟蹤異常,顯示其完整的追溯資訊。
display 在每次停止時自動顯示指定的變數值。
undisplay 停止自動顯示變數值。

粘性模式(Sticky Mode)

sticky 模式是 pdb++ 中的一個特殊功能,它會在除錯時持續顯示當前程式碼的上下文。每次程式執行到下一行,pdb++ 會自動重新整理並顯示當前位置的程式碼片段。

  • 啟用/禁用

    • 輸入 sticky 切換模式。
  • 使用場景

    • 在除錯過程中,sticky 模式可以幫助你持續觀察程式碼的變化,無需手動呼叫 list 命令。

命令別名

pdb++ 允許你為常用命令建立別名,簡化除錯過程。例如,你可以為 longlist 建立一個別名 ll

alias ll longlist

你也可以在 .pdbrc.py 檔案中配置這些別名,以便每次啟動 pdb++ 時自動載入。


自定義配置

你可以透過 ~/.pdbrc.py 檔案自定義 pdb++ 的行為,例如自動啟用語法高亮、粘性模式、設定別名等。

示例 .pdbrc.py 檔案:

# ~/.pdbrc.py

# 自動啟用粘性模式
def setup(self):
    self.sticky = True

# 設定別名
alias ll longlist
alias n next
alias s step
alias c continue

除錯快捷鍵

快捷鍵 說明
Ctrl + D 退出偵錯程式。
Ctrl + L 清屏。
Tab 自動補全命令、變數、函式名等。

常用除錯工作流

  1. 啟動除錯:在程式碼中插入 import pdb; pdb.set_trace(),或者執行指令碼時透過 python -m pdb your_script.py 進入除錯模式。
  2. 設定斷點:用 break <lineno>tbreak 設定斷點。
  3. 逐步除錯:使用 nextstep 逐行執行程式碼,檢查變數的值。
  4. 檢查變數:透過 ppp 列印變數的值,使用 whatis 檢視變數的型別。
  5. 跟蹤異常:使用 track 追蹤異常的完整回溯資訊。
  6. 使用粘性模式:透過 sticky 持續顯示程式碼的上下文,實時跟蹤程式碼執行。

結語

pdb++ 是一個強大且靈活的除錯工具,它在 pdb 的基礎上提供了許多增強功能,使除錯過程更加高效、直觀。透過這份 Cheatsheet,你可以快速掌握 pdb++ 的常用命令並提升除錯效率。

pdb(pdb++)方法

下面是你列出的 pdb++(和 pdb)中常用函式的詳細講解,包括各個函式的簽名、引數說明、用法以及它們的具體作用。在 pdb++ 中,這些函式提供了靈活的除錯能力,可以根據具體需求使用不同的除錯方法。


1. pdb.run(statement, globals=None, locals=None)

作用

  • 在偵錯程式中執行一條 Python 語句(類似於 exec() 函式),並在執行該語句時進入除錯模式。

簽名

def run(statement: str, globals: dict[str, Any] | None = None, locals: Mapping[str, Any] | None = None) -> None

引數

  • statement: 一個字串型別的 Python 語句,表示你想要執行的程式碼。
  • globals: 可選引數,表示全域性名稱空間的字典。如果不提供,預設為 None,使用當前的全域性名稱空間。
  • locals: 可選引數,表示區域性名稱空間的對映。如果不提供,預設為 None,使用當前的區域性名稱空間。

用法

import pdb

statement = 'x = 10; y = 20; z = x + y'
pdb.run(statement)

說明

  • pdb.run() 類似於 exec(),但在執行過程中啟動偵錯程式,方便你除錯一段程式碼的執行過程。

2. pdb.runeval(expression, globals=None, locals=None)

作用

  • 在偵錯程式中求值一個表示式(類似於 eval() 函式),並返回其結果。這個函式允許你在除錯模式下執行表示式並獲取結果。

簽名

def runeval(expression: str, globals: dict[str, Any] | None = None, locals: Mapping[str, Any] | None = None) -> Any

引數

  • expression: 一個字串型別的 Python 表示式,表示你想要計算的表示式。
  • globals: 可選引數,表示全域性名稱空間的字典。如果不提供,預設為 None,使用當前的全域性名稱空間。
  • locals: 可選引數,表示區域性名稱空間的對映。如果不提供,預設為 None,使用當前的區域性名稱空間。

用法

import pdb

expression = '10 + 20'
result = pdb.runeval(expression)
print(result)

說明

  • pdb.runeval() 類似於 eval(),但在偵錯程式執行環境中執行表示式,並返回計算結果。

3. pdb.runctx(statement, globals, locals)

作用

  • 在偵錯程式中執行一條 Python 語句,並顯式傳遞全域性和區域性名稱空間。這與 pdb.run() 類似,但強制要求提供 globalslocals 引數。

簽名

def runctx(statement: str, globals: dict[str, Any], locals: Mapping[str, Any]) -> None

引數

  • statement: 需要執行的 Python 語句。
  • globals: 全域性名稱空間的字典。
  • locals: 區域性名稱空間的對映。

用法

import pdb

globals = {'a': 10}
locals = {'b': 20}
statement = 'c = a + b'
pdb.runctx(statement, globals, locals)
print(globals, locals)

說明

  • pdb.runctx() 允許你使用自定義的名稱空間執行程式碼,這在除錯複雜的上下文時非常有用。

4. pdb.runcall(func, *args, **kwds)

作用

  • 在偵錯程式中呼叫一個函式,並傳遞引數。這允許你在除錯模式下執行函式並檢查其執行過程。

簽名

def runcall(func: Callable[_P, _T], *args: _P.args, **kwds: _P.kwargs) -> _T | None

引數

  • func: 要呼叫的函式。
  • *args: 傳遞給函式的引數。
  • **kwds: 傳遞給函式的關鍵字引數。

用法

import pdb

def divide(a, b):
    return a / b

result = pdb.runcall(divide, 10, 2)  # 除錯 divide(10, 2)
print(result)

說明

  • pdb.runcall() 允許你在偵錯程式中執行某些函式,並檢查函式執行的每一步。這在除錯有引數的函式時非常有用。

5. pdb.set_trace()

作用

  • 手動在程式碼中設定一個斷點,啟動偵錯程式並暫停程式執行。

簽名

def set_trace(*, header: str | None = None) -> None

引數

  • header: 可選引數,型別為 str | None,表示當偵錯程式啟動時輸出的自定義訊息。如果不提供,預設為 None

用法

import pdb

def my_function():
    x = 10
    pdb.set_trace(header="偵錯程式啟動")  # 在這裡進入除錯模式,顯示自定義訊息
    y = 20
    z = x + y
    print(z)

my_function()

說明

  • pdb.set_trace() 是手動除錯的常用工具,它允許你在程式碼執行到某個位置時暫停,並進入除錯模式。header 引數可以提供額外的資訊,以便在偵錯程式啟動時顯示。

6. pdb.post_mortem()

作用

  • 在異常發生後啟動偵錯程式,允許你檢視異常發生時的上下文。這在除錯未捕獲的錯誤時非常有用。

簽名

def post_mortem(t: TracebackType | None = None) -> None

引數

  • t: 可選引數,型別為 TracebackType | None,表示要除錯的回溯物件。如果不提供,使用最近的異常回溯。

用法

import pdb

def divide(a, b):
    return a / b

try:
    divide(1, 0)
except ZeroDivisionError:
    pdb.post_mortem()  # 在異常發生後進入除錯模式

說明

  • post_mortem() 允許你在異常發生後進入偵錯程式,並檢查導致異常的上下文。在除錯程式崩潰時非常有用。

7. pdb.pm()

作用

  • 立即進入除錯模式,除錯最近的未捕獲異常。它是 post_mortem() 函式的快捷方式。

簽名

def pm() -> None

引數

  • 無引數。

用法

import pdb

def divide(a, b):
    return a / b

try:
    divide(1, 0)
except:
    pdb.pm()  # 除錯最近的未捕獲異常

說明

  • pm()post_mortem() 的簡化版本,自動處理最近發生的異常並啟動偵錯程式。它適合快速進入除錯模式,而無需顯式捕獲異常。

總結

  • run(): 在除錯模式下執行一條 Python 語句。
  • runeval(): 在除錯模式下求值一個表示式並返回結果。
  • runctx(): 在除錯模式下執行一條 Python 語句,並使用自定義的全域性和區域性名稱空間。
  • runcall(): 透過偵錯程式呼叫一個函式,並傳遞引數。
  • set_trace(): 手動設定斷點,進入除錯模式。
  • post_mortem(): 在異常發生後進入除錯模式。
  • pm(): 除錯最近的未捕獲異常,是 post_mortem() 的快捷方式。

這些函式提供了靈活的除錯工具集,適合不同的除錯需求。

python debug相關環境變數

在 Python 中,偵錯程式(如 pdbpdb++)可以透過一些環境變數進行配置和控制。這些環境變數可以影響偵錯程式的行為、設定以及輸出。以下是與 Python 除錯相關的主要環境變數:


1. PYTHONBREAKPOINT

  • 作用:控制程式中斷點的行為,特別是用於設定或禁用偵錯程式。

  • 用法

    • 預設值PYTHONBREAKPOINT=pdb.set_trace
    • 禁用斷點:將變數設定為 0,所有的 breakpoint() 呼叫將被忽略。
    • 自定義行為:你可以將其設定為任意可呼叫的除錯函式,甚至是自定義函式。
  • 示例

    export PYTHONBREAKPOINT=0  # 禁用所有斷點
    export PYTHONBREAKPOINT=my_debugger.set_trace  # 使用自定義偵錯程式
    
  • 說明

    • 在 Python 3.7+ 中,breakpoint() 是用於手動進入除錯的內建函式。透過設定 PYTHONBREAKPOINT,你可以更改 breakpoint() 的行為。
    • 如果設定為 0,則除錯功能被禁用,即所有 breakpoint() 呼叫將被忽略。

2. PYTHONWARNINGS

  • 作用:控制 Python 警告的顯示方式,通常用於除錯時檢視潛在問題。

  • 用法

    • 預設值:Python 預設只顯示一次警告。
    • 可以設定成 error 來將警告變為異常,這樣在偵錯程式中可以捕獲並除錯警告。
  • 格式PYTHONWARNINGS=action:message:category:module:line(其中,action 是最常用的部分)

  • 常見值

    • default:預設行為,顯示一次警告。
    • always:總是顯示警告。
    • ignore:忽略警告。
    • error:將警告轉換為異常。
    • module:每個模組顯示一次警告。
    • once:僅顯示一次警告。
  • 示例

    export PYTHONWARNINGS="error"  # 將所有警告視為錯誤
    export PYTHONWARNINGS="always"  # 總是顯示所有警告
    
  • 說明

    • 當你設定 PYTHONWARNINGS="error" 時,所有 Python 警告將作為異常丟擲,這樣你可以在偵錯程式中捕獲它們並檢視具體的堆疊資訊。
    • 這在除錯潛在的程式碼問題或需要嚴格處理警告時非常有用。

3. PYTHONTRACEMALLOC

  • 作用:啟用或禁用記憶體分配跟蹤,用於除錯記憶體洩漏問題。

  • 用法

    • 預設值:未啟用。
    • 設定為正整數值來控制分配追蹤的深度。
  • 示例

    export PYTHONTRACEMALLOC=1  # 啟用記憶體分配跟蹤,深度為 1
    export PYTHONTRACEMALLOC=5  # 啟用記憶體分配跟蹤,深度為 5
    
  • 說明

    • 啟用記憶體跟蹤後,Python 將記錄記憶體分配的堆疊資訊。這對於除錯記憶體洩漏或定位高記憶體使用問題非常有幫助。
    • 你可以使用 tracemalloc 模組來獲取和顯示記憶體分配的相關資訊。

4. PYTHONDEBUG

  • 作用:啟用 Python 直譯器的除錯輸出,由直譯器在啟動時使用。

  • 用法

    • 設定為任意非空值時,啟用除錯模式(直譯器的除錯資訊將輸出到標準錯誤流)。
  • 示例

    export PYTHONDEBUG=1  # 啟用直譯器的除錯模式
    
  • 說明

    • 啟用 PYTHONDEBUG 後,Python 會輸出更多的除錯資訊,包括一些內部機制的輸出內容。這對於除錯 Python 直譯器本身或者檢視直譯器的詳細執行資訊非常有用。

5. PYTHONVERBOSE

  • 作用:啟用詳細輸出模式,用於顯示 Python 直譯器載入模組的詳細資訊,適合除錯模組匯入問題。

  • 用法

    • 設定為任意非空值時,啟用詳細模式。
  • 示例

    export PYTHONVERBOSE=1  # 啟用詳細輸出模式
    
  • 說明

    • 啟用 PYTHONVERBOSE 後,Python 會在程式執行過程中輸出每個模組匯入的詳細資訊。這對除錯模組匯入路徑、匯入衝突或未找到模組的問題非常有幫助。

6. PYTHONMALLOC

  • 作用:控制 Python 的記憶體分配器型別,用於除錯記憶體分配和記憶體問題。

  • 可選值

    • malloc:使用系統的 malloc()
    • pymalloc:使用 Python 的小物件分配器(預設)。
    • debug:使用 Python 的小物件分配器,並啟用記憶體除錯。
  • 示例

    export PYTHONMALLOC=debug  # 啟用記憶體除錯
    
  • 說明

    • 設定為 debug 時,Python 會啟用記憶體分配除錯機制,幫助你發現記憶體分配錯誤或記憶體洩漏問題。

7. PYTHONFAULTHANDLER

  • 作用:啟用 Python 的故障處理程式,使得 Python 在崩潰時列印出詳細的堆疊資訊。

  • 用法

    • 設定為任意非空值,啟用故障處理程式。
  • 示例

    export PYTHONFAULTHANDLER=1  # 啟用故障處理程式
    
  • 說明

    • 啟用 PYTHONFAULTHANDLER 後,當 Python 崩潰時,它會列印出 Python 錯誤堆疊資訊,幫助你更容易地定位問題。特別是在除錯 C 擴充套件模組或者直譯器崩潰時非常有用。

8. PYTHONHASHSEED

  • 作用:控制字串和其他雜湊種子的初始化值。

  • 用法

    • 設定為一個整數值,用於確定 Python 的雜湊種子。
    • 隨機雜湊種子預設用於防止雜湊衝突攻擊,但在某些情況下(如除錯或測試),你可能希望使用固定值。
  • 示例

    export PYTHONHASHSEED=42  # 使用固定的雜湊種子
    
  • 說明

    • 在除錯涉及雜湊的程式碼(如字典、集合)時,使用固定的雜湊種子可以幫助你復現和除錯某些雜湊相關的錯誤。

9. PYTHONINSPECT

  • 作用:在程式執行結束後自動進入互動模式(REPL),可用於除錯。

  • 用法

    • 設定為任意非空值時,程式執行結束後進入互動模式。
  • 示例

    export PYTHONINSPECT=1  # 程式結束後進入互動模式
    
  • 說明

    • 啟用 PYTHONINSPECT 後,當 Python 程式執行結束時,它會啟動互動式直譯器(REPL)。這在你想要在程式結束後進一步檢查狀態時非常有用。

總結

以下是常見的與 Python 除錯相關的環境變數:

  • PYTHONBREAKPOINT:控制 breakpoint() 的行為。
  • PYTHONWARNINGS:控制警告的處理方式。
  • PYTHONTRACEMALLOC:啟用記憶體分配跟蹤。
  • PYTHONDEBUG:啟用直譯器除錯模式。
  • PYTHONVERBOSE:啟用詳細輸出模式,除錯模組匯入問題。
  • PYTHONMALLOC:控制 Python 的記憶體分配器。
  • PYTHONFAULTHANDLER:啟用故障處理程式,捕獲崩潰時的堆疊資訊。
  • PYTHONHASHSEED:設定固定的雜湊種子,除錯雜湊相關問題。
  • PYTHONINSPECT:程式執行結束後進入互動模式。

這些環境變數為開發者提供了靈活的除錯選項,適用於不同的除錯場景和除錯需求。

注意breakpoint()set_trace()有一些細小差別。

相關文章