代码之家  ›  专栏  ›  技术社区  ›  Gilles 'SO- stop being evil'

在使用stdin的程序中使用line edition的Python调试器

  •  3
  • Gilles 'SO- stop being evil'  · 技术社区  · 5 年前

    要在Python脚本中添加一个特殊的调试器断点,我可以插入

    import pdb; pdb.set_trace()
    

    Pdb从标准输入读取数据,因此如果脚本本身也从标准输入读取数据,则此操作不起作用。作为解决方法,在类Unix系统上,我可以 tell pdb to read from the terminal :

    import pdb; pdb.Pdb(stdin=open('/dev/tty', 'r'), stdout=open('/dev/tty', 'w')).set_trace()
    

    pdb.set_trace ,我没有得到readline库(箭头键等)提供的命令行版本的好处。

    如何在不干扰脚本的stdin和stdout的情况下输入pdb,并且仍然获得命令行版本?

    玩具程序作为测试用例:

    #!/usr/bin/env python
    import sys
    for line in sys.stdin:
        #import pdb; pdb.set_trace()
        import pdb; pdb.Pdb(stdin=open('/dev/tty', 'r'), stdout=open('/dev/tty', 'w')).set_trace()
        sys.stdout.write(line)
    

    用法: { echo one; echo two; } | python cat.py

    1 回复  |  直到 5 年前
        1
  •  2
  •   Ondrej K.    5 年前

    我希望我没有遗漏任何重要的事情,但你似乎不能以一种完全微不足道的方式做到这一点,因为 readline 只有在 pdb.Pdb (分别。 cmd.Cmd use_rawinput 设置为非零,但会导致忽略 stdin 以及为调试器和脚本本身混合输入。也就是说,到目前为止,我能想到的最好的办法是:

    #!/usr/bin/env python3
    import os
    import sys
    import pdb
    
    pdb_inst = pdb.Pdb()
    
    stdin_called = os.fdopen(os.dup(0))
    console_new = open('/dev/tty')
    os.dup2(console_new.fileno(), 0)
    console_new.close()
    sys.stdin = os.fdopen(0)
    
    for line in stdin_called:
        pdb_inst.set_trace()
        sys.stdout.write(line)
    

    它对您的原始脚本是相对侵入性的,即使它至少可以放在它的外部并导入、调用或用作包装器。

    STDIN 到一个文件描述符并以 stdin_called /dev/tty 用于读取,替换了进程的文件描述符 0 (用于 标准物质 ;它应该使用返回的值 sys.stdin.fileno() )有了这个,我刚刚打开并重新分配了一个对应的类文件对象 sys.stdin . 这样程序循环 pdb pdb公司 与看起来只是一个“普通”控制台进行交互 标准物质 它很高兴能够 打开。

    阅读线 (行编辑、历史记录、完成)输入时 pdb公司

    $ { echo one; echo two; } | python3 cat.py
    > /tmp/so/cat.py(16)<module>()
    -> sys.stdout.write(line)
    (Pdb) c
    one
    > /tmp/so/cat.py(15)<module>()
    -> pdb_inst.set_trace()
    (Pdb) con[TAB][TAB]
    condition  cont       continue   
    (Pdb) cont
    two
    

    注意,从3.7版开始,您可以使用 breakpoint() 而不是 import pdb; pdb.Pdb().set_trace() dup2 调用以确保按预期创建/替换文件描述符。


    import sys
    
    # Add this: BEGIN
    import os
    import pdb
    import inspect
    
    pdb_inst = pdb.Pdb()
    
    class WrapSys:
        def __init__(self):
            self.__stdin = os.fdopen(os.dup(0))
            self.__console = open('/dev/tty')
            os.dup2(self.__console.fileno(), 0)
            self.__console.close()
            self.__console = os.fdopen(0)
            self.__sys = sys
    
        def __getattr__(self, name):
            if name == 'stdin':
                if any((f.filename.endswith("pdb.py") for f in inspect.stack())):
                    return self.__console
                else:
                    return self.__stdin
            else:
                return getattr(self.__sys, name)
    
    sys = WrapSys()
    # Add this: END
    
    for line in sys.stdin:
        pdb_inst.set_trace()  # Inject breakpoint
        sys.stdout.write(line)
    

    我一路都没挖过,但事实上, pdb公司 cmd 似乎不仅需要 也可以使用fd 为了 阅读线 sys 代表为了预设不同的含义 标准输入 当代码来自 pdb.py 在堆栈上。一个明显的警告。如果还有其他的话 也期望并依赖于 标准输入 fd将成为 0个 ,它仍然是不走运的(或者从另一个流读取它的输入,如果它只是为了它)。