1043

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?

martineau
  • 99,260
  • 22
  • 139
  • 249
John Smith Optional
  • 15,639
  • 10
  • 36
  • 54
  • 121
    This means you are running a module inside the package as a script. Only run scripts from *outside* the package. – Martijn Pieters Jun 07 '13 at 10:27
  • 4
    Probably you should define the conditions you have those 'sometimes' you mention. I understand you do not mean you have random errors. – joaquin Jun 07 '13 at 10:43
  • 1
    @ joaquin: I don't remember. It happened many, many times over the last weeks in different conditions. – John Smith Optional Jun 07 '13 at 10:46
  • 23
    @MartijnPieters: well, unfortunately, this module needs to be inside the package, and it also needs to be runnable as a script, sometimes. Any idea how I could achieve that? – John Smith Optional Jun 07 '13 at 10:49
  • 1
    yeah, I felt on the same pit weeks ago when running scripts in packages and subpackages (I use to use the `if __name__ == '__main__'` to independently run some modules to test them and thus these modules import others sometimes as a main script sometimes as a module). I would be good for the question if you could clarify in which conditions you get one or other outcome. – joaquin Jun 07 '13 at 10:54
  • 29
    @JohnSmithOptional: Mixing scripts inside packages is *tricky* and should be avoided if at all possible. Use a wrapper script that imports the package and runs your 'scripty' function instead. – Martijn Pieters Jun 07 '13 at 10:57
  • 5
    Seems unfortunate. I made a core module with classes/methods that can parse/analyze a certain kind of file, and I also have (mainly for myself) separate secondary modules and scripts which import it--these can massage/convert those files. But I also like to be able to hand that single core file (not a whole complex package) to the end user so they can easily place it next to their file and run it. In that "script mode", it parses and analyzes the file and encoding, tallies up various fields/values/special characters, and gives a report. But it doesn't actually modify the file. Anti-pattern? – Jon Coombs Mar 31 '14 at 03:50
  • 1
    python3 --help ... -m mod : run library module as a script (terminates option list) – The Demz Apr 01 '14 at 19:55
  • @MartijnPieters With your sentence, "Only run scripts from _outside_ the package,” do you mean: (a) Scripts should be outside packages when you run them; (b) Your current working directory should be outside packages when you run the scripts they contain; or something else? – duozmo Jan 08 '16 at 21:22
  • 2
    @duozmo: the script should not reside in the package directory. If your top-level directory is called 'foo', which is on your `PYTHONPATH` module search path, and you have a package 'bar' there (it is a directory with a `__init__.py` file in it), scripts should not be placed inside `bar`, but should live on in `foo` at best. – Martijn Pieters Jan 08 '16 at 22:07
  • It's great to see @MartijnPieters comments in here. Though the reality may not be pleasant at least I won't head scratch on it further. – StephenBoesch Mar 19 '20 at 20:45
  • Please let this be a lesson to everyone to use pathlib in your Python 3 code to resolve relative paths without all of the headaches – Alex W Sep 25 '20 at 17:15

18 Answers18

771

unfortunately, this module needs to be inside the package, and it also needs to be runnable as a script, sometimes. Any idea how I could achieve that?

It's quite common to have a layout like this...

main.py
mypackage/
    __init__.py
    mymodule.py
    myothermodule.py

...with a mymodule.py like this...

#!/usr/bin/env python3

# Exported function
def as_int(a):
    return int(a)

# Test function for module  
def _test():
    assert as_int('1') == 1

if __name__ == '__main__':
    _test()

...a myothermodule.py like this...

#!/usr/bin/env python3

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()

...and a main.py like this...

#!/usr/bin/env python3

from mypackage.myothermodule import add

def main():
    print(add('1', '1'))

if __name__ == '__main__':
    main()

...which works fine when you run main.py or mypackage/mymodule.py, but fails with mypackage/myothermodule.py, due to the relative import...

from .mymodule import as_int

The way you're supposed to run it is...

python3 -m mypackage.myothermodule

...but it's somewhat verbose, and doesn't mix well with a shebang line like #!/usr/bin/env python3.

The simplest fix for this case, assuming the name mymodule is globally unique, would be to avoid using relative imports, and just use...

from mymodule import as_int

...although, if it's not unique, or your package structure is more complex, you'll need to include the directory containing your package directory in PYTHONPATH, and do it like this...

from mypackage.mymodule import as_int

