# What are Python namespace packages?


A namespace package lets separate installed packages share one import path. A user installs several distributions, but Python presents them as pieces of one package.

The common example is a company or platform namespace. One distribution might install `acme.storage`, another might install `acme.billing`, and application code imports both under the `acme` prefix. No single wheel owns all of `acme`.

Namespace packages are useful when that shared import path is the product. They are unnecessary when a normal package name or a plain prefix gives readers the same clarity with less coordination.

## Split the install name from the import name

Python packaging has two names in play:

- The distribution name is what [pip](https://pydevtools.com/handbook/reference/pip.md) and [uv](https://pydevtools.com/handbook/reference/uv.md) use to install a project from an index such as [PyPI](https://pydevtools.com/handbook/explanation/what-is-pypi.md).
- The import package name is what Python finds when code runs `import acme.storage`.

[Distribution packages and import packages](https://pydevtools.com/handbook/explanation/distribution-package-vs-import-package.md) do not have to match. Namespace packages add one more twist: several distribution packages can contribute to the same import package.

That gives this shape:

| Distribution package | Import path it contributes |
|---|---|
| `acme-storage` | `acme.storage` |
| `acme-billing` | `acme.billing` |
| `acme-identity` | `acme.identity` |

The user installs separate projects because each one has its own release cycle and dependencies. The user imports a shared `acme` namespace because the projects belong to one public family.

## Let Python combine package portions

A regular package is a directory with an `__init__.py` file. When Python finds that file, that directory owns the package.

A native namespace package follows [PEP 420's implicit namespace package standard](https://peps.python.org/pep-0420/). The shared namespace directory omits `__init__.py`, so Python keeps looking across `sys.path` and combines every matching directory it finds.

Two separately installed distributions can therefore land in the same namespace:

{{< /filetree/folder >}}
      {{< /filetree/folder >}}
    {{< /filetree/folder >}}
  {{< /filetree/folder >}}
  {{< /filetree/folder >}}
      {{< /filetree/folder >}}
    {{< /filetree/folder >}}
  {{< /filetree/folder >}}
{{< /filetree/container >}}

Notice the missing `src/acme/__init__.py` file. That absence is the signal. The leaf packages, `acme.storage` and `acme.billing`, still have normal `__init__.py` files because they are regular packages inside the shared namespace.

With both distributions installed, application code can import from both portions, such as `from acme.storage.client import StorageClient` and `from acme.billing.invoices import InvoiceClient`.

If either distribution adds `src/acme/__init__.py`, it turns `acme` into a regular package and can hide the other distribution's portion. That is the main operational hazard: every project sharing the namespace must follow the same rule.

## Use namespaces for independently released families

Use a namespace package when the import path is a shared prefix for packages that ship separately.

Good fits include:

- An organization publishing client libraries under one brand, such as `acme.storage` and `acme.billing`
- Generated API clients where each service has separate dependencies and release timing
- Plugin namespaces where installed plugins should be discoverable under a shared package like `myapp.plugins.*`
- A [uv workspace](https://pydevtools.com/handbook/how-to/how-to-set-up-a-python-monorepo-with-uv-workspaces.md) whose members need separate wheels but one import family after installation

The common thread is independent ownership. Each portion can be released, versioned, and installed without forcing users to install the whole family.

## Prefer ordinary names for ordinary packages

Most projects should not use namespace packages. A normal import package is easier to explain and harder to break accidentally.

Prefer a regular package when:

- One distribution owns all of the import package
- The package needs initialization code in its top-level `__init__.py`
- Subpackages share tight internal implementation details
- Users install the whole library as one unit
- A flat prefix such as `acme_storage` is clear enough

The prefix alternative avoids shared ownership. `acme_storage` and `acme_billing` look flatter than `acme.storage`, but each package has one owner and one directory. That trade is worth making unless the shared import namespace carries real product meaning.

## Keep plugin systems explicit

Namespace packages are one way to discover plugins. If plugins install modules under `myapp.plugins`, the host application can scan that namespace and import the installed modules.

That works best when the filesystem location is the main contract: "put importable plugin modules here." It works poorly when the plugin system needs metadata, disabled plugins, compatibility checks, or multiple entry points per distribution.

For those cases, package metadata entry points are clearer. A plugin declares a named entry under a group such as `myapp.plugins`, and the host reads that registry through `importlib.metadata`. The plugin does not need to live under the host application's import package.

If a project uses namespace packages for plugins, keep the namespace narrow. Prefer `myapp.plugins` over making `myapp` itself a namespace package. A broken plugin should not make the host application's main package unimportable.

## Configure the build backend deliberately

Namespace packages are an import-system behavior, but the [build backend](https://pydevtools.com/handbook/explanation/what-is-a-build-backend.md) still has to include the right files in the wheel.

For native namespace packages:

- Omit `__init__.py` only from the shared namespace directory.
- Keep `__init__.py` in the leaf package that the distribution owns.
- Configure package discovery so the backend includes the leaf package.
- Document the namespace rule for every team that publishes into it.

With [uv_build](https://pydevtools.com/handbook/reference/uv-build.md), a single namespace portion can use a dotted module name:

```toml {filename="pyproject.toml"}
[tool.uv.build-backend]
module-name = "acme.storage"
```

With [setuptools](https://pydevtools.com/handbook/reference/setuptools.md), use namespace-package discovery rather than ordinary package discovery. The exact setting depends on whether the project uses `pyproject.toml`, `setup.cfg`, or `setup.py`, but the import layout rule stays the same: the shared directory has no `__init__.py`.

Avoid starting new projects with legacy `pkgutil` or `pkg_resources` namespace packages. They exist so older projects can keep working, not because they are a good default for new Python 3 packages. Migrating a legacy namespace requires coordinating every distribution that contributes to the namespace, so treat it as a planned packaging change, not a per-package cleanup.

## Separate workspaces from namespaces

A monorepo workspace and a namespace package solve different problems.

A workspace is a repository-management tool. It lets several projects share a checkout, lockfile, virtual environment, or local dependency graph.

A namespace package is an import-path design. It decides what users type after installing one or more wheels.

Those choices often appear together, but neither one requires the other. A workspace can contain regular packages named `storage_client` and `billing_client`. Separate repositories can publish namespace portions named `acme.storage` and `acme.billing`. Use the workspace to coordinate development; use the namespace only if the installed import path should read as one family.

## Choose the next step

- Building one library? Use a regular package and the [src layout](https://pydevtools.com/handbook/explanation/src-layout-vs-flat-layout.md).
- Splitting a large repository into several projects? Start with [uv workspaces](https://pydevtools.com/handbook/how-to/how-to-set-up-a-python-monorepo-with-uv-workspaces.md) and decide separately whether the installed packages need a shared import prefix.
- Publishing many related packages? Reserve the namespace and make every package follow the same native namespace-package pattern.
- Designing a plugin API? Prefer entry points unless import-path discovery is the main contract.
