117

I am learning to program with python and I am having issues with importing from a module in a package. I am usingvisual studio code with Python 3.8.2 64 bit.

My Project Directory

.vscode
├── ecommerce
│   ├── __init__.py
│   ├── database.py
│   ├── products.py
│   └── payments
│       ├── __init__.py
│       ├── authorizenet.py
│       └── paypal.py
├── __init__.py
└── main.py

in the ecommerce/products.py file I have:

#products.py
from .database import Database
p = Database(3,2)

So that I can import the Database class from the ecommerce/database.py file. But I get error

ImportError : Attempted relative import with no known parent package
JamesThomasMoon
  • 6,169
  • 7
  • 37
  • 63
Isaac Anatolio
  • 1,339
  • 3
  • 6
  • 5

8 Answers8

28

It seems, from Python docs and experimenting, that relative imports (involving ., .. etc) only work if

  1. the importing module has a __name__ other than __main__, and further,
  2. the __name__ of the importing module is pkg.module_name, i.e., it has to be imported from above in the directory hierarchy (to have a parent pkg as part of it's __name__.)

OR

the importing module is being specified via module syntax that includes a parent pkg as python -m pkg.module, in which case it's __name__ is still __main__, so it is being run as a script, yet relative imports will work. Here __package__ is set and used to find the parent package while __name__ is __main__; more here.

[After all that, it appears that __package__ and sys.path are key to determining if/how relative imports work. __name__ indicates script or module(i.e., __main__ or module_name). __package__ indicates where in the package the relative imports occur with respect to, and the top of __package__ needs to be in sys.path.]

So, continuing with @AmitTendulkar 's example, if you run this as > python main.py or > python -m main or > python -m ecommerce.products from the project root directory, or enter interactive python from that root directory and import main, or import ecommerce.products the relative imports in products.py will work.

But if you > python products.py or > python -m products from within ecommerce directory, or enter interactive python from that ecommerce directory and import products they will fail.

It is helpful to add

print("In module products __package__, __name__ ==", __package__, __name__)

etc. in each file to debug.

UPDATE:

How imports work depend on sys.path and __package__, not on __name__. Issued from /home/jj, > python sub/mod.py has a sys.path, __package__ of /home/jj/sub, None -absolute imports of modules in sys.path work, relative imports fail.

> python -m sub.mod has sys.path, __package__ of /home/jj, sub -absolute imports of modules in sys.path work, relative imports work relative to sys.path + __package__.

It is more helpful to add

import sys    
print("In module products sys.path[0], __package__ ==", sys.path[0], __package__)

etc. in each file to debug.

Don Slowik
  • 965
  • 10
  • 18
  • 1
    Indeed, I found that I need **all of** 1) putting an `__init__.py` in the top dir of my code **and** 2) adding the parent directory of the top dir to my `PYTHONPATH` **and** 3) setting the `__package__` variable in my Python program to the name of the directory that contains `__init__.py`. – AstroFloyd May 20 '21 at 12:52
  • 1
    Sounds similar to boilerplate described in the fourth paragraph under Proposed Changes of the ‘more here’ link. If your invoking as > python code.py, does invoking as > python -m dir.code eliminate need for 2) and 3) as in my UPDATE? – Don Slowik May 23 '21 at 20:16
  • Sort of; I need to adapt the relative imports slightly and after what looks like a succesful run, I get `python3: Error while finding module specification for 'dir.code.py' (ModuleNotFoundError: __path__ attribute not found on 'dir.code' while trying to find 'dir.code.py')`. – AstroFloyd May 26 '21 at 16:17
  • Point 1. above was very useful for me (importing module must have `__name__` other than `__main__`). I was running into this import issue with an otherwise "correctly" laid out package, but didn't realize that executing a module as a script would screw up local imports. Thank you! – blthayer Jun 06 '22 at 17:25
23

Since you are using Python 3.8 version, the imports work a little differently, but I think this should work:

Use either:

from database import Database
#Database is the class

or try:

import database.Database

lastly, this one is very secure and best practice possibly:

