我只是一个包的次要贡献者,人们必须这样做(Foo.Bar.Bar
是一类):
>>> from Foo.Bar import Bar
>>> s = Bar('a')
有时候人们会错误地这样做(Foo.Bar
是一个模块):
>>> from Foo import Bar
>>> s = Bar('a')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'module' object is not callable
这似乎很简单,但是用户仍然无法对其进行调试,我想使其更容易。我无法更改Foo
或Bar
的名称,但是我想添加更多信息,例如:
TypeError("'module' object is not callable, perhaps you meant to call 'Bar.Bar()'")
我阅读了Callable modules的问答,而且我知道我无法向模块添加__call__
方法(并且我不想为此将整个模块包装在一个类中) )。无论如何,我不希望模块可调用,我只希望自定义回溯。是否有适用于Python 3.x和2.7+的干净解决方案?
答案 0 :(得分:4)
将其添加到Bar.py
的顶部:(基于this question)
import sys
this_module = sys.modules[__name__]
class MyModule(sys.modules[__name__].__class__):
def __call__(self, *a, **k): # module callable
raise TypeError("'module' object is not callable, perhaps you meant to call 'Bar.Bar()'")
def __getattribute__(self, name):
return this_module.__getattribute__(name)
sys.modules[__name__] = MyModule(__name__)
# the rest of file
class Bar:
pass
注意:已在python3.6和python2.7上进行过测试。
答案 1 :(得分:3)
您想要的是在显示给用户时更改错误消息。一种方法是定义自己的excepthook。
您自己的功能可以:
TypeError
异常以及执行该异常的函数的信息)中搜索调用帧,Bar
对象,在Foo.__init__.py
中,您可以安装除钩子
import inspect
import sys
def _install_foo_excepthook():
_sys_excepthook = sys.excepthook
def _foo_excepthook(exc_type, exc_value, exc_traceback):
if exc_type is TypeError:
# -- find the last frame (source of the exception)
tb_frame = exc_traceback
while tb_frame.tb_next is not None:
tb_frame = tb_frame.tb_next
# -- search 'Bar' in the local variable
f_locals = tb_frame.tb_frame.f_locals
if 'Bar' in f_locals:
obj = f_locals['Bar']
if inspect.ismodule(obj):
# -- change the error message
exc_value.args = ("'module' object is not callable, perhaps you meant to call 'Foo.Bar.Bar()'",)
_sys_excepthook(exc_type, exc_value, exc_traceback)
sys.excepthook = _foo_excepthook
_install_foo_excepthook()
当然,您需要执行此算法…
具有以下示例:
# coding: utf-8
from Foo import Bar
s = Bar('a')
您得到:
Traceback (most recent call last):
File "/path/to/demo_bad.py", line 5, in <module>
s = Bar('a')
TypeError: 'module' object is not callable, perhaps you meant to call 'Foo.Bar.Bar()'
答案 2 :(得分:1)
有很多方法可以获取不同的错误消息,但是它们都有奇怪的警告和副作用。
用__class__
子类替换模块的types.ModuleType
可能是最干净的选择,但仅适用于Python 3.5 +。
除了3.5+的限制,我想到的这个选项的主要怪异副作用是,该模块将被报告为callable
函数可调用的,并且重新加载该模块将再次替换其类,除非您要小心避免这种双重替换。
将模块对象替换为其他对象可在3.5之前的Python版本上使用,但要完全正确将非常棘手。
子模块,重新加载,全局变量,除自定义错误消息外的任何模块功能...如果您错过实现的某些细微方面,所有这些功能都可能会破坏。同样,该模块将被报告为callable
可调用,就像用__class__
替换一样。
可以在引发异常后(例如在sys.excepthook
中尝试修改异常消息,但是没有一种很好的方法来告知任何特定的TypeError
来自尝试将模块作为函数调用。
您可能最好的办法是,在一个似乎可以调用您的模块的命名空间中,检查带有TypeError
消息的'module' object is not callable
,例如,如果{{ 1}}名称绑定到框架的本地变量或全局变量中的Bar
模块-但这仍然会有很多假阴性和假阳性。另外,Foo.Bar
替换与IPython不兼容,并且您使用的任何机制都可能与某些东西冲突。
现在,您所遇到的问题很容易理解和解释。尝试更改错误消息时可能遇到的问题可能很难理解,也很难解释。这可能不是一个值得的权衡。