# What is PEP 561?


[PEP 561: Distributing and Packaging Type Information](https://peps.python.org/pep-0561/) defines how an installed package tells type checkers that it ships type information. Its visible artifact is `py.typed`, an empty marker file whose presence means "the annotations in this package are intended to be used."

Without the marker, a library's carefully written annotations are invisible to [mypy](https://pydevtools.com/handbook/reference/mypy.md), no matter how complete they are.

## How does a package declare its types?

PEP 561 gives a package author three options:

- Inline types. Annotate the source and add an empty `py.typed` file inside the [import package](https://pydevtools.com/handbook/explanation/distribution-package-vs-import-package.md) (next to its `__init__.py`).
- Bundled stubs. Ship `.pyi` stub files alongside the modules, plus the same `py.typed` marker. Useful when annotations would clutter the implementation.
- Stub-only packages. Publish types as a separate distribution named with a `-stubs` suffix, the pattern behind packages like `pandas-stubs` and the `types-*` packages on PyPI. No marker needed; the suffix is the signal.

A package with incomplete types can mark itself partial by putting the word `partial` in `py.typed`, telling checkers to fall back to stubs for whatever's missing.

## What happens without the marker?

mypy refuses to read inline annotations from an installed package that lacks the marker:

```console
$ mypy check.py
check.py:1: error: Skipping analyzing "greeter": module is installed, but missing library stubs or py.typed marker  [import-untyped]
```

Every value imported from that package becomes `Any`, so type errors involving it pass silently. The same project produced this with the marker present: `Argument 1 to "greet" has incompatible type "int"; expected "str"`. One empty file is the difference between those two results.

[Pyright](https://pydevtools.com/handbook/reference/pyright.md) and [ty](https://pydevtools.com/handbook/reference/ty.md) behave differently: both read inline annotations from library code even without the marker (in Pyright this is the `useLibraryCodeForTypes` setting, on by default). That asymmetry is a recurring source of confusion: the same missing marker produces clean runs in VS Code's Pylance and `[import-untyped]` errors from mypy in CI.

## Ship the marker in your wheel

For library authors, creating `py.typed` is one `touch`; the failure mode is the file not making it into the built [wheel](https://pydevtools.com/handbook/reference/wheel.md). Hatchling includes every file inside the package directory automatically, while setuptools needs the file declared in `package-data`. Check the wheel directly rather than trusting the config:

```console
$ unzip -l dist/greeter-0.1.0-py3-none-any.whl | grep py.typed
        0  06-09-2026 14:01   greeter/py.typed
```

If users report `[import-untyped]` errors against a package that has the marker in its repository, this is the first thing to check.

## Learn More

- [PEP 561: Distributing and Packaging Type Information](https://peps.python.org/pep-0561/)
- [Distributing type information in the typing specification](https://typing.python.org/en/latest/spec/distributing.html)
- [mypy: Running mypy and managing imports](https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports)
- [What is PEP 484?](https://pydevtools.com/handbook/explanation/what-is-pep-484.md) covers the type-hint system these packages are distributing
