Why does import work with Python nose when the test directory contains __init__.py?

Consider the following project structure:

a.py test/ test_a.py 

with test_a.py importing module a :

 import a 

As expected, running nosetests in the test directory results in an import error:

 ERROR: Failure: ImportError (No module named a) 

However, I noticed that adding an empty __init__.py file to the test directory does import work with nosetests (but not when running test_a.py with Python). Could you explain why?

I understand that adding __init__.py makes the test package. But does this mean that the import includes a directory containing the package in the search?

+5
source share
2 answers

Having the __init__.py file in the directory converts test from the plain old directory to the python package . This affects sys.path .

Modify the test_a.py module as follows:

 import sys def test_thing(): for i, p in enumerate(sys.path): print i, p try: import a except ImportError: print('caught import error') 

Then try running nosetests -s from the test directory with and without __init__.py there.

Note: this is a test runner that scares sys.path . And this is described in the second β€œNote” of this section here (thanks @davidism). You will not see any changes there just by running python test_a.py with and without package structure.

+5
source

I looked into the sub nose code, and here's why.

 def importFromPath(self, path, fqname): """Import a dotted-name package whose tail is at path. In other words, given foo.bar and path/to/foo/bar.py, import foo from path/to/foo then bar from path/to/foo/bar, returning bar. """ # find the base dir of the package path_parts = os.path.normpath(os.path.abspath(path)).split(os.sep) name_parts = fqname.split('.') if path_parts[-1] == '__init__.py': path_parts.pop() path_parts = path_parts[:-(len(name_parts))] dir_path = os.sep.join(path_parts) # then import fqname starting from that dir return self.importFromDir(dir_path, fqname) def importFromDir(self, dir, fqname): """Import a module *only* from path, ignoring sys.path and reloading if the version in sys.modules is not the one we want. """ dir = os.path.normpath(os.path.abspath(dir)) 

In your case, when importFromDir is called from importFromPath, 'dir' is the directory at the level above from the __init__.py directory. So why adding __init__.py to your test does the work of "import a"

+2
source

All Articles