from . import Database  
# The '.' (dot) means from within the same directory as this __init__.py module grab the Database class.
de_classified
  • 1,927
  • 1
  • 15
  • 19
  • 9
    "the imports work a little differently" In what way? I cannot reproduce OP's problem (imports work fine) and your solutions don't work, resulting in `ModuleNotFoundError`, while I'm using Python 3.8.3. What am I not seeing? This post confused me a lot while trying to understand the import system. My `__init__.py` is empty btw. – Jupiter May 27 '20 at 14:24
  • 3
    https://docs.python.org/3/reference/import.html#package-relative-imports shows that what the OP is trying to do should work. – Jupiter May 27 '20 at 14:42
  • 7
    @Jupiter, it should work, but it doesn't for me on 3.8.5. Interestingly, pylance running in VSCode does resolve the imports when written as in the last two snippets, so I get code completion, but when I try running the code, it complains, `ImportError: attempted relative import with no known parent package`. Only the first snippet works for me, but then pylance fails – Arseny Jan 13 '21 at 03:38
  • 1
    @Im guessing here but that might have something to do with that pylance might invoke python from a different working directly than when you run the code yourself. – Jupiter Jan 23 '21 at 14:40
  • 43
    why there's not 'right' answer for this issue in python community ? – Beyhan Gul Feb 01 '21 at 20:25
  • @Arseny, you can use the first snippet and still have VSCode resolving your imports by using VSCode's multi-root workspace and add an extra path: "python.analysis.extraPaths": [ "./" ] to the settings.json. – Sorin Dragan Apr 08 '21 at 11:05
  • 1
    'readability counts' also: import file from file.... I feel like im taking crazy pills. – Bots Fab Nov 09 '21 at 02:34
  • 2
    @BeyhanGul, Python imports are very convoluted and get worse over time. At this point, C `#include`s are better... – SO_fix_the_vote_sorting_bug Dec 03 '21 at 14:30
  • I found [this](https://stackoverflow.com/a/14132912/2760299) answer to be extremely helpful. – topher217 May 17 '22 at 06:56
  • There is a right answer, and it's simple and has been clearly and straightforwardly explained many times. There are a lot of people asking similar questions because they do not properly understand how to characterize their exact situation, or because they read bad advice from other people who puzzled through a different setup without a proper understanding. At any rate, I can guarantee that the problem has **nothing to do with** Python 3.8. – Karl Knechtel Oct 12 '22 at 02:51
  • 1
    @BeyhanGul because people like to create artificial boundaries like that to secure their jobs ... meaning that whoever designed this puzzle was really insecure – IlliakaillI Oct 19 '22 at 02:44
  • "from within the same directory as this __init__.py module grab the Database class." How can a class be within a directory, not within a .py file in a directory? – stunlocked Feb 15 '23 at 10:01
  • @idiot, the dot `.` refers to the current directory, so it means the same directory as the current module where this code is written from ( the __init__.py this is the package file where the database is going to be imported from). So, the Database class is located in a module within the same directory as the module where the code is written for this package. – de_classified Feb 16 '23 at 05:08
  • @de_classified what if there are multiple modules in that directory? How does it know which .py file to open? – stunlocked Feb 16 '23 at 06:22
  • @idiot, you can have multiple modules but the statement `from . import Database` would go get the one with the `Database` import and if there are multiple `Database` imports (similar names) you could specify it more by doing `from nameofpyfile.module import Database` or even in that case you can do `import database.Database` basically the same thing. – de_classified Feb 16 '23 at 06:36
16

i had a similar issue on Windows, and what helped me was (adapted to your directory):

# local imports
import sys
sys.path.append(r"C:\path\to\your\project")

from ecommerce.database import Database
PB1899
  • 400
  • 4
  • 13
  • 2
    This seems like such a hacky way of doing it but it works and makes the most sense. – Tanner Davis Jul 08 '21 at 22:51
  • @TannerDavis I agree it's not the most portable, but solves the issue for OP. I had to use this when the environment we developed was significantly different to the one the script ran, so had to hardcode the location. For portability perhaps pathlib is better, declaring a root folder and append to path, using something like this: ```root_folder = r'{}'.format(pathlib.Path(pathlib.Path(__file__).parent.absolute().parent))``` and here you'll have to figure out where exactly your modules are. – PB1899 Jul 09 '21 at 08:46
  • This works for me, ubuntu 18, python 3.8.1 – Tiana987642 Jun 29 '22 at 08:41
  • 2
    Even easier and worked for me:`sys.path.append(os.getcwd())`, python 3.7.6 – Oliver Dec 08 '22 at 12:06
  • This doesn't fix it for me in Python 3.8.10 – My Code Made Me Suicidal Apr 13 '23 at 20:12
7

Considering the below basic file structure

├── ecommerce
│   ├── __init__.py
│   ├── database.py
|   └── products.py
└── main.py

I am assuming you are running python main.py from the project root.

Here is the text copied from the Python tutorial that explains the basic rule around relative import,

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.

So the below code will work,

# main.py
import ecommerce.products 
# or to use it directly 
# from ecommerce.products import my_product

ecommerce.products.my_product()

Your product.py might look like,

# ecommerce/products.py
from .database import Database

def my_product():
    p = Database(3, 2)

And database.py will look like below,

# ecommerce/database.py

class Database():
    def __init__(self, x, y):
        self._x = x
        self._y = y
        print('Inside DB init')

    # Rest of the methods...

You will now get,

> python main.py
Inside DB init

Ideally the __init__.py file at root is not required as the package name is starting from ecommerce.

You can also run python -m ecommerce.products command to directly call products.py. But that won't yield any output as we are not calling the my_product() function (only defining it).

Calling python ecommerce/products.py will not work as the name of the current module will then become __main__ and not ecommerce. The relative importing only works when used within the current package (so in your main script you always need to import your ecommerce package).

Amit Tendulkar
  • 178
  • 5
  • 15
2

To allow the use of relative imports, you need to "turn your code into a package". This means all of 1) putting an __init__.py in the top directory of your code (in your example .vscode) and 2) adding the full (absolute) path to the parent directory of the top directory (i.e., the parent of your directory .vscode) to your PYTHONPATH and 3) setting the __package__ variable in your Python program to the name of the directory that contains __init__.py, in your case ".vscode".

You should then be able to use in ecommerce/products.py:

from .ecommerce.database import Database

I'm not sure why the dot is in the name .vscode - is that part of the directory name, or part of the lines in your directory tree? If the latter, replace .vscode with vscode above.

AstroFloyd
  • 405
  • 5
  • 14
0

Try this...

from ecommerce.database import Database
-1

This may seem like a hack but it actually work, this happens because of bad package structure

In you case try importing

from .vs_code.ecommerce import database

Now wherever you want to call the class constructor do this:

database.Database()

you can assign this to a variable and use it.

STA
  • 30,729
  • 8
  • 45
  • 59
Ashish Bhatt
  • 77
  • 1
  • 4
-2

Presumably, you make executable the file "products.py", which violates the very concept of packages, the package ceases to be a package and relative import does not work. You must call this outside the package.

Here I am also not sure whether the root directory name can start with a dot like ".vscode".

oleg1551
  • 35
  • 4