2011-10-10 95 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操作第一次沟通之后... –

相关问题