Search code examples
pythonimportrelative-import

How to import modules in python using importlib?


I have this project structure:

Scripts

-- A
    == AA
        ++ __init__.py
        ++ AAA.py
        ++ AAB.py
        ++ AAC.py
    == AB
        ++ __init__.py
        ++ ABA.py
        ++ ABB.py
        ++ ABC.py
-- B
    == BA
        ++ __init__.py
        ++ BAA.py
        ++ BAB.py
        ++ BAC.py
    == BB
        ++ __init__.py
        ++ BBA.py
        ++ BBB.py
        ++ BBC.py

I want to import classes inside Scripts/A/AA/AAA.py into Scripts/B/BB/BBC.py. I have used some solutions:

  1. from AAA import * gives me ImportError: No module named AAA.
  2. from Scripts.A.AA.AAA import * gives me ImportError: No module named Scripts.A.AA.AAA.
  3. from ..A.AA.AAA import * gives me ValueError: Attempted relative import beyond toplevel package.
  4. from ....A.AA.AAA import * gives me ValueError: Attempted relative import in non-package.

what should I do ?

EDIT 1:

I have changed the project's structure and now it's something like this:

Scripts

-- elements
    == A
        ++ __init__.py
        ++ AAA.py
        ++ AAB.py
        ++ AAC.py
    == B
        ++ __init__.py
        ++ BAA.py
        ++ BAB.py
        ++ BAC.py
-- general
    == imports.py
    == __init__.py
-- main.py

I have wrote this piece of code to import all classes:

elementDict = []
dirList = os.walk('elements') 
for dname in dirList:  
    fileList = dname[2]
    for fname in fileList:
        if fname != '__init__.py' and fname.endswith('.py') and (not fname.endswith('_ut.py')) and (not fname.endswith('~')):  
            path = dname[0].split(os.sep)
            path = filter(None, path[path.index('elements'):])
            dir = '.'.join(path)
            mod = importlib.import_module(dir + '.' + fname[:-3])
            for cname, obj in inspect.getmembers(mod, inspect.isclass): 
                if cname not in elementDict:  
                    elementDict[cname] = obj

This results in:

Traceback (most recent call last):
  File "C:\Users\test\Scripts\general\imports.py", line 29, in __init__
    mod = importlib.import_module(dir + '.' + fname[:-3])
  File "C:\Python27\lib\importlib\__init__.py", line 37, in import_module
    __import__(name)
ImportError: No module named elements.A.AAA

Any idea?

EDIT 2:

I have resolved the problem; I did not have any __init__.py in elements.


Solution

  • Relative imports only work within a package structure. See this question for more detailed answers.

    In this case AA and BB are two different packages, so the relative imports in 3 and 4 fail. Scripts isn't a package at all (neither are A or B), so 2 fails and since it's not likely, that you have Scripts/A/AA in your PYTHONPATH also 1 fails.

    You need to have Scripts/A in your PYTHONPATH and then call from AA.AAA import *. If you already have Scripts in the PYTHONPATH, you could add a Scripts/A/__init__.py file to make the directory a python package and call from A.AA.AAA import * instead.