I want to import a function from another file in the same directory.
Sometimes it works for me with from .mymodule import myfunction
but sometimes I get a:
SystemError: Parent module '' not loaded, cannot perform relative import
Sometimes it works with from mymodule import myfunction
, but sometimes I also get a:
SystemError: Parent module '' not loaded, cannot perform relative import
I don't understand the logic here, and I couldn't find any explanation. This looks completely random.
Could someone explain to me what's the logic behind all this?
This question is related to
python
python-3.x
python-import
I was getting this ImportError: attempted relative import with no known parent package
In my program I was using the file from current path for importing its function.
from .filename import function
Then I modified the current path (Dot) with package name. Which resolved my issue.
from package_name.filename import function
I hope the above answer helps you.
TLDR; Append Script path to the System Path by adding following in the entry point of your python script.
import os.path
import sys
PACKAGE_PARENT = '..'
SCRIPT_DIR = os.path.dirname(os.path.realpath(os.path.join(os.getcwd(), os.path.expanduser(__file__))))
sys.path.append(os.path.normpath(os.path.join(SCRIPT_DIR, PACKAGE_PARENT)))
Thats it now you can run your project in PyCharma as well as from Terminal!!
Hopefully, this will be of value to someone out there - I went through half a dozen stackoverflow posts trying to figure out relative imports similar to whats posted above here. I set up everything as suggested but I was still hitting ModuleNotFoundError: No module named 'my_module_name'
Since I was just developing locally and playing around, I hadn't created/run a setup.py
file. I also hadn't apparently set my PYTHONPATH
.
I realized that when I ran my code as I had been when the tests were in the same directory as the module, I couldn't find my module:
$ python3 test/my_module/module_test.py 2.4.0
Traceback (most recent call last):
File "test/my_module/module_test.py", line 6, in <module>
from my_module.module import *
ModuleNotFoundError: No module named 'my_module'
However, when I explicitly specified the path things started to work:
$ PYTHONPATH=. python3 test/my_module/module_test.py 2.4.0
...........
----------------------------------------------------------------------
Ran 11 tests in 0.001s
OK
So, in the event that anyone has tried a few suggestions, believes their code is structured correctly and still finds themselves in a similar situation as myself try either of the following if you don't export the current directory to your PYTHONPATH:
$ PYTHONPATH=. python3 test/my_module/module_test.py
PYTHONPATH=.
, create a setup.py
file with contents like the following and run python setup.py development
to add packages to the path:# setup.py from setuptools import setup, find_packages setup( name='sample', packages=find_packages() )
If none of the above worked for you, you can specify the module explicitly.
Directory:
+-- Project
¦ +-- Dir
¦ ¦ +-- __init__.py
¦ ¦ +-- module.py
¦ ¦ +-- standalone.py
Solution:
#in standalone.py
from Project.Dir.module import ...
module - the module to be imported
Put this inside your package's __init__.py file:
# For relative imports to work in Python 3.6
import os, sys; sys.path.append(os.path.dirname(os.path.realpath(__file__)))
Assuming your package is like this:
+-- project
¦ +-- package
¦ ¦ +-- __init__.py
¦ ¦ +-- module1.py
¦ ¦ +-- module2.py
¦ +-- setup.py
Now use regular imports in you package, like:
# in module2.py
from module1 import class1
This works in both python 2 and 3.
My boilerplate to make a module
with relative imports in a package
runnable standalone.
package/module.py
## Standalone boilerplate before relative imports
if __package__ is None:
DIR = Path(__file__).resolve().parent
sys.path.insert(0, str(DIR.parent))
__package__ = DIR.name
from . import variable_in__init__py
from . import other_module_in_package
...
Now you can use your module in any fashion:
python -m package.module
python -c 'from package import module'
python package/module.py
#!/bin/env python
) just: package/module.py
NB! Using sys.path.append
instead of sys.path.insert
will give you a hard to trace error if your module
has the same name as your package
. E.g. my_script/my_script.py
Of course if you have relative imports from higher levels in your package hierarchy, than this is not enough, but for most cases, it's just okay.
I had a similar problem and solved it by creating a symbolic link to the package in the working directory:
ln -s ../../../my_package my_package
and then import it as usual:
import my_package
I know this is more like a "Linux" solution rather than a "Python" solution. but it's a valid approach nonetheless.
From PEP 328
Relative imports use a module's __name__ attribute to determine that module's position in the package hierarchy. If the module's name does not contain any package information (e.g. it is set to '__main__') then relative imports are resolved as if the module were a top level module, regardless of where the module is actually located on the file system.
At some point PEP 338 conflicted with PEP 328:
... 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)
and to address the issue, PEP 366 introduced the top level variable __package__
:
By adding a new module level attribute, this PEP allows relative imports to work automatically if the module is executed using the -m switch. A small amount of boilerplate in the module itself will allow the relative imports to work when the file is executed by name. [...] When it [the attribute] is present, relative imports will be based on this attribute rather than the module __name__ attribute. [...] When the main module is specified by its filename, then the __package__ attribute will be set to None. [...] When the import system encounters an explicit relative import in a module without __package__ set (or with it set to None), it will calculate and store the correct value (__name__.rpartition('.')[0] for normal modules and __name__ for package initialisation modules)
(emphasis mine)
If the __name__
is '__main__'
, __name__.rpartition('.')[0]
returns empty string. This is why there's empty string literal in the error description:
SystemError: Parent module '' not loaded, cannot perform relative import
The relevant part of the CPython's PyImport_ImportModuleLevelObject
function:
if (PyDict_GetItem(interp->modules, package) == NULL) {
PyErr_Format(PyExc_SystemError,
"Parent module %R not loaded, cannot perform relative "
"import", package);
goto error;
}
CPython raises this exception if it was unable to find package
(the name of the package) in interp->modules
(accessible as sys.modules
). Since sys.modules
is "a dictionary that maps module names to modules which have already been loaded", it's now clear that the parent module must be explicitly absolute-imported before performing relative import.
Note: The patch from the issue 18018 has added another if
block, which will be executed before the code above:
if (PyUnicode_CompareWithASCIIString(package, "") == 0) {
PyErr_SetString(PyExc_ImportError,
"attempted relative import with no known parent package");
goto error;
} /* else if (PyDict_GetItem(interp->modules, package) == NULL) {
...
*/
If package
(same as above) is empty string, the error message will be
ImportError: attempted relative import with no known parent package
However, you will only see this in Python 3.6 or newer.
Consider a directory (which is a Python package):
.
+-- package
¦ +-- __init__.py
¦ +-- module.py
¦ +-- standalone.py
All of the files in package begin with the same 2 lines of code:
from pathlib import Path
print('Running' if __name__ == '__main__' else 'Importing', Path(__file__).resolve())
I'm including these two lines only to make the order of operations obvious. We can ignore them completely, since they don't affect the execution.
__init__.py and module.py contain only those two lines (i.e., they are effectively empty).
standalone.py additionally attempts to import module.py via relative import:
from . import module # explicit relative import
We're well aware that /path/to/python/interpreter package/standalone.py
will fail. However, we can run the module with the -m
command line option that will "search sys.path
for the named module and execute its contents as the __main__
module":
vaultah@base:~$ python3 -i -m package.standalone
Importing /home/vaultah/package/__init__.py
Running /home/vaultah/package/standalone.py
Importing /home/vaultah/package/module.py
>>> __file__
'/home/vaultah/package/standalone.py'
>>> __package__
'package'
>>> # The __package__ has been correctly set and module.py has been imported.
... # What's inside sys.modules?
... import sys
>>> sys.modules['__main__']
<module 'package.standalone' from '/home/vaultah/package/standalone.py'>
>>> sys.modules['package.module']
<module 'package.module' from '/home/vaultah/package/module.py'>
>>> sys.modules['package']
<module 'package' from '/home/vaultah/package/__init__.py'>
-m
does all the importing stuff for you and automatically sets __package__
, but you can do that yourself in the
Please treat it as a proof of concept rather than an actual solution. It isn't well-suited for use in real-world code.
PEP 366 has a workaround to this problem, however, it's incomplete, because setting __package__
alone is not enough. You're going to need to import at least N preceding packages in the module hierarchy, where N is the number of parent directories (relative to the directory of the script) that will be searched for the module being imported.
Thus,
Add the parent directory of the Nth predecessor of the current module to sys.path
Remove the current file's directory from sys.path
Import the parent module of the current module using its fully-qualified name
Set __package__
to the fully-qualified name from 2
Perform the relative import
I'll borrow files from the Solution #1 and add some more subpackages:
package
+-- __init__.py
+-- module.py
+-- subpackage
+-- __init__.py
+-- subsubpackage
+-- __init__.py
+-- standalone.py
This time standalone.py will import module.py from the package package using the following relative import
from ... import module # N = 3
We'll need to precede that line with the boilerplate code, to make it work.
import sys
from pathlib import Path
if __name__ == '__main__' and __package__ is None:
file = Path(__file__).resolve()
parent, top = file.parent, file.parents[3]
sys.path.append(str(top))
try:
sys.path.remove(str(parent))
except ValueError: # Already removed
pass
import package.subpackage.subsubpackage
__package__ = 'package.subpackage.subsubpackage'
from ... import module # N = 3
It allows us to execute standalone.py by filename:
vaultah@base:~$ python3 package/subpackage/subsubpackage/standalone.py
Running /home/vaultah/package/subpackage/subsubpackage/standalone.py
Importing /home/vaultah/package/__init__.py
Importing /home/vaultah/package/subpackage/__init__.py
Importing /home/vaultah/package/subpackage/subsubpackage/__init__.py
Importing /home/vaultah/package/module.py
A more general solution wrapped in a function can be found here. Example usage:
if __name__ == '__main__' and __package__ is None:
import_parents(level=3) # N = 3
from ... import module
from ...module.submodule import thing
The steps are -
Replace explicit relative imports with equivalent absolute imports
Install package
to make it importable
For instance, the directory structure may be as follows
.
+-- project
¦ +-- package
¦ ¦ +-- __init__.py
¦ ¦ +-- module.py
¦ ¦ +-- standalone.py
¦ +-- setup.py
where setup.py is
from setuptools import setup, find_packages
setup(
name = 'your_package_name',
packages = find_packages(),
)
The rest of the files were borrowed from the Solution #1.
Installation will allow you to import the package regardless of your working directory (assuming there'll be no naming issues).
We can modify standalone.py to use this advantage (step 1):
from package import module # absolute import
Change your working directory to project
and run /path/to/python/interpreter setup.py install --user
(--user
installs the package in your site-packages directory) (step 2):
vaultah@base:~$ cd project
vaultah@base:~/project$ python3 setup.py install --user
Let's verify that it's now possible to run standalone.py as a script:
vaultah@base:~/project$ python3 -i package/standalone.py
Running /home/vaultah/project/package/standalone.py
Importing /home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/__init__.py
Importing /home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/module.py
>>> module
<module 'package.module' from '/home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/module.py'>
>>> import sys
>>> sys.modules['package']
<module 'package' from '/home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/__init__.py'>
>>> sys.modules['package.module']
<module 'package.module' from '/home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/module.py'>
Note: If you decide to go down this route, you'd be better off using virtual environments to install packages in isolation.
Frankly, the installation is not necessary - you could add some boilerplate code to your script to make absolute imports work.
I'm going to borrow files from Solution #1 and change standalone.py:
Add the parent directory of package to sys.path
before attempting to import anything from package using absolute imports:
import sys
from pathlib import Path # if you haven't already done so
file = Path(__file__).resolve()
parent, root = file.parent, file.parents[1]
sys.path.append(str(root))
# Additionally remove the current file's directory from sys.path
try:
sys.path.remove(str(parent))
except ValueError: # Already removed
pass
Replace the relative import by the absolute import:
from package import module # absolute import
standalone.py runs without problems:
vaultah@base:~$ python3 -i package/standalone.py
Running /home/vaultah/package/standalone.py
Importing /home/vaultah/package/__init__.py
Importing /home/vaultah/package/module.py
>>> module
<module 'package.module' from '/home/vaultah/package/module.py'>
>>> import sys
>>> sys.modules['package']
<module 'package' from '/home/vaultah/package/__init__.py'>
>>> sys.modules['package.module']
<module 'package.module' from '/home/vaultah/package/module.py'>
I feel that I should warn you: try not to do this, especially if your project has a complex structure.
As a side note, PEP 8 recommends the use of absolute imports, but states that in some scenarios explicit relative imports are acceptable:
Absolute imports are recommended, as they are usually more readable and tend to be better behaved (or at least give better error messages). [...] However, explicit relative imports are an acceptable alternative to absolute imports, especially when dealing with complex package layouts where using absolute imports would be unnecessarily verbose.
To obviate this problem, I devised a solution with the repackage package, which has worked for me for some time. It adds the upper directory to the lib path:
import repackage
repackage.up()
from mypackage.mymodule import myfunction
Repackage can make relative imports that work in a wide range of cases, using an intelligent strategy (inspecting the call stack).
For PyCharm users:
I also was getting ImportError: attempted relative import with no known parent package
because I was adding the .
notation to silence a PyCharm parsing error. PyCharm innaccurately reports not being able to find:
lib.thing import function
If you change it to:
.lib.thing import function
it silences the error but then you get the aforementioned ImportError: attempted relative import with no known parent package
. Just ignore PyCharm's parser. It's wrong and the code runs fine despite what it says.
if both packages are in your import path (sys.path), and the module/class you want is in example/example.py, then to access the class without relative import try:
from example.example import fkt
I think the best solution is to create a package for your module: Here is more info on how to do it.
Once you have a package you don't need to worry about relative import, you can just do absolute imports.
I had a similar problem: I needed a Linux service and cgi plugin which use common constants to cooperate. The 'natural' way to do this is to place them in the init.py of the package, but I cannot start the cgi plugin with the -m parameter.
My final solution was similar to Solution #2 above:
import sys
import pathlib as p
import importlib
pp = p.Path(sys.argv[0])
pack = pp.resolve().parent
pkg = importlib.import_module('__init__', package=str(pack))
The disadvantage is that you must prefix the constants (or common functions) with pkg:
print(pkg.Glob)
I tried all of the above to no avail, only to realize I mistakenly had a -
in my package name.
In short, don't have -
in the directory where __init__.py
is. I've never felt elated after finding out such inanity.
I needed to run python3 from the main project directory to make it work.
For example, if the project has the following structure:
project_demo/
+-- main.py
+-- some_package/
¦ +-- __init__.py
¦ +-- project_configs.py
+-- test/
+-- test_project_configs.py
I would run python3 inside folder project_demo/ and then perform a
from some_package import project_configs
Moving the file from which you are importing to an outside directory helps.
This is extra useful when your main file makes any other files in its own directory.
Ex:
Before:
Project
|---dir1
|-------main.py
|-------module1.py
After:
Project
|---module1.py
|---dir1
|-------main.py
I ran into this issue. A hack workaround is importing via an if/else block like follows:
#!/usr/bin/env python3
#myothermodule
if __name__ == '__main__':
from mymodule import as_int
else:
from .mymodule import as_int
# Exported function
def add(a, b):
return as_int(a) + as_int(b)
# Test function for module
def _test():
assert add('1', '1') == 2
if __name__ == '__main__':
_test()
Source: Stackoverflow.com