Search code examples
pythonmodulepython-importresolution

How to import python modules from sibling directory?


I have a python (3.6.3) project which has the following structure

setup.py
utils #python package
utils/constants.py
utils/name_reports.py
normalization #python package
normalization/resolve_name.py
categorization #python package
categorization/animal_categories.py

You can find the source code here on github - https://github.com/techmango-org/python-import-resolution

Here is the source code for utils/name_reports.py

import normalization.resolve_name as rn
import categorization.animal_categories as ac

test_name = 'DOGY'
resolved_name = rn.get_animal_name(test_name)

print('********ANIMAL CATEGORY IS:', ac.get_animal_score(resolved_name))

When I run python utils/name_reports.py I am getting the following exception

Traceback (most recent call last):
  File "utils/name_reports.py", line 1, in <module>
    import normalization.resolve_name as rn
ModuleNotFoundError: No module named 'normalization'

I have tried to solve this problem by installing the current package into virtual env site packages by running pip install . but that means for every local change I have to run pip install --upgrade . to move my local changes into site packages.

I have been using a hack -m unittest to get this problem solved. Check this screenshot

enter image description here

But I am unable to understand what difference does it create. Here are the exact questions -

  1. How to resolve python import issue in this situation?
  2. Is there a better approach to structure python code such that we do not face relative imports issue?
  3. What difference does -m unittest is creating which is solving this issue?

Solution

  • Since there are already many answers on SO for this*, I will focus on question (2). About what is a better code organization:

    |- setup.py
    |- top_pkg_name
        |- __init__.py
        |- pkg1
            |- __init__.py
        |- pkg2
            |- __init__.py
    

    The (relative) import is done as follows, from inside module_2:

    from ..pkg1 import module1 as m1
    

    Alternatively, you can use absolute imports, which refer to the top package name:

    from top_pkg_name.pkg1 import module1 as m1
    

    In such an organization, when you want to run any module as a script, you have to use the -m flag:

    python -m top_pkg_name.pkg1.module1
    

    For question (3), I'm not sure but it looks like the unittest module adds the folder to path, allowing the imports to happen directly.

    *Refer:

    1. How do I import a Python script from a sibling directory?
    2. Python import module from sibling folder