The Role of py.typed Marker Files in Python
The py.typed marker file is a packaging standard
introduced in PEP 561 that explicitly signals to static type checkers
that a library supports inline type hints. Without this marker, static
analysis tools such as Mypy, Pyright, and IDE type inspection engines
treat installed third-party packages as untyped by default, ignoring any
existing type annotations. This article explains why the
py.typed file exists, how type checkers interact with it,
and how to properly include it in modern Python package
distributions.
The Problem py.typed
Solves
Before PEP 561 was adopted, type checkers could not reliably determine whether a package's inline type hints were complete, maintained, and intended for public use, or merely experimental and partial. Analyzing incomplete type hints often produced false-positive errors for end users.
To prevent this, static type checkers adopted a conservative default
behavior: any imported third-party library without an explicit signal is
treated as untyped, falling back to Any types or raising
"missing type stubs" warnings. The py.typed marker file
provides an explicit, opt-in contract between package authors and
consumer tooling.
How the Marker Works
A py.typed file is typically an empty file placed
directly inside a Python package's directory alongside its
__init__.py file.
When an end-user runs a type checker on code that imports your package, the tool performs the following checks:
- Locates the Package: The type checker finds the installed package in the Python environment.
- Checks for
py.typed: It looks for a file namedpy.typedat the root of the package. - Parses Annotations: If the file is present, the
type checker parses the inline type hints within the package’s
.pyfiles (or any bundled.pyistub files) and verifies the user's code against them. - Fallback Behavior: If the file is missing, the type checker ignores the package's internal annotations and looks for external stubs (such as typeshed) or treats the package imports as dynamic.
How to Include
py.typed in a Distribution
Creating the file is not enough; the build backend must also be
instructed to include the file in the built wheel and source
distribution (sdist).
1. Create the File
Inside your package directory, create an empty file named
py.typed:
my_package/
├── my_package/
│ ├── __init__.py
│ ├── core.py
│ └── py.typed
├── pyproject.toml
└── README.md
2. Configure Your Build System
Ensure your packaging configuration treats py.typed as
package data:
- Hatchling: Automatically includes
py.typedfiles by default. - Flit: Automatically includes files inside the module directory.
- Poetry: Often includes package files automatically,
but can be made explicit in
pyproject.toml:[tool.poetry] packages = [{ include = "my_package" }] include = ["my_package/py.typed"] - Setuptools: Requires explicit inclusion via
pyproject.tomlorsetup.cfg:[tool.setuptools.package-data] my_package = ["py.typed"]
Rules for Subpackages and Namespace Packages
- Standard Packages: A single
py.typedfile in the top-level package directory covers all submodules and subpackages within that directory. - Namespace Packages: If your project uses PEP 420
namespace packages (packages split across multiple directories without
an
__init__.py), a separatepy.typedfile must be placed in each subpackage directory distributed by your project.
Verification
To verify that your package is correctly configured:
- Build your distribution wheel using a tool like
build(python -m build). - Inspect the contents of the generated
.whlfile (which is a standard zip archive) and confirm thatmy_package/py.typedis present. - Install the wheel into a clean virtual environment and run
mypyorpyrightagainst consumer code that imports the package to ensure types are resolved without warnings.