Sys.path
Last edited
Understanding sys.path
Suppose you have a project that uses the common src/ layout:
/tmp/proj
├── pyproject.toml
└── src
├── fetcher
│ └── __init__.py
└── twatch
├── __init__.py
└── __main__.pyfetcher and twatch aren’t two fully separate projects, but you want the code to act like they are, where twatch imports fetcher as a top-level package.
# twatch/__init__.py
import fetcherThis looks like it would be valid? However running:
python3 -m twatch
# /usr/bin/python3: No module named twatchIt fails on twatch before it even gets as far as import fetcher, but that’s the same problem twice, not two problems: neither one lives anywhere python looks. So the question is where does python look, and how do I get src/ into that list.
Let’s see where python is searching to find modules and packages
# cwd /tmp/proj
python3 -c "import sys; print(sys.path)"
[
'', # cwd
'/usr/lib/python313.zip',
'/usr/lib/python3.13',
'/usr/lib/python3.13/lib-dynload',
'/usr/local/lib/python3.13/dist-packages',
'/usr/lib/python3/dist-packages'
]
# As you can see there no way to find our packages/modulesPython is not smart, it doesn’t walk the file system, it JUST loops through this list.
When you run import fetcher it walks the sys.path list and in each entry tries to find (in this order):
- a folder named
fetcherwith__init__.pyinside it - a file named
fetcher.py
# Here's the structre for this test:
.
├── pyproject.toml
└── src
├── fetcher.py # note the module
├── fetcher # note the package
│ └── __init__.py
└── twatch
├── __init__.py
└── __main__.pyNow let’s see it resolve
python3 -m twatch
ran fetcher hello from fetcher/__init__.py
# Moving fetcher package out, so we can hit the module
mv fetcher othername
python3 -m twatch
ran fetcher hello from fetcher.pyOkay I get it. I see it’s dumb and I need to help it find folders (packages) and files (modules). What ways can I do this?
sys accepts an environment variable
PYTHONPATH, which will add it to the start of the list of sys.path.export PYTHONPATH="$PWD/src" python3 -c "import sys; print(sys.path)" [ '', '/tmp/proj/src', ... ]Great so now it will search for packages and modules in that folder as well
Install the packages from source with something like
pip, which will add either a copy or a symlink of the source files into a folder that’s already listed.source .venv/bin/activate python3 -c "import sys; print(sys.path)" [ '', '/usr/lib/python313.zip', '/usr/lib/python3.13', '/usr/lib/python3.13/lib-dynload', '/tmp/proj/.venv/lib/python3.13/site-packages' # replaced : the two dist-packages ]So now python will check the venv’s
site-packagesfolder for packages and modules.We need to first tell our
build tool(and in turnpip) where to find the folder to install.[project] name = "proj" version = "0.1.0" [build-system] requires = ["setuptools"] [tool.setuptools.packages.find] where = ["src"] # this says build (copy) the folders/files from this folder.First I’ll do a non-editable install, which will just copy the current folders/files in the folder so they can be resolved:
pip install . ls .venv/lib/python3.13/site-packages/ fetcher twatch ls .venv/lib/python3.13/site-packages/fetcher __init__.py cat .venv/lib/python3.13/site-packages/fetcher/__init__.py hello_from_fetcher = "hello from fetcher/__init__.py"Second I’ll show a editable install, note implementation differs on the build system
pip uninstall proj # remove our last install (copy) pip install --editable . # read the pyproject.toml to find what we need to do # I'm using setuptools which just uses this .pth file to tell python to look in the folder for more packages (basically a link) (.venv) ➜ proj cat .venv/lib/python3.13/site-packages/__editable__.proj-0.1.0.pth /tmp/proj/src (.venv) ➜ projAll that work and now python can find it’s way to our package from anywhere since the .venv is active and thus the folders are in the
sys.pathlist(.venv) ➜ proj pwd /tmp/proj (.venv) ➜ proj python -m twatch ran fetcher hello from fetcher.py
Commands
# pretty print the sys.path list
python3 -c "import sys; from pprint import pprint; pprint(sys.path)"