...or if you want it to work "out of the box", you can frob the PYTHONPATH in code first with this...

import sys
import os

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)))

from mypackage.mymodule import as_int

It's kind of a pain, but there's a clue as to why in an email written by a certain Guido van Rossum...

I'm -1 on this and on any other proposed twiddlings of the __main__ machinery. The only use case seems to be running scripts that happen to be living inside a module's directory, which I've always seen as an antipattern. To make me change my mind you'd have to convince me that it isn't.

Whether running scripts inside a package is an antipattern or not is subjective, but personally I find it really useful in a package I have which contains some custom wxPython widgets, so I can run the script for any of the source files to display a wx.Frame containing only that widget for testing purposes.

Aya
  • 33,417
  • 6
  • 47
  • 52
  • 8
    A better way to get SCRIPTDIR is given in [a comment of Import a module from a relative path](http://stackoverflow.com/questions/279237/import-a-module-from-a-relative-path?lq=1#comment15918105_6098238) as `os.path.realpath(os.path.dirname(inspect.getfile(inspect.currentframe())))` if your confident that your module has always a proper `file` you could also use `os.path.realpath(os.path.dirname(__file__))`. – marcz Mar 12 '14 at 14:31
  • 3
    You can expand your PYTHONPATH by applying more shorter and readable code snippet: `sys.path.append( os.path.join( os.path.dirname(__file__), os.path.pardir ) ) ` – Alex-Bogdanov Dec 02 '16 at 15:19
  • I still had trouble when dealing with a script that had the same name as the package, e.g. in `foo/foo.py: from foo.bar import baz`. Inserting the path at the beginning instead of appending solved the issue. The full command: `sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.path.pardir))` – Agargara Oct 18 '17 at 04:37
  • Great answer, I think I finally get it after a few years of stumbling across it! Essentially, the `.` throws off calling the python file from the command line, but the good-ish news is that usually, you can just remove it (hoping for no collisions) – information_interchange May 09 '19 at 04:29
  • 69
    `...which I've always seen as an antipattern.` I don't see how it's an anti pattern... It seems like it would be super convenient to simply have relative imports work intuitively. I just want to be able to import things that I know are in the same directory. I wonder what his reasoning was – Isaac C Way Sep 12 '19 at 03:46
  • 61
    This is the most saddening thing I've ever seen about Python. – AtilioA Jan 16 '20 at 17:42
  • @YungGun It is a _python_ pattern: to not include or disallow features/approaches that are useful. We all need to use the great python libraries: `numpy`, `tensorflow`, `scikit-learn`, `matplotlib`, but wow what environmental pain they live in. – StephenBoesch Mar 19 '20 at 20:36
  • 8
    python3 should really improve on the import, no good and clean solution for this – user3729779 Jun 07 '20 at 10:32
  • 6
    It's baffling to me that in a "modern" language like Python, imports can be so broken. Nearly every other language has solved this problem in a (somewhat) sane way. In Python, I find that I'm constantly dealing with new import issues that crop up, with no simple solutions. – GLee Jun 16 '20 at 04:56
  • 163
    good god I just want to import some code, why all this black magic? – pseudosudo Jun 16 '20 at 19:31
  • 4
    Guido Van Rossum obviously never heard of something called Jupyter Notebooks... – Jivan Jun 19 '20 at 18:49
  • All answers I found were about circumventing the problem of a script inside the package directory importing from itself. This answer finally gave me the correct answer: "Move your scripts away from your package directory"! – licorna Nov 30 '20 at 17:10
  • @licorna so where do you move them to? Importing from sibling directories is extraordinarily difficult and importing from a child directory is a no-go. – ThirtyOneTwentySeven Jan 20 '21 at 06:24
  • Do you have a modern Python solution - if you have no installation scripts to run then the use of \_\_init\_\_.py has not been needed since python 3.3+ – boardtc Jan 29 '21 at 21:54
  • you saved my night man! – T-student Feb 17 '21 at 19:45
  • 3
    This is ridiculous. I need a degree in Python politics to understand why I can't import a class in the same directory as I self learn. – RichieHH Mar 30 '21 at 21:14
392

Explanation

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.

Solution #1: Run your script using -m

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

Solution #2: Set __package__ manually

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,

  1. Add the parent directory of the Nth predecessor of the current module to sys.path

  2. Remove the current file's directory from sys.path

  3. Import the parent module of the current module using its fully-qualified name

  4. Set __package__ to the fully-qualified name from 2

  5. 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

Solution #3: Use absolute imports and setuptools

The steps are -

  1. Replace explicit relative imports with equivalent absolute imports

  2. 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.

Solution #4: Use absolute imports and some boilerplate code

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:

  1. 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
    
  2. 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.

vaultah
  • 36,713
  • 12
  • 105
  • 132
  • 4
    Is it possible to set `__package__` manually if name is `__main__` in order to solve the problem? – Paulo Scardine Jan 26 '15 at 19:09
  • Thanks, nice answers! I was able to load the module using the `imp` module and set `__package__` accordingly but the result is clearly an anti-pattern. – Paulo Scardine Jan 26 '15 at 23:14
  • I get the error `AttributeError: 'PosixPath' object has no attribute 'path'`. – user Aug 09 '16 at 16:16
  • Thanks for quick reply. I'm using nltk package, I'm getting an error: ` File "/usr/local/lib/python3.5/dist-packages/nltk/__init__.py", line 115, in from nltk.decorators import decorator, memoize File "/usr/local/lib/python3.5/dist-packages/nltk/decorators.py", line 23, in sys.path = [p for p in sys.path if "nltk" not in p] File "/usr/local/lib/python3.5/dist-packages/nltk/decorators.py", line 23, in sys.path = [p for p in sys.path if "nltk" not in p] TypeError: argument of type 'PosixPath' is not iterable ` – user Aug 09 '16 at 19:21
  • 1
    One minor gotcha with solution 2, is that this will fail if the file you're trying to run as a script from the command line has the same name as the value of `__package__`, as the file you're trying to run will then take precedence and be imported instead of the package. – nedned May 24 '17 at 02:01
  • 1
    You can also import a file by file path (relative too): https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly – Ctrl-C Dec 29 '17 at 16:24
  • I was running into this problem *because* I was using setuptools and found Solution #3 to work for me. Thanks for writing so many solutions in one answer! – raphael May 07 '18 at 20:51
  • I believe I have tried all the steps you suggested for my case, but it does not seem to be working (https://stackoverflow.com/questions/62365432/modulenotfounderror-no-module-named-model/62365470?noredirect=1#comment110299054_62365470) ? – Daniel Jun 13 '20 at 21:13
  • Thank you for bringing sanity to my world. I still think the decision to do it like this is a horrible idea, but at least I know how it works and how to work around it. – HerrKaputt Oct 19 '20 at 11:38
  • 3
    Thats the reason why people say: "Python is so simple" – Arwed Mett Oct 21 '20 at 17:03
  • @ArwedMett y'all are free to stop posting your opinions on Python's import system and general Python opinions under my answer. – vaultah Oct 22 '20 at 10:14
  • I'm able to use #1 with absolute imports - what am I doing wrong? – Jann Poppinga Nov 12 '20 at 14:01
  • Do you have a solution #5 for modern Python? If you have no installation scripts to run then the use of \_\_init\_\_.py has not been needed since python 3.3+ – boardtc Jan 29 '21 at 21:56
  • @boardtc this statement is misleading and partially wrong. – vaultah Jan 30 '21 at 01:17
  • In what way? All folders in Python 3.3+ are now implicit packages (without \_\_init\_\_.py and you can use absolute imports like "from src.mymodule import MyClass" or relative imports like "from ..src.mymodule import MyClass" https://chrisyeh96.github.io/2017/08/08/definitive-guide-python-imports.html – boardtc Jan 31 '21 at 14:25
159

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.

Brian Burns
  • 14,953
  • 5
  • 69
  • 59
am5
  • 1,782
  • 1
  • 9
  • 10
  • 7
    I also think this deserves more votes. Putting this in every `__init__.py` will basically solve all the relative import errors. – frankliuao Apr 26 '19 at 18:45
  • 6
    I can't speak for others, but I tend to avoid modifying `sys.path` because I'm concerned it might affect other code. (Partially this is because I don't know the intricacies of how it works.) – pianoJames Oct 10 '19 at 18:05
  • 1
    @pianoJames I know what you mean, this (seemingly, after a lot of screwing around) magic fix does seem a little too easy. But it works. Would be interested not know from those-that-know if this is has negative side effects. – Jon Nov 26 '19 at 05:33
  • 2
    Wiat if you have two modules with the same name in two different packages - won't it cause a collision? – Erel Segal-Halevi Oct 19 '20 at 15:09
  • Re: "This works in both python 2 and 3." That said if you have no installation scripts to run then the use of \_\_init\_\_.py has not been needed since python 3.3+ – boardtc Jan 29 '21 at 21:57
  • In my case, `module1` does not contain a Class, only functions. Then I get `No module named 'module1'` – Bersan Mar 31 '21 at 14:20
  • @ErelSegal-Halevi I confirm a drawback is if you have two files from different modules with same name. When running python -m pytest, I have this conflict issue. Would be great if the author could provide a solution for it. – nolw38 Apr 01 '21 at 09:30
49

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()
Leon
  • 28,052
  • 3
  • 52
  • 82
goffer
  • 559
  • 4
  • 6
  • @Perkins Well... in most cases it _wouldn't_. I think relative imports may be the exception though. – wizzwizz4 Jun 13 '18 at 17:26
  • Worked for me with python 3.8. I tried many other solutions and this is the first one that lets me do development in Emacs on the files within the folders as I am building the package. Other advice to make the package work was useless to me, I need to develop the package itself. – pauljohn32 Aug 27 '20 at 03:24
  • 1
    @pauljohn32 If you're developing a package you should be testing it by installing it with an editable pip install (`pip install -e .` from the package root where `setup.py` is) and importing it as a package in your test files, not meddling around with conditional imports like this. – Robin De Schepper Sep 20 '20 at 17:37
14

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.

Grant Curell
  • 734
  • 6
  • 18
  • 1
    usually the parser in the IDE is wrong because its path has not been set. You should find the option which specifies the CWD (current workind directory) and set it to the same thing you use on the command line – Ciprian Tomoiagă Aug 26 '20 at 12:59
  • After far too much futzing around with Python and Pycharm, I'm going to: `try: from .mname import symbol except: from mname import symbol` – gerardw Nov 22 '20 at 17:25
  • 1
    @gerardw PyCharm looks at symbols based on the base folder of the project directory. If it's flopping on you that *typically* means there is something wonky about the location from which you're opening the project. You might try opening it in a different root directory. This is what Ciprian was talking about. Might help you - but maybe not – Grant Curell Nov 22 '20 at 17:29
12

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).

fralau
  • 2,040
  • 18
  • 33
  • I tried it. Still failed: `ImportError: attempted relative import with no known parent package` – pauljohn32 Aug 27 '20 at 02:56
  • @pauljohn32 How did you do your import? Also, `up()` goes only one level in the directory hierarchy. You would need to check what you actually find there. – fralau Aug 27 '20 at 18:25
  • 1
    @fraulau. Thanks. I've been testing. I was trying to leave relative import after up(). That's wrong, I see from your example. If I rewrite as absolute, then up() seems have same effect as `sys.path.append` to add the "containing folder" in the search path. Then the absolute paths work. – pauljohn32 Aug 28 '20 at 18:51
9

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:

  1. Run your code and explicitly include the path like so: $ PYTHONPATH=. python3 test/my_module/module_test.py
  2. To avoid calling 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()
)
LaCroixed
  • 516
  • 4
  • 3
7

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

Solution

I would run python3 inside folder project_demo/ and then perform a

from some_package import project_configs
Árthur
  • 121
  • 1
  • 6
5

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.

vijayraj34
  • 813
  • 12
  • 18
4

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:

  1. Run module as usual: python -m package.module
  2. Use it as a module: python -c 'from package import module'
  3. Run it standalone: python package/module.py
  4. or with shebang (#!/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.

Andor
  • 4,269
  • 4
  • 22
  • 21
  • Thanks for this @Andor, it helped me solve my case. For me __package__ was preset to an empty string, so this boilerplate condition worked: ````if not __package__: [set __package__]```` – dbouz Apr 30 '21 at 19:37
  • Worked like a charm. I needed to add one more `.parent` to line 3, but that's specific to my case of nested packages. Anyhow, thanks! – bobsbeenjamin May 04 '21 at 08:14
3

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
Salt999
  • 51
  • 1
3

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

AlexisG
  • 1,954
  • 2
  • 5
  • 19
Came Up
  • 31
  • 2
3

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.

Cat Mai
  • 300
  • 2
  • 7
2

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.

0

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)
tommiport5
  • 31
  • 7
0

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

PythonSnek
  • 448
  • 15
0

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!!

Hitesh Sahu
  • 31,496
  • 11
  • 150
  • 116
-2

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.

Mahmoud K.
  • 4,822
  • 1
  • 25
  • 38