要在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,并且仍然获得命令行版本?
理想情况下,相同的代码在Python 2和Python 3中都应该起作用。与非Unix系统的兼容性将是一个加分。
玩具程序作为测试用例:
#!/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
答案 0 :(得分:2)
我希望我没有错过任何重要的事情,但是看来您真的不能以一种完全琐碎的方式做到这一点,因为readline
仅在pdb.Pdb
({{1} } it sublcasses)已将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
;它应该使用STDIN
返回的值)我刚刚打开了这个对象,并将一个类似文件的对象重新分配给sys.stdin.fileno()
。这样,程序循环并且sys.stdin
使用它们自己的输入流,而pdb
与似乎只是一个“普通”控制台pdb
进行交互,它很高兴启用{{1 }}。
这不是很漂亮,但是应该照做您想做的事,希望它会提供有用的提示。在STDIN
中时,它使用readline
(如果有){行编辑,历史记录,完成情况):
readline
请注意,从3.7版开始,为方便起见,您可以使用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
,还可以检查breakpoint()
调用的结果以确保文件描述符已按预期创建/替换
编辑:如前所述,OP在评论中指出,此脚本既丑陋又具有侵入性。它并没有使其更漂亮,但是我们可以采取一些技巧来减少对其周围环境的影响。我一起砍过一个这样的选项:
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
,而且还需要使用fd cmd
为了使sys.stdin
生效。上面的示例占用了一个缺口,并且在脚本中劫持了0
的含义,以便在{{1 }}在堆栈上。一个明显的警告。如果readline
之外的其他任何事物也期望并且取决于sys
fd为sys.stdin
,那么它仍然会很不幸(或者如果只是为了它)。