Suppose the top level of a project is a PEP 420 namespace package without an __init__.py, like this:
src
└── toplevel
└── secondlevel
├── __init__.py
└── tests
├── __init__.py
└── test_something.py
The test file can be imported just fine:
$ python3.8 -m venv v
$ ln -s $PWD/src/toplevel v/lib/python3.8/site-packages/
$ v/bin/python -m toplevel.secondlevel.tests.test_something
Hi from test_something.py
However, if I point unittest at this structure, it fails:
$ v/bin/python -m unittest discover toplevel.secondlevel
Traceback (most recent call last):
File "/usr/lib/python3.8/runpy.py", line 192, in _run_module_as_main
return _run_code(code, main_globals, None,
File "/usr/lib/python3.8/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/usr/lib/python3.8/unittest/__main__.py", line 18, in <module>
main(module=None)
File "/usr/lib/python3.8/unittest/main.py", line 100, in __init__
self.parseArgs(argv)
File "/usr/lib/python3.8/unittest/main.py", line 124, in parseArgs
self._do_discovery(argv[2:])
File "/usr/lib/python3.8/unittest/main.py", line 244, in _do_discovery
self.createTests(from_discovery=True, Loader=Loader)
File "/usr/lib/python3.8/unittest/main.py", line 154, in createTests
self.test = loader.discover(self.start, self.pattern, self.top)
File "/usr/lib/python3.8/unittest/loader.py", line 340, in discover
self._get_directory_containing_module(top_part)
File "/usr/lib/python3.8/unittest/loader.py", line 354, in _get_directory_containing_module
full_path = os.path.abspath(module.__file__)
File "/usr/lib/python3.8/posixpath.py", line 374, in abspath
path = os.fspath(path)
TypeError: expected str, bytes or os.PathLike object, not NoneType
NoneType obviously comes from toplevel.__file__, which is None for PEP 420 namespace packages. unittest.loader.TestLoader.discover does seem to contain some machinery for namespace packages, but it doesn’t work in this case.
If I add the __init__.py file, everything works fine:
$ touch src/toplevel/__init__.py
$ v/bin/python -m unittest discover toplevel.secondlevel
Hi from test_something.py
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
How to make unittest work properly with __init__-less packages?