Using MANIFEST.in for Python Source Distributions

When distributing Python projects, code alone is rarely sufficient; packages often require non-code assets such as configuration files, documentation, data sets, and templates. This article provides a focused explanation of the MANIFEST.in file, its role in packaging Python source distributions (sdists), the mechanics of how it controls file inclusion, and how it interacts with modern packaging configurations to ensure reproducible builds.

Default Packaging Behavior

When creating a Python source distribution using build backends such as setuptools, the packaging tool automatically includes a baseline set of files. By default, these typically consist of:

However, arbitrary assets—such as JSON schemas, HTML templates, SQL seed files, or C extension headers—are often ignored by the default packaging discovery mechanism.

The Purpose of MANIFEST.in

A MANIFEST.in file is a text file located in the project's root directory that provides explicit instructions on which files and directories to include or exclude when generating a source distribution (.tar.gz).

During the source distribution build process (such as executing python -m build --sdist), the build tool reads MANIFEST.in to modify the default list of files. It essentially acts as a rule-based filter that constructs the final distribution manifest (SOURCES.txt), guaranteeing that end users or build systems receiving the source tarball have all the ancillary files required to build, test, or install the package.

Common MANIFEST.in Directives

MANIFEST.in uses a declarative syntax processed line-by-line from top to bottom. The most frequently used directives include:

Because rules are evaluated sequentially, later directives override earlier ones.

Distinction Between Source Distributions and Wheels

It is critical to distinguish between a source distribution (sdist) and a built distribution (wheel):

  1. MANIFEST.in targets the sdist: It dictates what exists inside the source tarball. It does not directly configure what gets installed into Python's site-packages on an end user's system.
  2. package_data targets wheels and runtime: Non-code assets intended to be accessible at runtime must be bundled into the wheel. For setuptools, enabling include_package_data = true inside pyproject.toml or setup.cfg instructs the wheel builder to include any runtime package files already captured in the source distribution by MANIFEST.in.

If a file is missing from MANIFEST.in, it will not be present in the source distribution, making it impossible for downstream builders to access or include it when compiling the package from source.