python2.7调试时行为与非调试不同

1 投票
1 回答
973 浏览
提问于 2025-04-18 17:46

我在程序里遇到了一个bug,想用调试工具来看看。在我的开发环境(WingIDE)里有调试功能,但我不能通过命令行来调用程序。所以我使用了Python的pdb模块。我的应用程序是单线程的。

我查了一下 代码在发布模式和调试模式下表现不同,但我觉得这和我的情况不太一样。

我把问题缩小到以下这段代码。

我做了什么:

我创建了一个简短的方法,它只会在不使用IDE时被调用。

def set_pdb_trace():
    run_in_ide = not sys.stdin.isatty()
    if not run_in_ide:
        import pdb; pdb.set_trace() # use only in python interpreter    

这个方法运行得很好,我在很多情况下都用过。

我想调试下面这个方法:

import sys
import os    
import subprocess32           

def call_backported():
    command = 'lsb_release -r'
    timeout1 = 0.001 # make value too short, so time-out will enforced  
    try:      
        p = subprocess32.Popen(command, shell=True,
                                  stdout=subprocess32.PIPE,
                                  stderr=subprocess32.STDOUT)  
        set_pdb_trace()
        tuple1 = p.communicate(input=b'exit %errorlevel%\r\n', timeout=timeout1)     
        print('No time out')
        value = tuple1[0].decode('utf-8').strip()  
        print('Value : '+ value)
    except subprocess32.TimeoutExpired, e:                  
        print('TimeoutExpired')                            

解释一下。 我想调用一个子进程,并设置超时时间。对于Python 3.3及以上版本,这个功能是内置的,但我的应用程序也需要能在Python 2.7上运行。所以我使用了 https://pypi.python.org/pypi/subprocess32/3.2.6 作为替代方案。 为了读取返回值,我参考了 如何从子进程获取有用的结果?
在没有设置超时的情况下,设置超时为1秒时,这个方法按预期工作,结果值和'没有超时'都会被打印出来。

我想强制设置超时,所以我把超时时间设置得非常短,0.001秒。这样现在只应该打印'超时已过期'。

我想在命令行中执行这个。

当我先注释掉#set_pdb_trace()这一行时,'超时已过期'会被打印出来,这就是我预期的行为。

现在我取消注释set_pdb_trace()并在命令行中执行。

调试器显示出来后,我按下'c'(继续),然后'没有超时'和结果被打印出来。这个结果和没有调试时不同。生成的输出是:

bernard@bernard-vbox2:~/clones/it-should-work/unit_test$ python test_subprocess32.py 
--Return--
> /home/bernard/clones/it-should-work/unit_test/test_subprocess32.py(22)set_pdb_trace()->None
-> import pdb; pdb.set_trace() # use only in python interpreter
(Pdb) c
No time out
Value : Release:    13.10
bernard@bernard-vbox2:~/clones/it-should-work/unit_test$ 

这是怎么回事?我该怎么解决?

1 个回答

1

你在打开子进程和向它写入数据之间加了一个延迟。

当你创建 Popen() 对象时,子进程会立刻启动。但是,当你接着调用 p.communicate() 并尝试向它写入数据时,进程还没准备好接收输入。这种延迟,加上读取进程输出所需的时间,超过了你设定的 0.0.1 秒的超时时间。

当你插入断点时,进程有机会启动;而 lsb_release 命令不需要等待输入,立刻就会产生输出。等到调用 p.communicate() 时,就不需要再等管道了,输出会立刻产生。

如果你把断点放在 Popen() 调用之前,然后按 c,你会再次看到超时触发。

撰写回答