Python namespace packages are an advanced Python feature. You may have heard them mentioned in relation to the __init__.py file. Specifically, if you don’t include at least an empty __init__.py file in your package, then your package becomes a namespace package.
For the most part, namespace packages and regular packages won’t behave differently when it comes to using them in your project. In fact, you’ve probably accidentally forgotten to include an __init__.py file in at least one package but didn’t notice any side effects. While namespace packages are generally a bit slower to import than regular packages, you won’t usually run into many issues.
In this tutorial, you’ll dive into what Python namespace packages are, where they come from, why they’re created when you don’t include an __init__.py file, and when they might be useful.
Along the way, you’ll get the chance to make your own namespace package structures, and you’ll install and extend an experimental namespace package from PyPI.
Python namespace packages are probably going to be useful for people who manage or architect collections of related packages. That said, you’ll get to experiment with a project that can make namespace packages more accessible to the average user.
This is an advanced tutorial, and to get the most out of it, you should be very familiar with the basics of Python and comfortable with the import system, as well as having some exposure to packaging.
So, what’s a Python namespace package, and what’s it for?
Free Source Code: Click here to download the free source code that you’ll use to explore Python namespace packages.
In Short: Python Namespace Packages Are a Way to Organize Multiple Packages
By way of a quick recap, you’ll first examine the general concept of a Python namespace before tackling namespace packages. A namespace is a way to group objects under a specific name. You can group values, functions, and other objects.
For example, when you import math, you gain access to the math namespace. Inside the math namespace, you can select from a whole host of different objects.
You can also think of a Python dictionary as a namespace. With a Python dictionary, you can take two variables that started out as completely separate, independent variables and include them within the same dictionary namespace:
>>> real_python = {}
>>> home_page = "https://realpython.com"
>>> import_tutorial = "python-import"
>>> real_python["home_page"] = home_page
>>> real_python["import_tutorial"] = import_tutorial
Now, you can reference both the home_page and import_tutorial values from the real_python namespace. Namespace packages work in a similar way, but they combine whole packages instead of values or other Python objects.
This allows you to have two independent packages on PyPI, for example, that still share the same namespace. One single namespace package doesn’t make a whole lot of sense. To really see the advantage of namespace packages, you need at least two packages.
Namespace packages are typically used by businesses that may have extensive libraries of packages that they want to keep under a company namespace. For example, the Microsoft Azure packages are all accessible, once installed, through the azure namespace.
That’s why you’ll create your own company namespace in the next section.
What Does a Namespace Package Look Like?
Imagine you work for the Snake Corporation, and your team wants to make sure that all the packages in its library are always accessible from the snake_corp namespace. So, no matter what package you’re using, as long as it was made by the Snake Corporation, you’ll import from snake_corp.
Without namespace packages, you’d have two options:
- Create a monorepo, which would be a single package called
snake_corpwith hundreds of modules for all the different libraries and utilities that you might need. - Create various packages, but prefix them all with
snake_corp. For example, you might havesnake_corp_dateutilas a package.
The trouble with creating a monorepo is that everyone has to download all the code even if they only use a tiny fraction of it. It also complicates matters in terms of version management and other packaging workflows, especially if different teams are in charge of different subpackages.
The issue with creating various packages with a common prefix is that it can be quite verbose, messy, and inelegant. And, at the end of the day, the Snake Corporation CEO has said that they don’t like that solution. They’d prefer using a monorepo over prefixing all the packages. Besides, using common prefixes is just a convention that doesn’t technically create a common namespace.
What you have in this situation is a perfect use case for namespace packages! Namespace packages allow you to have multiple separate packages with their own packaging workflow, but they can all live in the same snake_corp namespace.
Note: In the following examples, you’ll be creating a bunch of packages. You’ll note that the containing folders and the package names vary depending on whether you’re referring to the name for when you pip install or import, for example. Check out this table for a rough breakdown of what’s generally used:
| Purpose | Typical Casing | Example |
|---|---|---|
PyPI and pip |
Kebab | charset-normalizer |
| Import | Snake | charset_normalizer |
| Prose | Pascal or none | Charset Normalizer |
In the following examples, you’ll be using these conventions. But be aware that these conventions aren’t universal.
To illustrate how you can achieve the kind of structure to make the Snake Corporation CEO happy, you’ll now make three packages:
snake-corp-dateutil: Some date utilities specific to the Snake Corporationsnake-corp-magic-numbers: Some number-generation utilities specific to the Snake Corporationsnake-service: An employee project that wants to make use of both thesnake-corp-dateutilandsnake-corp-magic-numberspackages
In this example, you’ll take the prose name to be the same as the pip name.
In the next section, you’ll set up the packages and install them in your virtual environment. In the section after that, you’ll get to use them.
Setting Up Some Namespace Packages
To get started setting up the Snake Corporation’s namespace packages, create the following folder and file structure:
snake-corp/
│
├── snake-corp-dateutil/
│ ├── snake_corp/
│ │ └── dateutil.py
│ └── pyproject.toml
│
├── snake-corp-magic-numbers/
│ ├── snake_corp/
│ │ └── magic.py
│ └── pyproject.toml
│
└── snake-service/
└── snake_service.py
With that, you have the basic file structure of three packages. Both the utility packages, snake-corp-dateutil and snake-corp-magic-numbers, start by defining an implicit namespace package called snake_corp. It’s an implicit namespace package because of the absence of an __init__.py file.
As for the import name, you’ll import the snake-corp-dateutil package as snake_corp.dateutil and snake-corp-magic-numbers as snake_corp.magic.
You’ll use a virtual environment to contain your project. Inside the virtual environment, you’ll install the two utility packages, which is why you have a pyproject.toml file for each. This simulates what it’d be like if they were available on a corporate package server, for example.
Inside the dateutil.py module, all you need to include is a business-critical function that’ll return the number of days until World Snake Day:
# snake-corp-dateutil/snake_corp/dateutil.py
from datetime import datetime
def days_to_snake_day(from_date=None):
"""Return the number of days until World Snake Day (July 16th)."""
if from_date is None:
from_date = datetime.now()
if from_date.month > 7 or (from_date.month == 7 and from_date.day > 16):
day_of_snake = datetime(from_date.year + 1, 7, 16)
else:
day_of_snake = datetime(from_date.year, 7, 16)
return (day_of_snake - from_date).days
You’ll use just one function here for demonstration purposes, but you can imagine that you might have a bunch of date-related functions that are specific to the Snake Corporation.
In the corresponding pyproject.toml file, you fill out some information that setuptools needs to properly install the package:
# snake-corp-dateutil/pyproject.toml
[build-system]
requires = ["setuptools", "setuptools-scm"]
build-backend = "setuptools.build_meta"
[tool.setuptools.packages.find]
where = ["."]
include = ["snake_corp"]
namespaces = true
[project]
name = "snake-corp-dateutil"
version = "1.0.3"
description = "Date Utilities for the Snake Corporation"
license = { text = "Snake Corp Software License Agreement" }
This configuration file ensures that pip knows how to treat this package.
The tool.setuptools.packages.find section is what controls package discovery, and while namespace packages are generally enabled by default, you can be explicit about including your namespace package by specifying where, include, and namespaces options.
You can read more about configuring package discovery in pyproject.toml files by checking out the setuptools documentation.
Note: It’s possible to get away with including less information in your pyproject.toml file. In some cases, pip can even install a package without pyproject.toml or an equivalent file. That said, in other cases, it’ll treat a namespace package as a normal package, meaning that the examples below won’t work.
In the magic.py module, you can also add in a special number generator that’s required for some special Snake Corporation beaurocracy. This number generator is just a placeholder for the example:
# snake-corp-magic-numbers/snake_corp/magic.py
import random
def secret_number_generator():
# Insert special Snake Corporation sauce here ...
return random.randint(0, 9999)
You only return a random number in this function, which isn’t very useful in and of itself. But you can imagine that this might be a special token generator. You may need to get a token from a corporate API service or generate a universally unique identifier (UUID).
The corresponding pyproject.toml file is very similar to the last one, with changes to the name, version, and description:
# snake-corp-magic-numbers/pyproject.toml
[build-system]
requires = ["setuptools", "setuptools-scm"]
build-backend = "setuptools.build_meta"
[tool.setuptools.packages.find]
where = ["."]
include = ["snake_corp"]
namespaces = true
[project]
name = "snake-corp-magic-numbers"
version = "1.1.8"
description = "Super Magic Secret Numbers for the Snake Corporation"
license = { text = "Snake Corp Software License Agreement" }
Now the two utility packages are ready for you to install with pip in a virtual environment. Well done!
In the next section, you’ll see what it’s like to develop a new package, making use of the namespace packages that you just created.
Installing and Using Namespace Packages
Navigate to the outer snake-corp folder, where you can see the folders of the two utility packages. Create a virtual environment there, and install the packages: