python pytest importing modules which import local modules

Viewed 5198

I have the following python3 project structure:

tests/
    - testsuite_service1/
        - test_main.py
    - testsuite_service2/
        - test_main.py
src/
    - service1/
       - codebase/
           - __init__.py
           - main.py
           - logger.py
           - waiter.py
    - service2/
       - codebase/
           - __init__.py
           - main.py
           - logger.py
           - waiter.py

In my tests (pytest), I am importing like so:

from src.service1.codebase.waiter import check_status

In order to import a specific function within the service1 module.

Within waiter.py in service1 I am importing a function from logger.py like so

from logger import configure_logger

however, when running the tests, I get the error:

Traceback:
tests/test_main/test_main.py:3: in <module>
    from src.service1.codebase.waiter import check_status
src/codebase/waiter.py:8: in <module>
    from logger import configure_logger
E   ModuleNotFoundError: No module named 'logger'.

This occurs when the test runs, it feels like waiter.py is not looking in its local directory to find logger.py but rather may be in some other location?

Changing it to

from .logger import configure_logger

does solve the issue but causes issues with the runtime I plan on deploying this into (single zip of service1 with no parent directory) and thus gives error Unable to import module 'main': attempted relative import with no known parent package

Is it possible to not use the relative import and have my test understand where to find the desired files?

2 Answers

Adding import os, sys; sys.path.append(os.path.dirname(os.path.realpath(__file__))) to __init__.py in the codebase/ folder solved the issue.

The package src should contain the __init__.py file at root level to indicate it is a package. Then you can use absolute or relative paths to import from other files. The absolute path would be

from src.service1.codebase.logger import configure_logger

But your relative path should work to after including the __init__.py file.

Check this answer for more info.

Related