setuptools test hides import errors. How to have better info?

In python setuptools, python setup.py test runs the testsuite. However if I have an import error in my testsuite, the only error message I obtain is an AttributeError complaining that my test class is missing. Is there a way to obtain a more detailed error message, so I can fix the testsuite ?

I will explain myself better with the following example. Suppose I have a package called foo, created anew with paster. I then add the test

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

Now, suppose mytest.py contains the following code

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

This works. However, if I try to import an unexistent module

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

This is the error I obtain

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'

In other words, there's no reference to the failed import.


The problem is that the first argument of __import__() must be module. When you need to reach some object with dotted name you never know what part is module and what isn't. The one way to get module.subname object is to try import it as submodule first and when it fails use getattr(module, subname) , as unittest does. This will sometimes give you AttributeError instead of ImportError . Another way to do this is to try getattr(module, subname) first and only when it fails try import. This way isn't better: it will sometimes give you ImportError when more appropriate would be AttributeError .

Probably the best unittest can do in this case is to raise its own exception saying that both import and attribute lookup failed. Try to send bug report for this problem.


Use nose. No modification to your code should be necessary. Just do:

$ pip install nose
$ nosetests

If there is an ImportError, you will see it:

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

You may also want to try your code with Distribute (a fork of setuptools since setuptools isn't being very actively maintained). I don't know if it will do anything different, but it's worth a shot.

链接地址: http://www.djcxy.com/p/43764.html

上一篇: 用lxml.html替换元素

下一篇: setuptools测试隐藏导入错误。 如何获得更好的信息?