44.4 Building with python -m build
The python -m build command is the modern, officially recommended tool for building Python distribution packages. It replaces the older setup.py-based approach, which directly invoked setuptools and could lead to inconsistencies. The core philosophy behind build is to provide a reliable, standardized, and isolated process for creating source distributions (sdist) and built distributions (wheel). It ensures the build environment is clean, preventing undeclared dependencies from your local development environment from inadvertently becoming part of the build process.
Installing the build package
Before you can use python -m build, you must install the build package. It is a pure build dependency and should not be part of your project’s runtime dependencies listed in install_requires. The recommended practice is to install it in your development environment or use it via pipx.
# Install globally using pip (can work, but may have dependency conflicts)
pip install build
# Recommended: Install in a virtual environment
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
pip install build
# Best Practice: Install using pipx for an isolated, global tool
pipx install build
Basic usage and output artifacts
The most common invocation is to run build from the root directory of your project, which is expected to contain a pyproject.toml file. By default, it builds both a source archive and a wheel.
# Run from the directory containing pyproject.toml
python -m build
This command performs two key steps. First, it creates a Source Distribution (sdist), a .tar.gz archive containing your source code, pyproject.toml, and any other files specified for inclusion. This is the most fundamental distribution format. Second, it creates a Built Distribution (wheel), a .whl file. A wheel is a built package that can be installed quickly without needing to run a build step on the user’s machine. The output will be placed in a new dist/ directory.
project_root/
├── my_package/
│ └── __init__.py
├── pyproject.toml
├── ...other files...
└── dist/ # Created by build
├── my_package-0.1.0.tar.gz # sdist
└── my_package-0.1.0-py3-none-any.whl # wheel
Building specific distribution types
You can instruct build to create only one type of distribution using the --sdist or --wheel flags. This is useful for debugging or if you only need one format.
# Build only the source distribution (.tar.gz)
python -m build --sdist
# Build only the wheel (.whl)
python -m build --wheel
Building just the wheel is particularly valuable for testing the packaging of pure Python packages or packages with complex binary components, as it isolates any potential build failures to that specific step.
The isolation mechanism and its importance
A critical feature of python -m build is its use of isolation. By default, it creates a temporary virtual environment, installs only the build dependencies specified in your pyproject.toml file (under [build-system]), and executes the build backend (like setuptools or hatchling) within that pristine environment.
This is a major improvement over the old python setup.py sdist bdist_wheel method. The old way ran in your current environment, meaning any package installed locally could potentially be imported and affect the build, leading to unreproducible builds—a package that built on your machine might fail for a colleague or on the PyPI servers. The build tool eliminates this class of errors.
You can see this isolation in action by the pause and flurry of output as it creates the temporary environment and installs dependencies. To disable this for debugging (not recommended for final builds), use the --no-isolation flag.
# Disable isolation - uses your current environment's build backend and dependencies.
python -m build --no-isolation
Configuration via pyproject.toml
The build tool itself doesn’t require configuration; its behavior is driven entirely by your project’s pyproject.toml file. The [build-system] section is paramount. It declares the build backend and the minimum dependencies required to build your project.
# Example pyproject.toml [build-system] section
[build-system]
requires = ["setuptools>=61.0.0", "wheel"]
build-backend = "setuptools.build_meta"
This configuration tells build to create a virtual environment, install setuptools>=61.0.0 and wheel into it, and then use the setuptools.build_meta backend to perform the actual building. Other common backends include hatchling (for Hatch) and flit_core (for Flit).
Common pitfalls and best practices
- Missing
build-systeminpyproject.toml: This will cause the build to fail. Ensure this section is present and correct. - Including unnecessary files: The build backend (e.g., setuptools) determines which files are included. For setuptools, use
MANIFEST.inorpackage_datainsetup.py/setup.cfg. For newer backends, configuration is done inpyproject.toml. Always check the contents of yoursdistby extracting the.tar.gzfile to avoid leaking secrets or including large, irrelevant data. - Building in the wrong directory: Always run
python -m buildfrom the directory that contains thepyproject.tomlfile. Running it from a subdirectory will fail as it cannot find the project configuration. - Outdated build dependencies: Ensure the versions in the
requireslist are appropriate. Pinning to a minimum version (e.g.,"setuptools>=64.0.0") is good practice to ensure a consistent build environment. - Testing your builds: Before uploading to PyPI, test installing your built packages into a fresh virtual environment. This is the ultimate test of your packaging process.
# Create a test virtual environment
python -m venv test_env
source test_env/bin/activate
# Install from the wheel you just built
pip install dist/my_package-0.1.0-py3-none-any.whl
# Test that it imports correctly
python -c "import my_package; print('Success!')"