5

I have a folder structure like this:

api
-- test
    -- test_api.py
    -- __init__.py
-- api
    -- api.py
    -- __init__.py
    -- sub
        -- sub.py
        -- __init__.py

sub.py:

Base = 'base'

api.py:

from sub.sub import Base

def stuff_to_test(): pass

test_api.py:

from api.api import stuff_to_test

def test_stuff_to_test():
    stuff_to_test()

I am in directory api. I run pytest:

==================================== ERRORS ====================================
______________________ ERROR collecting tests/test_api.py ______________________
ImportError while importing test module '/<somepath>/api/tests/test_api.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
tests/test_api.py:1: in <module>
    from ..api.api import stuff_to_test
api/__init__.py:1: in <module>
    from . import api
api/api.py:1: in <module>
    from sub.sub import Base
E   ImportError: No module named 'sub'
!!!!!!!!!!!!!!!!!!! Interrupted: 1 errors during collection !!!!!!!!!!!!!!!!!!!!
=========================== 1 error in 0.08 seconds ============================

Same happens if I run the python interpreter and import stuff from test_api.py:

>>> from tests.test_api import *
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/<somepath>/api/tests/test_api.py", line 1, in <module>
    from api.api import stuff_to_test
  File "/<somepath>/api/api/__init__.py", line 1, in <module>
    from . import api
  File "/<somepath>/api/api/api.py", line 1, in <module>
    from sub.sub import Base
ImportError: No module named 'sub'

My first idea was to make the import in api.py relative:

from .sub.sub import Base

This way tests run fine. But if I run python api/api.py I get this error:

Traceback (most recent call last):
  File "api/api.py", line 1, in <module>
    from .sub.sub import Base
SystemError: Parent module '' not loaded, cannot perform relative import

How can I have it so tests run and application runs?

Euphe
  • 3,531
  • 6
  • 39
  • 69

2 Answers2

5

I solved it by adding the following to test.__init__.py

project_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
api_path= os.path.join(project_path, 'api')
sys.path.append(api_path)
Euphe
  • 3,531
  • 6
  • 39
  • 69
  • For submodules, I would recommend using `from .sub.sub import Base` in api.py or at least an absolute `from api.sub.sub import Base` – Arunmozhi Oct 18 '17 at 13:21
0

In python there are two ways to import a module, with a relative path or with an absolute path. When you write from sub.sub import Base you are doing an absolute path import, for a relative path import write from .sub.sub import Base.

An absolute path import go look in the PYTHONPATH to find the starting point of your import, so you should write from api.sub.sub import Base.

For more information: Absolute vs. explicit relative import of Python module