Relative Imports

Hello,

I am currently on the topic of Relative Imports. My package is set up as shown in the following figure:

package_layout

I have Windows 11 Home, and Python v3.12.

I am attempting to import file_2.py and sub_file.py into file_1.py. The script in my file_1.py is as follows:

'''  Desktop\pkg\package1\file_1.py   ''' 

'''  Action Desired: Import file_2.py and sub_file.py     '''

from . import file_2
from .sub_package import sub_file

num1 = 11
string1 = "I'm in package1, file_1."

def run_1(message):
    print(message)

run_1(string1)

file_2.py has the following code:

'''  Desktop\pkg\package1\file_2.py   ''' 

num2 = 22
string2 = "I'm in package1, file_2."

def run_2(message):
    print(message)

run_2(string2)

The code in the sub_file.py is:

'''  Desktop\pkg\package2\sub_package\sub_file.py   '''

num_sub = 100
string_sub = "I'm in package2,\sub_package, sub_file!"

def run_sub(message):
    print(message)

run_sub(string_sub)

When I run file_1.py, I get the following error:

    from . import file_2
ImportError: attempted relative import with no known parent package

Can someone please advise.

Thank you.

None of this has anything to do with your operating system, and you would have to go into truly ancient Python versions for that to matter either.

You must ensure that the pkg package is imported before its contents can do relative imports of each other. There are many ways to do this, but in general you want a program to start with a single absolute import first.

For example, if I create a trivial test package:

$ mkdir project
$ cat >> project/one.py
from . import two
$ touch project/two.py
$ python project/one.py 
Traceback (most recent call last):
  File "project/one.py", line 1, in <module>
    from . import two
ImportError: attempted relative import with no known parent package

It doesn’t work from inside the directory, either:

$ (cd project && python one.py)
Traceback (most recent call last):
  File "one.py", line 1, in <module>
    from . import two
ImportError: attempted relative import with no known parent package

The simplest thing is to run the script as a module, using the package name rather than the source code file name. So this runs without error:

$ python -m project.one

Practically speaking, if we want an “entry point” to the code, we should use a driver script that is outside the package, which can find the package by absolute import and use something from it:

$ cat >> driver.py
import project.one 
$ python driver.py

That worked because the containing folder for driver.py was on the module search path (sys.path), because of how we started Python - so the project folder could be found directly within the CWD.

Here are the rules for how sys.path gets initialized:

We can, of course, make this more robust by actually installing the package. That will make sure that the package appears on sys.path regardless, and also opens up other, more explicit options for creating an entry point.

The corresponding Q&A on Stack Overflow is not terribly high quality (everything to do with import is a huge mess full of redundancy and cargo-culting) but it does give the necessary information:


Once you have fixed this, the next problem is that your second relative import simply has the wrong “path”. It should be from ..package2.sub_pkg import sub_file.

1 Like

Thank you for responding to my post. Much appreciated.

I noticed that you begin your instructions in terms of DOS commands. In order to make use of relative imports, do you have to set up the package via the command prompt? You can’t set up your package exclusively within Python (a bit odd if so)?

After this quote, you begin immediately with DOS instructions inside the command prompt. Why not with instructions within the Python shell editor? Is Python not equipped with the ability to create Python packages? In my opinion it would make sense for there to be a Pythonic way of creating packages for relative imports without the need of deferring this action to DOS.

Yes, I fixed this. Thank you.

Update. After making the update with respect to the sub_file import reference in file_1, I executed the following commands in the command prompt. After doing so, the following response was observed:

C:\User\mycomp\Desktop\pkg>python -m package1.file_1
I'm in package1, file_2
Traceback (most recent call last):
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "C:\Users\mycomp\Desktop\pkg\package\file_1.py", line 18, in <module>
   from ..package2.sub_package import sub_file
ImportError: attempted relative import beyond top-level package


C:\User\mycomp\Desktop\pkg>

They’re Linux commands, actually. I’m only doing this as a quick way to show a complete demonstration. The cat, touch etc. stuff is just to create source code files that I use to show how it works.

I’m not sure how to interpret this. There isn’t a “within Python” here, because Python isn’t the system that you use to create files, it’s the rules for interpreting what’s written in them. It’d be like asking about how to write books “within English” when someone showed how to use a word processor instead of a pen and paper.

But the point is that the package doesn’t really need any “setup”. I created some files and put them in folders to give a simpler example than your actual project. It’s not necessary to make any more files or do anything special with the folder structure. What is necessary is to make sure that either:

  • some absolute import happens first;

  • Python runs the code as a module (with a fully-qualified package name) using -c.

This way, the package will be loaded first before any relative imports are attempted.

Keep in mind here that Python packages are modules - they’re represented within Python by the same type of object, and stored within sys.modules in the same way.

Because then I would have to go back and forth between plain English explanations of steps to follow, and code blocks showing things to type. :wink:

Sorry for the confusion.

The command should be run from the Desktop folder, and it should use pkg in the path:

C:\User\mycomp\Desktop\>python -m pkg.package1.file_1

This way, pkg is the top-level package, which contains package1 and package2 sub-packages. Since file_1 and sub_file now have a common “root” - in the package system, not just your file hierarchy on disk - they can use relative import.

If you don’t want the pkg folder to represent a Python package, then it won’t be possible to use relative import between a module in package1 and another module package2 or vice-versa - as far as Python is concerned, they are not related to each other, and they could be relocated to any separate places. As I described, relative import only works within an already-loaded package; for something to be “an already-loaded package”, we must be willing to treat it as a package.

1 Like

Hi Karl,

thank you very much for all of your time and attempting to resolve this issue. :slightly_smiling_face:

My misinterpretation is all. I create my folders/directories via Windows. This is not the only way of course. You can of course create them via DOS or Linux as you know. I was just a bit concerned why you were doing it differently and thinking there was a more sinister reason for doing so (there isn’t, just preference I suppose).

Well, I have found where I went wrong. The relative import syntax from my original post is not wrong at all. The issue was that I was attempting to run that module from its location that was the issue. From Learning Python, 5th E.:

In Python 3.X, the new relative search rule change means that a file can no longer
serve as both script and package module as easily as it could in 2.X.

In order to test/run the relative imports instructions, you must implicitly run them from a main module, …, the main of the package. So, I have added a root_main.py module to the project. If I run the project from there, no exception errors are encountered. Here is my updated package:

relative_import_package

From the figure, I was attempting to run the file_1.py module in the package1 directory. This is what was causing the errors (as highlighted by the Learning Python reference). I went ahead and added a new module named root_file.py. When I ran the script from there, all was well. :smiley: :tada: In other words, I was attempting to run the module as the package script. Of course, this module is a package module and not the script of the package, hence the exception errors.

In my new module root_file.py, I have the following script:

from package1 import file_1

file_1.run_1('Hello from the pkg\root_file.')

The script in my file_1.py file, I have:

from . import file_2
from .sub_package1 import sub_file_1

num1 = 11
string1 = "I'm in package1, file_1."

def run_1(message):
    print(message)

run_1(string1)

Here is the YouTube video that I referenced. Note that it discusses both the absolute and the relative import methods. If you want to skip to the beginning of the relative imports discussion, jump to the ~5:35 minute mark.

The limitation of relative imports is that you can only import modules that are along the hierarchy (above and below) but not adjacent. For example, with relative import syntax, I wouldn’t be able to import modules from the package2 directory. For that, you have to use absolute path imports.

Well, thank you again for your time and attempting to resolve this issue. Much appreciated!

All is well now.