Skip to content
← All Python articles

Python explainer

How Python Finds a Module

About 5 min to read. Allow extra time to work through examples.

See which folders Python searches when you import a module, why the command that starts Python changes the first one, and how to print what your program sees.

This article uses Python. You can make it your practice mode to browse related content across the site.

You run your command-line tool from the project folder, and it works:

cd ~/code/pricing
python -m pricing.cli report

From your home folder, the same command fails with ModuleNotFoundError: No module named 'pricing'. No file moved. What changed is the list of folders Python searched. Print that list with the same Python, from the same folder as the failing command:

import os
import sys

print("python:", sys.executable)
print("folder:", os.getcwd())
for entry in sys.path:
    print(repr(entry))

Python searches a list of folders in order

sys.path is the search path, the list of folders Python looks in, in order, when you import a module. For import pricing, Python checks each entry for a pricing package or a pricing.py file, and it uses the first one it finds.

Most entries come with your Python install, such as the standard library and site-packages, the folder where pip puts installed packages. The first entry depends on how you started Python, as the table below shows.

Python finds a submodule differently. For import pricing.cli, it imports pricing first. Then it looks for cli only in the package's own folders, which it lists in pricing.__path__.

Python runs each module once

sys.modules is the module cache, the dictionary of modules already imported. A second import pricing returns the cached module without running the file again.

Python adds a module to the cache before it runs the module's code. So in a circular import, where two modules import each other, an import can return a module whose code has not finished. If a cache entry is None, importing that name raises ModuleNotFoundError. A test can set an entry to None to block a module on purpose.

The way you start Python sets the first entry

Launch First entry on sys.path
python script.py The script's folder, with symlinks resolved
python -m pkg.mod The current working directory
python -c "...", stdin or the REPL An empty string, which means the current working directory
python appdir or python app.zip That folder or zip file, which holds __main__.py

python -m put the current folder first. In the project folder, that folder holds pricing/, so the import worked. In your home folder, no entry holds pricing. Install the package, for example with pip install -e ., and the import works from any folder.

Python reads the empty string as "the current directory" each time it searches, so it follows a later os.chdir(). An absolute path always means the same folder.

The -P flag leaves out the first entry for the first three launch forms. Then a stray file in your current folder cannot hide an installed module. It keeps the folder or zip file in the last row, because Python needs that entry to find __main__.py. sys.flags.safe_path is True when -P is on. The -I flag turns on -P, and it also ignores PYTHON* environment variables and your per-user site-packages folder.

Check your understanding You run python -P app.zip. Is app.zip on sys.path? Show answer

Yes. -P drops only the entry that Python adds on its own for a script, a module or the current folder. Python needs the zip file on the search path to find its __main__.py, so that entry stays.

Common mistakes

Running a file inside a package directly. python pricing/core.py runs the file as a plain script, so from . import rates fails with ImportError: attempted relative import with no known parent package. Run python -m pricing.core from the folder that contains pricing/, so Python knows the file belongs to pricing.

Fixing an import with sys.path.insert(). Editing the search path in your code makes the import work on your machine and hides the real problem. Install the package, or fix the command that starts the program. Keep path edits for test helpers and tools, with a comment that says why.

Installing into a different Python. If sys.executable is not the Python you installed the package into, pip put the package in another environment's site-packages. Run python -m pip install ... with the same python that runs the program.

Check your understanding The tool works from the project folder, and pricing.__file__ points into that folder. Was the package never installed? Show answer

You cannot tell from that alone. An editable install also loads the code from your project folder. Run python -m pip show pricing to see whether this Python has the package installed.

Going further

Python asks each finder in sys.meta_path in turn to find a module, and the usual path-based finder is the one that searches sys.path. Other entries on the search path come from the PYTHONPATH environment variable and from .pth files in site-packages.

See the Python documentation on how Python builds sys.path, the import system and command-line options.

step 3 of 41

Modules, Packaging and Tooling

Up next Work Out the First Entry on sys.path