ModuleNotFoundError: No module named '__main__.xxxx'; '__main__' is not a package
Asked Answered
P

7

84

Currently trying to work in Python3 and use absolute imports to import one module into another but I get the error ModuleNotFoundError: No module named '__main__.moduleB'; '__main__' is not a package. Consider this project structure:

proj
    __init__.py3 (empty)
    moduleA.py3
    moduleB.py3

moduleA.py3

from .moduleB import ModuleB
ModuleB.hello()

moduleB.py3

class ModuleB:
    def hello():
        print("hello world")

Then running python3 moduleA.py3 gives the error. What needs to be changed here?

Printery answered 1/8, 2017 at 19:42 Comment(6)
The leading '.' only works the way you want it to when the file it is in is in a package.Violative
@Violative is "proj" not a package? since it has an init.py3 ?Printery
You can't use the leading '.' in imports and have it work the way you want it to when you're running the file directly with Python. You have to import the file. If you placed another file outside proj that had import moduleA in it, I believe you would see the output you're expecting.Violative
Possible duplicate of ModuleNotFoundError: What does it mean __main__ is not a package?Amundson
Does this answer your question? How to fix "Attempted relative import in non-package" even with __init__.pyNeutralization
Since Python 3.3, having __init__.py has nothing to do with whether the folder is a package or not. (It was never sufficient, and then it became unnecessary - without __init__.py, you get a namespace package instead of a regular package.)Neutralization
C
55

.moduleB is a relative import. Relative only works when the parent module is imported or loaded first. That means you need to have proj imported somewhere in your current runtime environment. When you are are using command python3 moduleA.py3, it is getting no chance to import parent module. You can:

  • from proj.moduleB import moduleB OR
  • You can create another script, let's say run.py, to invoke from proj import moduleA

Good luck with your journey to the awesome land of Python.

Corm answered 19/10, 2017 at 8:58 Comment(1)
And it's hilariously busted imports.Afloat
R
44

Foreword

I'm developing a project which in fact is a Python package that can be installed through pip, but it also exposes a command line interface. I don't have problems running my project after installing it with pip install ., but hey, who does this every time after changing something in one of the project files? I needed to run the whole thing through simple python mypackage/main.py.

/my-project
    - README.md
    - setup.py
    /mypackage
      - __init__.py
      - main.py
      - common.py

The different faces of the same problem

I tried importing a few functions in main.py from my common.py module. I tried different configurations that gave different errors, and I want to share with you with my observations and leave a quick note for future me as well.

Relative import

The first what I tried was a relative import:

from .common import my_func

I ran my application with simple: python mypackage/main.py. Unfortunately this gave the following error:

ModuleNotFoundError: No module named '__main__.common'; '__main__' is not a package

The cause of this problem is that the main.py was executed directly by python command, thus becoming the main module named __main__. If we connect this information with the relative import we used, we get what we have in the error message: __main__.common. This is explained in the Python documentation:

Note that relative imports are based on the name of the current module. Since the name of the main module is always __main__, modules intended for use as the main module of a Python application must always use absolute imports.

When I installed my package with pip install . and then ran it, it worked perfectly fine. I was also able to import mypackage.main module in a Python console. So it looks like there's a problem only with running it directly.

Absolute import

Let's follow the advise from the documentation and change the import statement to something different:

from common import my_func

If we now try to run this as before: python mypackage/main.py, then it works as expected! But, there's a caveat when you, like me, develop something that need to work as a standalone command line tool after installing it with pip. I installed my package with pip install . and then tried to run it...

ModuleNotFoundError: No module named 'common'

What's worse, when I opened a Python console, and tried to import the main module manually (import mypackage.main), then I got the same error as above. The reason for that is simple: common is no longer a relative import, so Python tries to find it in installed packages. We don't have such package, that's why it fails.

The solution with an absolute import works well only when you create a typical Python app that is executed with a python command.

Import with a package name

There is also a third possibility to import the common module:

from mypackage.common import my_func

This is not very different from the relative import approach, as long as we do it from the context of mypackage. And again, trying to run this with python mypackage/main.py ends similar:

ModuleNotFoundError: No module named 'mypackage'

How irritating that could be, the interpreter is right, you don't have such package installed.

The solution

For simple Python apps

Just use absolute imports (without the dot), and everything will be fine.

For installable Python apps in development

Use relative imports, or imports with a package name on the beginning, because you need them like this when your app is installed. When it comes to running such module in development, Python can be executed with the -m option:

-m mod : run library module as a script (terminates option list)

So instead of python mypackage/main.py, do it like this: python -m mypackage.main.

Retorsion answered 12/8, 2019 at 12:53 Comment(2)
You are a superhero. Thanks for this clear explanation about why this is happening.Etching
Can we see whats in the files in terms of boilerplate?Pistol
L
26

In addition to md-sabuj-sarker's answer, there is a really good example in the Python modules documentation.

This is what the docs say about intra-package-references:

Note that relative imports are based on the name of the current module. Since the name of the main module is always "__main__", modules intended for use as the main module of a Python application must always use absolute imports.

If you run python3 moduleA.py3, moduleA is used as the main module, so using the absolute import looks like the right thing to do.

However, beware that this absolute import (from package.module import something) fails if, for some reason, the package contains a module file with the same name as the package (at least, on my Python 3.7). So, for example, it would fail if you have (using the OP's example):

proj/
    __init__.py (empty)
    proj.py (same name as package)
    moduleA.py
    moduleB.py

in which case you would get:

ModuleNotFoundError: No module named 'proj.moduleB'; 'proj' is not a package

Alternatively, you could remove the . in from .moduleB import, as suggested here and here, which seems to work, although my PyCharm (2018.2.4) marks this as an "Unresolved reference" and fails to autocomplete.

Leanaleanard answered 11/10, 2018 at 9:41 Comment(2)
works for me. pycharm red lighted the import line, but it runs no problem! (Shame on you, pycharm)Colas
@KaiWang To avoid PyCharm marks this as an "Unresolved reference", you can mark root directory as the source root.Amparoampelopsis
C
3

Maybe you can do this before importing the module:

moduleA.py3

import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))

from moduleB import ModuleB
ModuleB.hello()

Add the current directory to the environment directory

Corrosive answered 1/3, 2019 at 3:54 Comment(0)
C
1

An easy way to solve this issue is to launch python from the project directory and use the -m flag:

python3 -m proj.moduleA

This flag will import the parent package so the relative import will work.

Catatonia answered 26/9, 2023 at 10:9 Comment(0)
B
0

Just rename the file from where you run the app to main.py:

from app import app


if __name__ == '__main__':
    app.run()
Bonitabonito answered 4/6, 2020 at 6:38 Comment(0)
A
-1

import os

import sys

sys.path.append(os.path.dirname(os.path.abspath(__file__)))

will solve the issue of import path issue.

Aweinspiring answered 7/11, 2021 at 16:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.