【问题标题】:How to debug python CLI that takes stdin?如何调试采用标准输入的 python CLI?
【发布时间】:2013-06-09 01:51:51
【问题描述】:

我正在尝试调试我编写的可以从标准输入获取参数的 Python CLI。一个简单的测试用例的输出是

echo "test" | python mytool.py

相当于

的输出
python mytool.py test

我想用这个工具调试一些问题,所以我尝试运行这个:

echo "test" | pdb mytool.py

但我得到这个输出,然后 pdb 退出:

> /path/to/mytool.py(5)<module>()
-> '''
(Pdb) *** NameError: name 'test' is not defined
(Pdb)

当我将-m python 添加到shebang 时,如果我在脚本中运行pdb.set_trace(),也会发生同样的事情。

这是怎么回事?

【问题讨论】:

  • 您能否更改您的脚本以接受来自标准输入以外的文件的输入?
  • @user1901786 如何访问 CLI 脚本中的标准输入?使用sys.stdin?另外,您只是想为此编写一个测试,还是需要在脚本中启动调试器?如果您需要调试器,您希望它在什么时候使用脚本?抱歉,这个问题我有点不清楚。
  • @Paul Sweatte。不是重复的。
  • 我要做的是获取 PyCharm 社区版并尝试使用它进行调试。如果您可以手动输入或粘贴输入,则使用标准本地调试应该可以工作。如果您需要调试来自管道或重定向的输入,您可能必须使用远程调试,它的设置稍微复杂一些。

标签: python stdin pdb command-line-interface command-line-tool


【解决方案1】:

另一种选择是创建您自己的 Pdb 对象,并在那里设置标准输入和标准输出。我的概念证明涉及 2 个终端,但可以肯定的是,某些工作可以合并到某种非常不安全的网络服务器。

  1. 创建两个fifo:

    mkfifo fifo_stdin
    mkfifo fifo_stdout
    
  2. 在一个终端中,在后台打开标准输出,然后写入标准输入:

    cat fifo_stdout &
    cat > fifo_stdin
    
  3. 在您的 python 代码/控制台中创建 pdb 对象并使用它:

    import pdb
    mypdb=pdb.Pdb(stdin=open('fifo_stdin','r'), stdout=open('fifo_stdout','w'))
    ...
    mypdb.set_trace()
    ...
    
  4. 利润!

您应该可以在第一个控制台上使用 pdb。

唯一的缺点是必须使用您的自定义 pdb,但在初始化时进行一些猴子补丁(PYTHONSTARTUP 或类似)会有所帮助:

import pdb
mypdb=pdb.Pdb(stdin=open('fifo_stdin','r'), stdout=open('fifo_stdout','w'))
pdb.set_trace=mypdb.set_trace

【讨论】:

  • 方便,例如,如果您想在使用 Pytest 时调试 Flask。从 Python 3.7 开始有断点():breakpoint(stdin=open('fifo_stdin', 'r'), stdout=open('fifo_stdout', 'w'))
【解决方案2】:

您可以使用其他文件描述符。使用 bash,您可以创建一个新的文件描述符:

exec 3<> test.txt

然后在你的python文件上有类似的东西:

#!/usr/bin/python

# Use fd 3 as another stdin file.
import os
stdin=os.fdopen(3)

while True:
    s=stdin.readline()
    import pdb; pdb.set_trace()
    print len(s)

只要运行你的脚本就会使用 test.txt 作为输入,你可以在标准输入上使用标准输入。如果需要,它也可以与管道一起使用。

【讨论】:

  • 我更喜欢不需要修改工具代码的解决方案。
【解决方案3】:

您的控制 TTY 仍然是终端,对吗?用这个代替pdb.set_trace

def tty_pdb():
    from contextlib import (_RedirectStream,
                            redirect_stdout, redirect_stderr)
    class redirect_stdin(_RedirectStream):
        _stream = 'stdin'
    with open('/dev/tty', 'r') as new_stdin, \
         open('/dev/tty', 'w') as new_stdout, \
         open('/dev/tty', 'w') as new_stderr, \
         redirect_stdin(new_stdin), \
         redirect_stdout(new_stdout), redirect_stderr(new_stderr):
        __import__('pdb').set_trace()

在这些情况下,还没有让 readline 自动完成。向上箭头也不起作用,或者任何其他 readline 细节都不起作用。

【讨论】:

    【解决方案4】:

    当您使用 pdb(或任何其他 python 调试器)时,它会为调试命令获取 stdin,这就是您得到 NameError: name 'test' is not defined 的原因。

    例如,此命令将在运行时请求时退出调试器,并且您不会在一次运行中收到此错误(也不会进行交互式调试):

    (echo cont;echo "test") | python -m pdb mytool.py

    【讨论】:

    • 虽然这解释了发生了什么,但它并没有提供答案。
    • @whereswalden 实际上,“答案”是 pdb 因从标准输入获取输入而损坏且愚蠢。 ://
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-20
    • 1970-01-01
    相关资源
    最近更新 更多