Python relative import not working even though root package name is being used

84 Views Asked by At

I've read quite a bit about relative import's tricky aspects (especially this question). However, the following example is still not working.

I have the following project (available on GitHub):

DirectoryTree

Here's test_some_unit.py:

import unittest

from ..util.foo import run_foo


class TestSomeUnit(unittest.TestCase):
    def test_some_unit(self):
        run_foo()
        print("Unit package")

The integration side is analogous.

Running discovery from current working directory UnittestDiscoverywhich is the parent of package, for the root package package produces a relative import error:

UnittestDiscovery> python -m unittest discover package
EE
======================================================================
ERROR: integration.test_some_integration (unittest.loader._FailedTest.integration.test_some_integration)
----------------------------------------------------------------------
ImportError: Failed to import test module: integration.test_some_integration
Traceback (most recent call last):
  File "C:\ProgramData\anaconda3\Lib\unittest\loader.py", line 407, in _find_test_path
    module = self._get_module_from_name(name)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\ProgramData\anaconda3\Lib\unittest\loader.py", line 350, in _get_module_from_name
    __import__(name)
  File "C:\Users\rodrigobraz\Documents\PyCharmProjects\UnittestDiscovery\package\integration\test_some_integration.py", line 3, in <module>
    from ..util.foo import run_foo
ImportError: attempted relative import beyond top-level package


======================================================================
ERROR: unit.test_some_unit (unittest.loader._FailedTest.unit.test_some_unit)
----------------------------------------------------------------------
ImportError: Failed to import test module: unit.test_some_unit
Traceback (most recent call last):
  File "C:\ProgramData\anaconda3\Lib\unittest\loader.py", line 407, in _find_test_path
    module = self._get_module_from_name(name)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\ProgramData\anaconda3\Lib\unittest\loader.py", line 350, in _get_module_from_name
    __import__(name)
  File "C:\Users\rodrigobraz\Documents\PyCharmProjects\UnittestDiscovery\package\unit\test_some_unit.py", line 3, in <module>
    from ..util.foo import run_foo
ImportError: attempted relative import beyond top-level package


----------------------------------------------------------------------
Ran 2 tests in 0.001s

FAILED (errors=2)

Why does this fail?

2

There are 2 best solutions below

5
Mr_and_Mrs_D On BEST ANSWER

In answer to the comment Python relative import not working even though root package name is being used

Thanks. I am running from the directory above package (it's called UnittestDiscovery) so I would expect module test_some_unit to have name test.unit.test_some_unit, leading ..util to refer to test.util. Is this reasoning incorrect?

The reasoning is correct (although I don't see any test package) – however probably once the tests are discovered unittest tries to run them from their directory hence you get the error on the top level package – so you should find a way to instruct unittest to run the tests using the -m switch, from the parent of the root package as you do. Don't do the sys.path.append hack, by all means.

4
Tony Stark On

This does not work, because you are running your test unit discovery probably from the root of your project, and when it runs your test_some_unit.py, it tries to import the function relatively to the current directory (aka the root of your project), that is why it states that you attempt a relative import beyond the top-level package.

You can either:

  1. keep your relative imports and consider that you will always run your unit tests using your command line above from the root of your project, and adapt the code of your test_some_unit.py

    from util.foo import run_foo
    

or

  1. (what I recommend) use absolute imports. This way you can both run your unit tests with your command line and by running directly your unit test script.

    import os.path
    import sys
    
    sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))