Search code examples
pythonpython-3.ximporterrorgit-submodules

Unable to run python package stand-alone and nested under another repoistory


I created a python package which I use for brain preprocessing - named bpt. I would also like to use the same package as a sub-module in another project named brain_seg. As a results, I am having problems with import statements which either results in errors when running the package stand-alone or nested.

The structure of the bpt package is as follows:

bpt
   |--__init__.py
   |--module1.py
   |--module2.py
   |--run.py

Import statements in the file run.py look something like:

import os
import module1
import module2

.
.
.

The structure of the brain_seg package is as follows:

brain_seg
   |--bpt
   |     |--__init__.py
   |     |--module1.py
   |     |--module2.py
   |     |--run.py
   |
   |--package2
   |     |--__init__.py
   |     |--module1a.py
   |
   |--__init__.py

When running the run.py script which is part of the stand alone bpt package, everything runs as expected.

If I try to run the same script as part of the brain_seg package, the following error is dispatched:

ModuleNotFoundError: No module named 'module1'

I tried to use relative imports so the nested project will work as expected, resulting in the following run.py file:

import os
from . import module1
from . import module2

.
.
.

Following the change, execution of the brain_seg project worked as expected but when I tried to call the run.py script from the stand alone bpt package, it resulted in the following error being dispatched:

ImportError: attempted relative import with no known parent package

How should I handle the imports such that both options will be supported?


Solution

  • Imports in bpt.run must be absolute or relative:

    from bpt.module1 import ... # absolute
    from .module1 import ...  # relative
    

    To run bpt.run from your repository root with your pythonpath getting correctly set, use -m:

    python bpt/run.py  # bad
    python -m bpt.run  # good
    

    If brain_seg is not truly meant to be a package itself, but just a project consisting of multiple packages and modules, get rid of brain_seg/__init__.py.

    If you do mean to use it as a package, then move it below the repo root, i.e.

    - README.md (etc.)
    - .gitignore (etc.)
    - brain_seg
      - __init__.py
      - bps
        - __init__.py
        - main.py
    

    and use e.g. python -m brain_seg.bps.run from the repository root.

    If you also intend to use bps as a standalone thing, I wouldn't recommend keeping it in the brain_seg hierarchy at all, but to make it pip installable (see https://packaging.python.org/tutorials/packaging-projects/) and then pip install -e ../somewhere/bps to have pip set up an editable link between your projects.