Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting "ImportError: attempted relative import with no known parent package" when running from Python Interpreter

I'm creating a modular app using Flask blueprints feature. As a result, my directory structure is like this:

project
    __init__.py
    config.py
    mould.py
    modules
        __init__.py
        core
            __init__.py
            core.py
            db.py
            models.py

The modules directory here is not be confused with Python modules, they are for giving a modular structure to my project (core module, foo module, bar module, etc.). Now each folder in the modules directory (and a module inside it with same name such as core.core) is dynamically imported in my main flask app (mould.py) by doing this:

for item in os.listdir("modules"):
    if not os.path.isfile("modules" + os.sep + item) and not item.startswith("__"):
        ppath = "modules" + "." + item
        fullpath = "modules" + "." + item + "." + item
        module = importlib.import_module(fullpath)
        app.register_blueprint(module.app)
        print("Registered: " + ppath)

As a result of this, I'm unable to do this in the module scripts like db.py:

import models

Since it gives a path error as the entire module is being executed at the project level, so I had to do this:

from . import models

This solves the issue and I'm able to successfully import all modules. However, when I go to the core modules directory for some troubleshooting and start the python interpreter, it doesn't allow me to import the db module:

>>> import db
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "db.py", line 7, in <module>
    from . import models
ImportError: attempted relative import with no known parent package

Is there a way around this? So, that I can import the db module successfully in the code as well as interpreter?

like image 212
Prahlad Yeri Avatar asked Jul 13 '17 18:07

Prahlad Yeri


People also ask

How do you use relative import in Python?

Relative imports use dot(.) notation to specify a location. A single dot specifies that the module is in the current directory, two dots indicate that the module is in its parent directory of the current location and three dots indicate that it is in the grandparent directory and so on.

How do you fix ModuleNotFoundError and ImportError?

Python's ImportError ( ModuleNotFoundError ) indicates that you tried to import a module that Python doesn't find. It can usually be eliminated by adding a file named __init__.py to the directory and then adding this directory to $PYTHONPATH .


2 Answers

I know I'm late to the party, but I think I've found a solution to this problem. Hopefully this will be useful to someone else working on a large Python project.

The trick is to try one import format and fall back to the other format if the first fails.

Approach 1

db.py

try:
    # Assume we're a sub-module in a package.
    from . import models
except ImportError:
    # Apparently no higher-level package has been imported, fall back to a local import.
    import models

On the plus side, this approach is pretty simple, but doesn't scale well (module names are duplicated). Scaling can be improved by importing programmatically.

Approach 2 (not recommended)

db.py

import importlib

root = 'project.modules.core'
my_modules = ['core', 'models']
for m in my_modules
    try:
        globals()[m] = importlib.import_module(root + '.' + m)
    except ImportError:
        globals()[m] = importlib.import_module(m)

globals() is the global symbol table.

Of course, now this functionality needs to be duplicated in every module. I'm not sure that's actually an improvement over the first approach. However, you can separate this logic out into its own independent package that lives somewhere on pythonpath.

Approach 3

package_importer.py

import importlib

def import_module(global_vars, root, modules):
    for m in modules
        try:
            global_vars[m] = importlib.import_module(root + '.' + m)
        except ImportError:
            global_vars[m] = importlib.import_module(m)

db.py

import package_importer

root = 'project.modules.core'
my_modules = ['core', 'models']
package_importer.import_module(globals(), root, my_modules)
like image 53
ErikusMaximus Avatar answered Sep 19 '22 16:09

ErikusMaximus


This may be a bit outdated, but maybe someone else will benefit of my answer. Since python2 und python3 have different default import behavior, you have to determine between these two python versions.

Python 2.X The default behavior for import models is to look up first the relative and then the absolute search path order. Therefore it should work.

However, in Python 3.X the default behavior for import models is to look for the model only in the absolute paths (called absolute imports). The current package core gets skipped and since the module db cannot be found anywhere else in the sys.path search path, it throws an error. To resolve this issue you have to use the import statement with dots from . import models to make clear that you are trying to import from a relative directory.

If you are interested to learn more about importing python modules, I suggest you to start your research with the following key words: module search path, python package import and relative package imports

like image 34
schnellerblitz Avatar answered Sep 21 '22 16:09

schnellerblitz