J L
J L

Reputation: 319

How does python library handle internal imports?

Consider the following directory

myProject
    myCode.py
    __init__.py
myProject2
    __init__.py
    myProject2Inner
         myCode.py
         __init__.py

myLibrary
    __init__.py
    myPackage1
        __init__.py
        myPackage1Code.py
    myPackage2
        __init__.py
        myPackage2Code.py

If myCode.py is dependent on myPackage1Code.py and myPackage1Code.py is dependent on myPackage2Code.py

I am currently doing the following

sys.path.append(os.path.abspath('../myLibrary/myPackage2/'))
import myPackage2Code

in myPackage1Code.py to make the code run successfully. But this is obviously really bad since the library import path is entirely dependent on who is using it. For example if myProject2Inner requires myPackage1 then the code above wouldn't work.

I would have to do

sys.path.append(os.path.abspath('../../myLibrary/myPackage2/'))
import myPackage2Code

I think I am doing something really wrong here, can someone point me a direction of how to handle import path within a self containing library?

Upvotes: 1

Views: 1176

Answers (1)

MisterMiyagi
MisterMiyagi

Reputation: 50076

In your case, myLibrary, myPackage1 and myPackage2 are packages. To import modules (or packages) from other packages, you must either use an absolute or relative path:

# in myPackage1Code.py
# absolute import
from myLibrary.myPackage2 import myPackage2Code
# relative import
from ..myPackage2 import myPackage2Code

This uniquely identifies the module you actually want, and tells Python where to find it. Note that . and .. are not file-system operations: they also work with dynamically composed namespace packages.

If you want to execute a script contained inside your package, you execute it as part of the package:

python2 -m myLibrary.myPackage1.myPackage1Code

Python2 also has implicit relative imports:

# in myLibrary/__init__.py
from myPackage2 import myPackage2Code

This form is generally discouraged, as it breaks if there is a global myPackage2. It also does not work with Python3.


Note that for packages to work, you have to use them as such! If you directly access part of a package (don't do this at home!)

 # directly run code module of a package in the shell
 python2 myLibrary/myPackage1/myPackage1Code.py


 # directly import module of a package
 sys.path.append(os.path.abspath('../../myLibrary/myPackage2/'))
 import myPackage2Code

then Python does not know that myPackage2Code belongs to myLibrary.myPackage2. This has two notable effects:

  • The myPackage2Code cannot use relative imports. Python considers it a top-level module, so imports cannot go "up" in the package hierarchy.
  • If another module imports it with its full path, this creates two separate modules myPackage2Code and myLibrary.myPackage2.myPackage2Code. Since these contain separate objects, they for example fail isinstance checks of except clauses.

Upvotes: 2

Related Questions