How do implicit relative imports work in Python?
Assume I have the following files,
pkg/
pkg/__init__.py
pkg/main.py # import string
pkg/string.py # print("Package's string module imported")
Now, if I run main.py
, it says "Package's string module imported"
.
This makes sense and it works as per this statement in this link:
"it will first look in the package's directory"
Assume I modified the file structure slightly (added a core directory):
pkg/
pkg/__init__.py
plg/core/__init__.py
pkg/core/main.py # import string
pkg/string.py # print("Package's string module imported")
Now, if I run python core/main.py
, it loads the built-in string
module.
In the second case too, if it has to comply with the statement "it will first look in the package's directory" shouldn't it load the local string.py
because pkg
is the "package directory"?
My sense of the term "package directory" is specifically the root folder of a collection of folders with __init__.py
. So in this case, pkg is the "package directory". It is applicable to main.py
and also files in sub- directories like core/main.py
because it is part of this "package".
Is this technically correct?
PS: What follows after #
in the code snippet is the actual content of the file (with no leading spaces).
Packages are directories with a __init__.py
file, yes, and are loaded as a module when found on the module search path. So pkg
is only a package that you can import and treat as a package if the parent directory is on the module search path.
But by running the pkg/core/main.py
file as a script, Python added the pkg/core
directory to the module search path, not the parent directory of pkg
. You do have a __init__.py
file on your module search path now, but that's not what defines a package. You merely have a __main__
module, there is no package relationship to anything else, and you can't rely on implicit relative imports.
You have three options:
Do not run files inside packages as scripts. Put a script file outside of your package, and have that import your package as needed. You could put it next to the
pkg
directory, or make sure thepkg
directory is first installed into a directory already on the module search path, or by having your script calculate the right path to add tosys.path
.Use the
-m
command line switch to run a module as if it is a script. If you usepython -m pkg.core
Python will look for a__main__.py
file and run that as a script. The-m
switch will add the current working directory to your module search path, so you can use that command when you are in the right working directory and everything will work. Or have your package installed in a directory already on the module search path.Have your script add the right directory to the module search path (based on
os.path.absolute(__file__)
to get a path to the current file). Take into account that your script is always named__main__
, and importingpkg.core.main
would add a second, independent module object; you'd have two separate namespaces.
I also strongly advice against using implicit relative imports. You can easily mask top-level modules and packages by adding a nested package or module with the same name. pkg/time.py
would be found before the standard-library time
module if you tried to use import time
inside the pkg
package. Instead, use the Python 3 model of explicit relative module references; add from __future__ import absolute_import
to all your files, and then use from . import <name>
to be explicit as to where your module is being imported from.