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

  1. Missing build-system in pyproject.toml: This will cause the build to fail. Ensure this section is present and correct.
  2. Including unnecessary files: The build backend (e.g., setuptools) determines which files are included. For setuptools, use MANIFEST.in or package_data in setup.py/setup.cfg. For newer backends, configuration is done in pyproject.toml. Always check the contents of your sdist by extracting the .tar.gz file to avoid leaking secrets or including large, irrelevant data.
  3. Building in the wrong directory: Always run python -m build from the directory that contains the pyproject.toml file. Running it from a subdirectory will fail as it cannot find the project configuration.
  4. Outdated build dependencies: Ensure the versions in the requires list are appropriate. Pinning to a minimum version (e.g., "setuptools>=64.0.0") is good practice to ensure a consistent build environment.
  5. 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!')"