73

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
  • 4,639
  • 6
  • 33
  • 42
Isaac Anatolio
  • 839
  • 1
  • 4
  • 5

9 Answers9

19

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.
FishingCode
  • 1,649
  • 1
  • 12
  • 19
  • 7
    "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
  • 5
    @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
  • 23
    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
  • 1
    @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
16

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
  • 805
  • 8
  • 16
  • 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
9

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
PB.1899
  • 186
  • 2
  • 11
  • 1
    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. – PB.1899 Jul 09 '21 at 08:46
5

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
  • 149
  • 4
  • 13
1

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
  • 385
  • 4
  • 14
0

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
  • 24,192
  • 8
  • 39
  • 53
0

Try this...

from ecommerce.database import Database
-1

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
  • 37
  • 4
-3

try the following

from ecommerce import database
t0rus
  • 191
  • 3
  • 9