2011-10-10 96 views
5

我正在編寫一個小的Python IDE,我想添加簡單的調試。我不需要winpdb的所有功能。 如何啓動一個python程序(通過文件名)並將一個斷點設置爲行號,以使其運行到該行號並暫停? 請注意,我不想從命令行執行此操作,並且我不想編輯源代碼(例如,通過插入set_trace)。我不希望它停在第一行,所以我必須從那裏運行調試器。我已經用pdb和bdb嘗試了所有顯而易見的方法,但我必須缺少一些東西。從程序運行python調試會話,而不是從控制檯運行

+0

所以你不想使用命令行,而且你不想編輯源代碼......你還想過其他什麼方式? – Amber

回答

7

幾乎是唯一可行的方法(據我所知)是從您的IDE中運行Python作爲子進程。這樣可以避免當前Python解釋器造成的「污染」,這使得程序運行的可能性與您獨立開始運行的可能性相當。 (如果你有這樣的問題,檢查進程環境中)。在這種方式下,你可以使用

p = subprocess.Popen(args=[sys.executable, '-m', 'pdb', 'scriptname.py', 'arg1'], 
        stdin=subprocess.PIPE, 
        stdout=subprocess.PIPE, 
        stderr=subprocess.PIPE) 

這將在調試器提示符啓動Python的運行在「調試模式」的腳本。你需要運行一些調試器命令來設置斷點,你可以做像這樣:

o,e = p.communicate('break scriptname.py:lineno') 

如果一切正常,o應該是Python解釋器的正常輸出它設置一個斷點後,和e應空着。我建議你玩這個,並在你的代碼中添加一些檢查,以確保斷點是否正確設置。

之後,你就可以啓動程序與

p.communicate('continue') 

運行在這一點上,你可能會想鉤輸入,輸出和錯誤流達,你在嵌入控制檯您IDE。你可能會需要一個事件循環,要做到這一點,大概像這樣:

while p.returncode is None: 
    o,e = p.communicate(console.read()) 
    console.write(o) 
    console.write(e) 

您應該考慮到段得到有效的僞代碼,因爲這取決於究竟如何控制檯的作品,它可能會採取一些修修補補來修正它。

如果這看起來過於凌亂,你也許可以簡化這個過程有點使用Python的pdbbdb模塊功能(我猜分別爲「Python調試」,並基本調試器」)。關於如何做的最好的參考資料這是pdb模塊本身的源代碼。基本上,模塊的責任分開的方式是bdb處理「引擎蓋下」調試器功能,如設置斷點或停止並重新啓動執行; pdb是對此的封裝它處理用戶交互,即讀取命令和顯示輸出。

對於你的IDE集成的調試器,它將有助於調節t時的pdb模塊的,我能想到的兩種行爲:

  1. 初始化過程中,系統將自動設置斷點,您無需顯式地發送文本命令這樣做
  2. 使其採取從輸入並將輸出發送到您的IDE控制檯

只需通過繼承pdb.Pdb即可輕鬆實現這兩項更改。您可以創建一個子類,其初始化需要斷點的列表,作爲一個額外的參數:

class MyPDB(pdb.Pdb): 
    def __init__(self, breakpoints, completekey='tab', 
       stdin=None, stdout=None, skip=None): 
     pdb.Pdb.__init__(self, completekey, stdin, stdout, skip) 
     self._breakpoints = breakpoints 

實際設置斷點邏輯的地方是調試器讀取其.pdbrc文件,它發生在pdb.Pdb.setup方法之後。來執行實際的設置中,使用set_break方法從bdb.Bdb繼承:

def setInitialBreakpoints(self): 
     _breakpoints = self._breakpoints 
     self._breakpoints = None # to avoid setting breaks twice 
     for bp in _breakpoints: 
      self.set_break(filename=bp.filename, line=bp.line, 
          temporary=bp.temporary, conditional=bp.conditional, 
          funcname=bp.funcname) 

    def setup(self, f, t): 
     pdb.Pdb.setup(self, f, t) 
     self.setInitialBreakpoints() 

這段代碼將工作每個斷點作爲例如被傳遞一個命名的元組。您也可以嘗試直接構建bdb.Breakpoint實例,但我不確定這是否可以正常工作,因爲bdb.Bdb保留有關斷點的自己的信息。

接下來,您需要爲您的模塊創建一個新的main方法,該方法以與運行pdb相同的方式運行。在某種程度上,您可以從pdb(和當然的if __name__ == '__main__'聲明)複製main方法,但您需要用某種方式來擴充它,以傳入有關其他斷點的信息。什麼我建議是寫斷點從你的IDE中的臨時文件,並作爲第二個參數傳遞一個文件的名稱:在mypdb.main()

tmpfilename = ... 
# write breakpoint info 
p = subprocess.Popen(args=[sys.executable, '-m', 'mypdb', tmpfilename, ...], ...) 
# delete the temporary file 

然後,你會增加這樣的事情:

def main(): 
    # code excerpted from pdb.main() 
    ... 
    del sys.argv[0] 

    # add this 
    bpfilename = sys.argv[0] 
    with open(bpfilename) as f: 
     # read breakpoint info 
     breakpoints = ... 
    del sys.argv[0] 
    # back to excerpt from pdb.main() 

    sys.path[0] = os.path.dirname(mainpyfile) 

    pdb = Pdb(breakpoints) # modified 

現在,您可以像使用pdb一樣使用新的調試器模塊,不同之處在於您無需在過程啓動之前明確發送break命令。這具有如下優點:如果允許您這樣做,則可以直接將Python子進程的標準輸入和輸出掛接到控制檯。

+0

感謝您的意見。但是,一旦到達p.communicate('continue'),我就會收到ValueError:關閉文件的I/O操作。 在Popen中使用close_fds = False並沒有幫助... –

+0

@antwin:請記住,所有這些本質上都是僞代碼。如果你只是運行它,當然你會得到各種各樣的錯誤。您需要根據自己的項目來調整它,但是我無法告訴您任何有關您的源代碼的信息。 –

+0

確實!因此,我將您的建議解壓到以下文件中:#!/ usr/bin/python import sys,subprocess fileName ='/tmp/test.py' lineno = 4 p = subprocess.Popen(args = [sys.executable ,'-m','pdb',fileName], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.STDOUT) cmd ='break%s:%d'%(fileName,lineno ); print cmd o,e = p.communicate(cmd) p.communicate('continue') 它應該工作,並且我以前使用過類似的方法,但它仍然給出ValueError:關閉文件的I/O操作第一次溝通之後... –

相關問題