3

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?

Roman Odaisky
  • 2,811
  • 22
  • 26
  • Does this answer your question? [Recursive unittest discovery with python3 and without \_\_init\_\_.py files](https://stackoverflow.com/questions/46976256/recursive-unittest-discovery-with-python3-and-without-init-py-files) – Cameron Hudson Mar 25 '20 at 20:32

1 Answers1

0

You shouldn't omit __init__.py in regular package.

Namespace package is not regular package. Omitting __init__.py in regular package is abuse of the PEP 420. unittest and other tools doesn't support such abuse.

See https://dev.to/methane/don-t-omit-init-py-3hga too.

methane
  • 469
  • 3
  • 5
  • 11
  • 1
    Does this mean _all_ packages using PEP 420 are an abuse and _none_ of those can be tested using unittest? – Roman Odaisky Jan 21 '20 at 14:42
  • 1
    No, when package author really need *namespace* package, it is not abuse of PEP 420. But 99% of people doesn't need *namespace* package. – methane Jan 24 '20 at 00:05
  • It is worth noting this answer author is a CPython contributor. This otherwise reads as a subjective opinion. – jmlane Apr 03 '20 at 18:03