python - 运行 `python -m unittest` 会更改在回溯中打印覆盖 `__name__` 的异常的方式

标签 python exception factory python-unittest

我有一段动态创建异常的代码。以这种方式创建的每个异常类都会覆盖其 __name__:

def exception_injector(name, parent, module_dict):
    class product_exception(parent):
        pass
    product_exception.__name__ = name
    product_exception.__module__ = module_dict["__name__"]
    module_dict[name] = product_exception

当我经常使用它时,它可以很好地打印所有内容:

>>> namespace = {"__name__": "some.module"}
>>> exception_injector("TestError", Exception, namespace)
>>> raise namespace["TestError"]("What's going on?")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
some.module.TestError: What's going on?

但是当我使用 unittest 模块时,会打印原始的 __name__:

>>> import unittest
>>> class DemoTestCase(unittest.TestCase):
...     def test_raise(self):
...         namespace = {"__name__": "some.module"}
...         exception_injector("TestError", Exception, namespace)
...         namespace["TestError"]("What's going on?")
...
>>> unittest.main(defaultTest="DemoTestCase", argv=["demo"], exit=False)
E
======================================================================
ERROR: test_raise (__main__.DemoTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "<stdin>", line 3, in test_raise
some.module.exception_injector.<locals>.product_exception: What's going on?

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)
<unittest.main.TestProgram object at 0x1017c3eb8>

unittest 模块可以从异常对象的何处获取原始信息?

最佳答案

单元测试使用 traceback module格式化异常。您可以通过执行相同的操作来重现输出:

>>> import traceback
>>> try:
...     raise namespace["TestError"]("What's going on?")
... except Exception as e:
...     tb = traceback.TracebackException.from_exception(e)
...     print(*tb.format())
...
Traceback (most recent call last):
   File "<stdin>", line 2, in <module>
 some.module.exception_injector.<locals>.product_exception: What's going on?

正在打印的是对象__module__属性值*,其中object.__qualname__ attribute附加(带有点分隔符),而不是 __module____name__:

>>> namespace["TestError"].__qualname__
'exception_injector.<locals>.product_exception'

限定名称包括 full scope where the class was created (此处为函数名称,但这也可以包括类名称)。

如果您的目标是向模块的全局命名空间添加异常,则只需将其设置为与 name 相同的值即可:

>>> namespace["TestError"].__qualname__ = "TestError"
>>> try:
...     raise namespace["TestError"]("What's going on?")
... except Exception as e:
...     tb = traceback.TracebackException.from_exception(e)
...     print(*tb.format())
...
Traceback (most recent call last):
   File "<stdin>", line 2, in <module>
 some.module.TestError: What's going on?

或者在您的代码上下文中:

def exception_injector(name, parent, module_dict):
    class product_exception(parent, details=args):
        pass

    product_exception.__name__ = name
    product_exception.__qualname__ = name
    product_exception.__module__ = module_dict["__name__"]
    module_dict[name] = product_exception

* 如果模块名称是 __main__builtins,则回溯模块将忽略异常模块。

关于python - 运行 `python -m unittest` 会更改在回溯中打印覆盖 `__name__` 的异常的方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63871136/

相关文章:

python - super(Foo, self) 和 super(Foo, self.__class__) 的区别?

java - 遍历 Arraylist 时出现 ConcurrentModificationException(不删除)

c++ - 我可以抽象出有关模板类的具体细节吗?

python - 首选 Python XML 处理器

python - pygraphviz,导入错误 : undefined symbol: Agundirected

python - 我不明白 Python (2.7.3) 中的编码和解码

java.lang.NoClassDefFoundError

c++ - 防止 gcc/libstdc++ 在从 throw() 方法抛出时调用终止

c# - 在另一个任务中启动任务会重复我的 WebRequest

python - 使用元类实现工厂设计模式