从程序运行python调试会话,而不是从控制台运行

ant*_*win 5 python debugging

我正在编写一个小的python IDE,我想添加简单的调试.我不需要winpdb的所有功能.如何启动一个python程序(按文件名),并在行号处设置断点,以便它运行到该行号并停止?请注意,我不想从命令行执行此操作,并且我不想编辑源(例如,通过插入set_trace).我不希望它停在第一行,所以我必须从那里运行调试器.我已经尝试了pdb和bdb的所有显而易见的方法,但我必须遗漏一些东西.

Dav*_*d Z 7

几乎唯一可行的方法(据我所知)是从IDE中运行Python作为子进程.这避免了当前Python解释器的"污染",这使得程序很可能以与独立启动它相同的方式运行.(如果您遇到此问题,请检查子进程环境.)以这种方式,您可以使用"调试模式"运行脚本

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

这将在调试器提示符下启动Python.您需要运行一些调试器命令来设置断点,您可以这样做:

o,e = p.communicate('break scriptname.py:lineno')
Run Code Online (Sandbox Code Playgroud)

如果这样做,o应该是Python解释器设置断点后的正常输出,并且e应该为空.我建议您使用它并在代码中添加一些检查以确保断点是否正确设置.

之后,您可以启动运行的程序

p.communicate('continue')
Run Code Online (Sandbox Code Playgroud)

此时,您可能希望将输入,输出和错误流挂接到您嵌入IDE中的控制台.您可能需要使用事件循环执行此操作,大致如下:

while p.returncode is None:
    o,e = p.communicate(console.read())
    console.write(o)
    console.write(e)
Run Code Online (Sandbox Code Playgroud)

您应该认为该代码段是有效的伪代码,因为根据您的控制台的工作原理,它可能需要一些修补才能使其正确.

如果这看起来过于混乱,你可以使用Python pdbbdb模块的功能(我分别猜测"Python调试器"和基本调试器)来简化过程.关于如何做到这一点的最佳参考是源代码的pdb.模块本身基本上,模块的责任被分割的方式是,bdb手柄调试器的功能,如设置断点,或停止和重新启动执行"罩下"; pdb是解决此的包装处理用户交互,即,读命令和显示输出.

对于集成IDE的调试器,以pdb我能想到的两种方式调整模块的行为是有意义的:

  1. 让它在初始化期间自动设置断点,而不必明确发送文本命令来执行此操作
  2. 使其从IDE控制台获取输入并将输出发送到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
Run Code Online (Sandbox Code Playgroud)

实际设置断点的逻辑位置就在调试器读取其.pdbrc文件之后,该文件发生在pdb.Pdb.setup方法中.要执行实际设置,请使用set_breakbdb.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()
Run Code Online (Sandbox Code Playgroud)

这段代码适用于作为例如命名元组传递的每个断点.您也可以尝试bdb.Breakpoint直接构造实例,但我不确定这是否可以正常工作,因为bdb.Bdb它保留了自己的断点信息.

接下来,您需要main为模块创建一个新方法,该方法以相同的方式pdb运行它.在某种程度上,您可以mainpdb(以及if __name__ == '__main__'当然声明)复制方法,但是您需要通过某种方式来增加它以传递有关其他断点的信息.我建议将断点写入IDE中的临时文件,并将该文件的名称作为第二个参数传递:

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

然后mypdb.main(),你会添加这样的东西:

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
Run Code Online (Sandbox Code Playgroud)

现在您可以像使用一样使用新的调试器模块pdb,除了您不必break在进程启动之前显式发送命令.这样做的好处是,如果允许您这样做,可以直接将Python子进程的标准输入和输出挂钩到控制台.