Your package reads an HTML template from src/mypkg/templates/report.html.
The tests pass on your laptop. A teammate installs the package and gets this:
FileNotFoundError: [Errno 2] No such file or directory: '.../site-packages/mypkg/templates/report.html'
Your tests read the files in your project folder. Your teammate got what pip installed. To see what they got, build the package and install it yourself.
Build a wheel
A wheel is a .whl file, which is a zip archive of your package's files,
ready to install. It is what pip downloads from PyPI. Here is a small project:
project/
pyproject.toml
src/
mypkg/
__init__.py
templates/report.html
[build-system]
requires = ["setuptools>=64"]
build-backend = "setuptools.build_meta"
[project]
name = "mypkg"
version = "0.1.0"
requires-python = ">=3.12"
The next article explains each table. From project/, build the wheel:
python -m pip wheel . --no-deps --wheel-dir dist
This writes dist/mypkg-0.1.0-py3-none-any.whl. The file name gives the
package, the version and where it runs. py3-none-any means any Python 3 on
any operating system.
Look inside the wheel
A wheel is a zip file, so zipfile can list it:
import zipfile
with zipfile.ZipFile("dist/mypkg-0.1.0-py3-none-any.whl") as wheel:
for name in wheel.namelist():
print(name)
mypkg/__init__.py
mypkg-0.1.0.dist-info/METADATA
mypkg-0.1.0.dist-info/WHEEL
mypkg-0.1.0.dist-info/top_level.txt
mypkg-0.1.0.dist-info/RECORD
The .dist-info folder is for pip. METADATA holds the name, version and
dependencies, and RECORD lists every file pip installs. The template is
missing. With this configuration, setuptools packs the Python files and
leaves the HTML file out. Name the extra files in pyproject.toml, rebuild,
and the template is in the list:
[tool.setuptools.package-data]
mypkg = ["templates/*.html"]
Install it in a fresh virtual environment
A virtual environment is a folder with its own Python and its own site-packages, the folder where pip puts installed packages. Make one, install the project, and ask Python which file it imported:
python -m venv .venv
source .venv/bin/activate
python -m pip install .
cd ..
python -c "import mypkg; print(mypkg.__file__)"
/home/you/project/.venv/lib/python3.14/site-packages/mypkg/__init__.py
pip install . builds the same wheel and unpacks it into site-packages.
On Windows, activate with .venv\Scripts\activate. The cd .. matters with
a flat layout, where a mypkg folder sits next to pyproject.toml. If you
ran the command from there, Python would import that folder instead of the
installed copy.
An editable install points back at your folder
Reinstalling after every edit is slow. An editable install installs a link back to your project folder instead of a copy:
cd project
python -m pip install -e .
python -c "import mypkg; print(mypkg.__file__)"
/home/you/project/src/mypkg/__init__.py
Edits now take effect without reinstalling. That is also why your tests never noticed the missing template. Python read it straight from your project folder. Use an editable install while you work, and test the built wheel in a fresh environment before a release.
Common mistakes
- Testing only the editable install. It reads your project folder, so a file missing from the wheel goes unnoticed. Install the wheel in a fresh virtual environment too, and run a test that reads each data file.
- Checking the import from inside the project folder. With a flat layout, the current folder can win over the installed copy. The first item in this track shows how to tell which copy you got.
Check your understanding
After python -m pip install -e ., mypkg.__file__ points into your project's src/ folder. Does that tell you the wheel holds every file your package needs?
Show answer
Hide answer
No. An editable install reads from your project folder, so it shows you what
is in the folder, not what is in the wheel. Build the wheel, list it with
zipfile or install it in a fresh virtual environment, and run a test that
reads each data file.
Going further
- An sdist, or source distribution, is a
.tar.gzof the project source. pip can build a wheel from it when no wheel fits your platform. python -m buildanduv buildalso build wheels, and they write an sdist as well.- The packaging tutorial in the Python Packaging User Guide walks through a whole project.