setuptools 测试隐藏导入错误。如何获取更好的信息?

7 投票
4 回答
1891 浏览
提问于 2025-04-15 16:31

在Python的setuptools中,运行python setup.py test可以执行测试套件。不过,如果我的测试套件里有导入错误,得到的错误信息只是一个AttributeError,抱怨我的测试类缺失。有没有办法获取更详细的错误信息,这样我就能修复测试套件呢?

我用下面的例子来解释得更清楚。假设我有一个叫foo的包,是用paster新创建的。然后我添加了测试

./foo
./foo/__init__.py
./foo/tests
./foo/tests/__init__.py
./foo/tests/mytest.py
./setup.cfg
./setup.py

现在,假设mytest.py里包含以下代码

import unittest
class MyTestClass(unittest.TestCase):
    def testFoo(self):
        self.assertEqual(1,1)

这样是可以正常工作的。但是,如果我尝试导入一个不存在的模块

import unittest
import frombiz
class MyTestClass(unittest.TestCase):
    def testFoo(self):
        self.assertEqual(1,1)

我得到的错误是这样的

Traceback (most recent call last):
  File "setup.py", line 26, in <module>
    test_suite = "foo.tests"
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/distutils/core.py", line 152, in setup
    dist.run_commands()
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/distutils/dist.py", line 975, in run_commands
    self.run_command(cmd)
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/distutils/dist.py", line 995, in run_command
    cmd_obj.run()
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/Extras/lib/python/setuptools/command/test.py", line 121, in run
    self.with_project_on_sys_path(self.run_tests)
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/Extras/lib/python/setuptools/command/test.py", line 101, in with_project_on_sys_path
    func()
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/Extras/lib/python/setuptools/command/test.py", line 130, in run_tests
    testLoader = loader_class()
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/unittest.py", line 816, in __init__
    self.parseArgs(argv)
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/unittest.py", line 843, in parseArgs
    self.createTests()
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/unittest.py", line 849, in createTests
    self.module)
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/unittest.py", line 613, in loadTestsFromNames
    suites = [self.loadTestsFromName(name, module) for name in names]
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/unittest.py", line 587, in loadTestsFromName
    return self.loadTestsFromModule(obj)
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/Extras/lib/python/setuptools/command/test.py", line 34, in loadTestsFromModule
    tests.append(self.loadTestsFromName(submodule))
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/unittest.py", line 584, in loadTestsFromName
    parent, obj = obj, getattr(obj, part)
AttributeError: 'module' object has no attribute 'mytest'

换句话说,错误信息里没有提到失败的导入。

4 个回答

0

你也可以试试用 Distribute 来运行你的代码。这个工具是从setuptools分出来的,因为setuptools现在维护得不是很积极。我不确定它会有什么不同的效果,但试试看也是值得的。

3

使用nose这个工具。你的代码不需要做任何修改。只需执行以下命令:

$ pip install nose
$ nosetests

如果出现导入错误,你会看到相关信息:

ERROR: Failure: ImportError (cannot import name MyModel)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/garyvdm/bwreport/ve/lib/python3.2/site-packages/nose/failure.py", line 37, in runTest
    raise self.exc_class(self.exc_val).with_traceback(self.tb)
  File "/home/garyvdm/bwreport/ve/lib/python3.2/site-packages/nose/loader.py", line 390, in loadTestsFromName
    addr.filename, addr.module)
  File "/home/garyvdm/bwreport/ve/lib/python3.2/site-packages/nose/importer.py", line 39, in importFromPath
    return self.importFromDir(dir_path, fqname)
  File "/home/garyvdm/bwreport/ve/lib/python3.2/site-packages/nose/importer.py", line 86, in importFromDir
    mod = load_module(part_fqname, fh, filename, desc)
  File "/home/garyvdm/bwreport/bwreport/daemon/__init__.py", line 11, in <module>
    from bwreport.models import (
ImportError: cannot import name MyModel
1

这个问题是因为 __import__() 的第一个参数必须是一个模块。当你需要通过带点的名字来访问某个对象时,你可能不知道哪个部分是模块,哪个部分不是。获取 module.subname 对象的一种方法是先尝试将其作为子模块导入,如果失败了再用 getattr(module, subname) 来获取,就像 unittest 那样。这种方法有时会给你 AttributeError 错误,而不是 ImportError 错误。另一种方法是先尝试 getattr(module, subname),只有在失败时才去导入。这种方法并不更好:它有时会给你 ImportError 错误,而更合适的应该是 AttributeError

在这种情况下,unittest 可能做的最好的事情就是抛出一个自己的异常,说明导入和属性查找都失败了。建议你为这个问题提交一个bug报告。

撰写回答