为什么我看不到来自 readline.set_completion_display_matches_hook 的错误?
Why don't I see errors from readline.set_completion_display_matches_hook?
考虑这段代码:
#!/usr/bin/env python3
from cmd import Cmd
import readline
class mycmd(Cmd):
def match_display_hook(self, substitution, matches, longest_match_length):
someNonexistentMethod()
print()
for match in matches:
print(match)
print(self.prompt, readline.get_line_buffer(), sep='', end='', flush=True)
def do_crash(self, s):
someNonexistentMethod()
def do_quit(self, s):
return True
if __name__ == '__main__':
obj = mycmd()
readline.set_completion_display_matches_hook(obj.match_display_hook)
obj.cmdloop()
我希望看到 NameError: name 'someNonexistentMethod' is not defined
当我 运行 并点击 TabTab 时。然而,实际上似乎什么都没有发生(错误确实发生了,所以其他打印完成的函数没有 运行;我只是没有看到错误)。当我 运行 crash
时,我确实看到了预期的错误,所以我知道错误处理在整个程序中工作正常,但只是在 set_completion_display_matches_hook
回调内部被破坏了。为什么会这样,我能做些什么吗?
TL;DR
看起来好像 readline
C-Binding 只是在调用钩子时忽略异常,当 TabTab 被按下时.
我认为问题的根源可能是 C 绑定中的这些行 (1033-1049) readline.c
r = PyObject_CallFunction(readlinestate_global->completion_display_matches_hook,
"NNi", sub, m, max_length);
m=NULL;
if (r == NULL ||
(r != Py_None && PyLong_AsLong(r) == -1 && PyErr_Occurred())) {
goto error;
}
Py_CLEAR(r);
if (0) {
error:
PyErr_Clear();
Py_XDECREF(m);
Py_XDECREF(r);
}
其中,如果出现错误,直接清除即可。参考 PyErr_Clear()
我用于调试的步骤:
检查是否引发异常
我把函数改成了:
def match_display_hook(self, substitution, matches, longest_match_length):
try:
someNonexistentMethod()
except Exception as e:
print(e)
然后按预期打印 name 'someNonexistentMethod' is not defined
(以及所有其他预期的输出)。在这里引发任何其他异常并没有退出命令提示符。
检查打印到 stderr
是否有效
最后,我检查是否可以通过添加以下内容打印到 sys.stderr
:
def match_display_hook(self, substitution, matches, longest_match_length):
print("foobar", file=sys.stderr, flush=True)
按预期打印 foobar
。
为什么?
我猜这是设计使然。根据rlcompleter docs:
Any exception raised during the evaluation of the expression is caught, silenced and None is returned.
请参阅 rlcompleter source code 了解基本原理:
- Exceptions raised by the completer function are ignored (and generally cause the completion to fail). This is a feature -- since readline sets the tty device in raw (or cbreak) mode, printing a traceback wouldn't work well without some complicated hoopla to save, reset and restore the tty state.
解决方法
作为解决方法,为了调试,将钩子包装在捕获所有异常的函数中(或编写函数装饰器),并使用 logging module 将堆栈跟踪记录到文件中:
import logging
logging.basicConfig(filename="example.log", format='%(asctime)s %(message)s')
def broken_function():
raise NameError("Hi, my name is Name Error")
def logging_wrapper(*args, **kwargs):
result = None
try:
result = broken_function(*args, **kwargs)
except Exception as ex:
logging.exception(ex)
return result
logging_wrapper()
此脚本成功运行,example.log 包含日志消息和堆栈跟踪:
2020-11-17 13:55:51,714 Hi, my name is Name Error
Traceback (most recent call last):
File "/Users/traal/python/./stacktrace.py", line 12, in logging_wrapper
result = function_to_run()
File "/Users/traal/python/./stacktrace.py", line 7, in broken_function
raise NameError("Hi, my name is Name Error")
NameError: Hi, my name is Name Error
考虑这段代码:
#!/usr/bin/env python3
from cmd import Cmd
import readline
class mycmd(Cmd):
def match_display_hook(self, substitution, matches, longest_match_length):
someNonexistentMethod()
print()
for match in matches:
print(match)
print(self.prompt, readline.get_line_buffer(), sep='', end='', flush=True)
def do_crash(self, s):
someNonexistentMethod()
def do_quit(self, s):
return True
if __name__ == '__main__':
obj = mycmd()
readline.set_completion_display_matches_hook(obj.match_display_hook)
obj.cmdloop()
我希望看到 NameError: name 'someNonexistentMethod' is not defined
当我 运行 并点击 TabTab 时。然而,实际上似乎什么都没有发生(错误确实发生了,所以其他打印完成的函数没有 运行;我只是没有看到错误)。当我 运行 crash
时,我确实看到了预期的错误,所以我知道错误处理在整个程序中工作正常,但只是在 set_completion_display_matches_hook
回调内部被破坏了。为什么会这样,我能做些什么吗?
TL;DR
看起来好像 readline
C-Binding 只是在调用钩子时忽略异常,当 TabTab 被按下时.
我认为问题的根源可能是 C 绑定中的这些行 (1033-1049) readline.c
r = PyObject_CallFunction(readlinestate_global->completion_display_matches_hook,
"NNi", sub, m, max_length);
m=NULL;
if (r == NULL ||
(r != Py_None && PyLong_AsLong(r) == -1 && PyErr_Occurred())) {
goto error;
}
Py_CLEAR(r);
if (0) {
error:
PyErr_Clear();
Py_XDECREF(m);
Py_XDECREF(r);
}
其中,如果出现错误,直接清除即可。参考 PyErr_Clear()
我用于调试的步骤:
检查是否引发异常
我把函数改成了:
def match_display_hook(self, substitution, matches, longest_match_length):
try:
someNonexistentMethod()
except Exception as e:
print(e)
然后按预期打印 name 'someNonexistentMethod' is not defined
(以及所有其他预期的输出)。在这里引发任何其他异常并没有退出命令提示符。
检查打印到 stderr
是否有效
最后,我检查是否可以通过添加以下内容打印到 sys.stderr
:
def match_display_hook(self, substitution, matches, longest_match_length):
print("foobar", file=sys.stderr, flush=True)
按预期打印 foobar
。
为什么?
我猜这是设计使然。根据rlcompleter docs:
Any exception raised during the evaluation of the expression is caught, silenced and None is returned.
请参阅 rlcompleter source code 了解基本原理:
- Exceptions raised by the completer function are ignored (and generally cause the completion to fail). This is a feature -- since readline sets the tty device in raw (or cbreak) mode, printing a traceback wouldn't work well without some complicated hoopla to save, reset and restore the tty state.
解决方法
作为解决方法,为了调试,将钩子包装在捕获所有异常的函数中(或编写函数装饰器),并使用 logging module 将堆栈跟踪记录到文件中:
import logging
logging.basicConfig(filename="example.log", format='%(asctime)s %(message)s')
def broken_function():
raise NameError("Hi, my name is Name Error")
def logging_wrapper(*args, **kwargs):
result = None
try:
result = broken_function(*args, **kwargs)
except Exception as ex:
logging.exception(ex)
return result
logging_wrapper()
此脚本成功运行,example.log 包含日志消息和堆栈跟踪:
2020-11-17 13:55:51,714 Hi, my name is Name Error
Traceback (most recent call last):
File "/Users/traal/python/./stacktrace.py", line 12, in logging_wrapper
result = function_to_run()
File "/Users/traal/python/./stacktrace.py", line 7, in broken_function
raise NameError("Hi, my name is Name Error")
NameError: Hi, my name is Name Error