I want to import a module from a subpackage so I went here Relative importing modules from parent folder subfolder and since it was not working I read all the literature here on stack and found a smaller problem that I can reproduce but cannot solve.
I want to use relative imports because I don't wanna deal with sys.path etc and I don't wanna install every module of my project to be imported everywhere. I wanna make it work with relative imports.
My project structure:
project/
__init__.py
bar.py
foo.py
main.py
bar.py:
from .foo import Foo
class Bar():
@staticmethod
def get_foo():
return Foo()
foo.py:
class Foo():
pass
main.py:
from bar import Bar
def main():
f = Bar.get_foo()
if __name__ == '__main__':
main()
I am running the project code from terminal with python main.py and I get the following:
Traceback (most recent call last):
File "**omitted** project/main.py", line 1, in <module>
from bar import Bar
File "**omitted** project/bar.py", line 1, in <module>
from .foo import Foo
ImportError: attempted relative import with no known parent package
Why am I getting this error? It seems that bar doesn't recognize project as the parent package but:
__init__.py is in placebar.py is being run as a module not a script since it is called from main and not from the command line (so __package__ and __name__ should be in place to help solve the relative import Relative imports for the billionth time)Why am I getting this error? What am I getting wrong? I have worked for a while just adding the parent of cwd to the PYTHONPATH but I wanna fix this once and for all.
You should be running your project from the parent of project dir as:
$ python -m project.main # note no .py
This tells python that there is a package named project and inside it a module named main - then relative and absolute imports work correctly - once you change the import in main in either of
from .bar import Bar # relative
from project.bar import Bar # absolute
@kevin41's answer is solid. I wanted to add some additional info that seemed a bit much for comments.
As a general rule, your top level script, the one being run by Python and which has __name__=="__main__", should NOT be inside any package.
By running main.py inside of package, the package itself is never imported.
Relative imports are package relative, not path relative. Python knows it is working with a package import and should use relative imports based on the __package__ special variable being set, and __package__ gets set based on using an import with a ., such as import package.bar, in which case __package__=="package"
The __package__ special variable is never set for the top level script, which is why main.py must use absolute imports.
When main.py imports bar using an absolute import, the value of the __package__ special variable while bar is being processed is also None, because bar was not imported as part of a package, therefore it can't use relative imports either.
With some small changes we can observe how bar imports foo based on how main is importing bar.
bar.py
try:
from .foo import Foo
print("Using relative import")
except ImportError:
from foo import Foo
print("Using absoloute import")
print(f"{__package__=} {__name__=} {__file__=}")
class Bar():
@staticmethod
def get_foo():
return Foo()
Output when running main.py as you have it configured:
Using absoloute import
__package__='' __name__='bar' __file__='c:\\Users\\...\\package\\bar.py'
Moving main.py outside of the package directory and changing the import to from package.bar import Bar, gives us the output
Using relative import
__package__='package' __name__='package.bar' __file__='c:\\Users\\...\\package\\bar.py'
The correct solution is to move your top level script outside of the package folder. If your application needs a primary script within the package, then that script should use relative imports and it should be imported by another top-level script outside of the package.
Example structure:
project/
__init__.py
bar.py
foo.py
main.py
app.py
main.py first line:
from .bar import Bar
app.py
from package.main import main
main()
Additional relevant posts:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With