How to fix "Attempted relative import in non-package" even with __init__.py

Viewed 608446

I'm trying to follow PEP 328, with the following directory structure:

pkg/
  __init__.py
  components/
    core.py
    __init__.py
  tests/
    core_test.py
    __init__.py

In core_test.py I have the following import statement

from ..components.core import GameLoopEvents

However, when I run, I get the following error:

tests$ python core_test.py 
Traceback (most recent call last):
  File "core_test.py", line 3, in <module>
    from ..components.core import GameLoopEvents
ValueError: Attempted relative import in non-package

Searching around I found "relative path not working even with __init__.py" and "Import a module from a relative path" but they didn't help.

Is there anything I'm missing here?

22 Answers

Issue is with your testing method,

you tried python core_test.py

then you will get this error ValueError: Attempted relative import in non-package

Reason: you are testing your packaging from non-package source.

so test your module from package source.

if this is your project structure,

pkg/
  __init__.py
  components/
    core.py
    __init__.py
  tests/
    core_test.py
    __init__.py

cd pkg

python -m tests.core_test # dont use .py

or from outside pkg/

python -m pkg.tests.core_test

single . if you want to import from folder in same directory . for each step back add one more.

hi/
  hello.py
how.py

in how.py

from .hi import hello

incase if you want to import how from hello.py

from .. import how

As Paolo said, we have 2 invocation methods:

1) python -m tests.core_test
2) python tests/core_test.py

One difference between them is sys.path[0] string. Since the interpret will search sys.path when doing import, we can do with tests/core_test.py:

if __name__ == '__main__':
    import sys
    from pathlib import Path
    sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
    from components import core
    <other stuff>

And more after this, we can run core_test.py with other methods:

cd tests
python core_test.py
python -m core_test
...

Note, py36 tested only.

You can use from pkg.components.core import GameLoopEvents, for example I use pycharm, the below is my project structure image, I just import from the root package, then it works:

enter image description here

This approach worked for me and is less cluttered than some solutions:

try:
  from ..components.core import GameLoopEvents
except ValueError:
  from components.core import GameLoopEvents

The parent directory is in my PYTHONPATH, and there are __init__.py files in the parent directory and this directory.

The above always worked in python 2, but python 3 sometimes hit an ImportError or ModuleNotFoundError (the latter is new in python 3.6 and a subclass of ImportError), so the following tweak works for me in both python 2 and 3:

try:
  from ..components.core import GameLoopEvents
except ( ValueError, ImportError):
  from components.core import GameLoopEvents

As you have already marked everything as a module, there's no need to use the relative reference if you launch as python module.

Instead of

from ..components.core import GameLoopEvents

simply

from pkg.components.core import GameLoopEvents

When you run from the parent of pkg, use the following

python -m pkg.tests.core_test

Try this

import components
from components import *

Here's one way which will piss off everyone but work pretty well. In tests run:

ln -s ../components components

Then just import components like you normally would.

For me only this worked: I had to explicitly set the value of package to the parent directory, and add the parent directory to sys.path

from os import path
import sys
if __package__ is None:
    sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
    __package__= "myparent"

from .subdir import something # the . can now be resolved

I can now directly run my script with python myscript.py.

python <main module>.py does not work with relative import

The problem is relative import does not work when you run a __main__ module from the command line

python <main_module>.py

It is clearly stated in PEP 338.

The release of 2.5b1 showed a surprising (although obvious in retrospect) interaction between this PEP and PEP 328 - explicit relative imports don't work from a main module. This is due to the fact that relative imports rely on __name__ to determine the current module's position in the package hierarchy. In a main module, the value of __name__ is always '__main__', so explicit relative imports will always fail (as they only work for a module inside a package).

Cause

The issue isn't actually unique to the -m switch. The problem is that relative imports are based on __name__, and in the main module, __name__ always has the value __main__. Hence, relative imports currently can't work properly from the main module of an application, because the main module doesn't know where it really fits in the Python module namespace (this is at least fixable in theory for the main modules executed through the -m switch, but directly executed files and the interactive interpreter are completely out of luck).

To understand further, see Relative imports in Python 3 for the detailed explanation and how to get it over.

I've had similar issues and as a software engineer, I think some of the suggested solutions here are not ideal. If you want relative imports, you should not have try/except and then sometimes do an absolute import. Also, to run a program, you should not have to change sys.path.

Furthermore, the program should always work, independent of your current working directory and independent of how you start it.

Thus, I've created a new, experimental import library: ultraimport It allows file system based imports, no matter how you run your code.

From the original question, you would change your core_test.py to something like

import ultraimport
GameLoopEvents = ultraimport('__dir__/../components/core.py', 'GameLoopEvents')
print(GameLoopEvents)

and it would always find it, no matter how you run your tests.

$ python -m tests.core_test
<class 'core.GameLoopEvents'>
 python ./tests/core_test.py 
<class 'core.GameLoopEvents'>

I've also put this example into the examples folder in the git repo.

As the library is experimental, I am interested in feedback. It works for me but it not widely tested, yet.

This is very confusing, and if you are using IDE like pycharm, it's little more confusing. What worked for me: 1. Make pycharm project settings (if you are running python from a VE or from python directory) 2. There is no wrong the way you defined. sometime it works with from folder1.file1 import class

if it does not work, use import folder1.file1 3. Your environment variable should be correctly mentioned in system or provide it in your command line argument.

If your project structure would look like this:

   project
     |
     | --- module1
     |      |
     |      file1.py
     |
     |-----module2
     |     |
     |     file2.py

and you are going import file1.py from within file2.py, you can do this in file2.py:

import sys
sys.path.append('.')

import file2

I still don't know why and how, but it worked for me.

Because your code contains if __name__ == "__main__", which doesn't be imported as a package, you'd better use sys.path.append() to solve the problem.

Related