Skip to main content

NamespacedEnums

Library for defining containers within Enum Python classes.

Installation

Run

$ pip install namespaced_enums

Using mypy? See mypy support to enable the bundled plugin.

Usage

Library provides two special descriptors for Enum classes:

  • EnumContainer
  • StrictEnumContainer

as well as an additional enum-subclass:

  • NamespacedEnum

EnumContainer

This allows for defining arbitrary containers within Enum classes, e.g.

from enum import Enum
from namespaced_enum import EnumContainer

class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3
    
    LIGHT_RED = 4
    LIGHT_GREEN = 5
    LIGHT_BLUE = 6
    
    DARK_RED = 7
    DARK_GREEN = 8
    DARK_BLUE = 9
    
    light_colors = EnumContainer([LIGHT_RED, LIGHT_GREEN, LIGHT_BLUE])
    dark_colors = EnumContainer([DARK_RED, DARK_GREEN, DARK_BLUE])
    
---

>>> print(Color.RED.value in Color.light_colors)  # False
>>> print(Color.DARK_BLUE.value in Color.dark_colors)  # True

# Caution!
# Values within containers are of enum value types
>>> print(Color.dark_colors)  # prints [7, 8, 9] and not [DARK_RED, DARK_GREEN, DARK_BLUE]!

StrictEnumContainer and NamespacedEnum

StrictEnumContainer accepts only dict containers. Using this descriptor within a class inheriting from NamespacedEnum will enforce that the provided dictionary contains all enum possible values:

from namespaced_enums import NamespacedEnum, StrictEnumContainer

class Food(NamespacedEnum):
    spam = "spam"
    eggs = "eggs"
    foo = "foo"

    reactions = StrictEnumContainer(
        {
            spam: "I like it",
            eggs: "I don't like it...",
            foo: "I like?",
        }
    )

---

>>> print(Food.reactions[Food.spam])  # "I like it"

# Caution!
# Unlike the `EnumContainer` this one converts dict keys to enums!
>>> print(list[Food.reactions.keys()])  # prints [<Food.spam: spam>, <Food.eggs: eggs>, <Food.foo: foo>]

Forgetting to provide all possible enum values within a strict container will raise a RuntimeError:

from namespaced_enums import NamespacedEnum, StrictEnumContainer

class Food(NamespacedEnum):
    spam = "spam"
    eggs = "eggs"
    foo = "foo"

    reactions = StrictEnumContainer(
        {
            spam: "I like it",
            eggs: "I don't like it...",
            # missing foo in the dict
        }
    )

---

>>> # Trying to start the program raises a `RuntimeError`:
# The following Food fields do not contain all possible enum values: ['reactions']

mypy support

The library ships with a mypy plugin (and a py.typed marker). Without it, mypy sees container attributes as enum members and rejects indexing them ("Enum" is not indexable).

Enabling the plugin

mypy does not load plugins automatically, so enabling support is two steps:

1. Install the optional dependency (requires mypy >= 1.0):

$ pip install namespaced_enums[mypy]

2. Register the plugin in your mypy configuration. Add the following to whichever config file your project uses:

mypy.ini (or the [mypy] section of setup.cfg):

[mypy]
plugins = namespaced_enums.plugin

pyproject.toml:

[tool.mypy]
plugins = ["namespaced_enums.plugin"]

That's it — no import changes are needed in your own code.

What you get

With the plugin enabled, containers are typed correctly instead of being seen as enum members:

class DogBreed(NamespacedEnum):
    BULLDOG = 'bulldog'
    PUG = 'pug'
    SHIBA = 'shiba'

    size = StrictEnumContainer({BULLDOG: 40, PUG: 30, SHIBA: 35})

reveal_type(DogBreed.size)          # dict[DogBreed, int]
n: int = DogBreed.size[DogBreed.PUG]  # ok, no "not indexable" error

More importantly, the plugin turns the runtime RuntimeError for an incomplete StrictEnumContainer into a static error (error code namespaced-enum), so a forgotten member is caught by mypy instead of at import time:

class DogBreed(NamespacedEnum):
    BULLDOG = 'bulldog'
    PUG = 'pug'
    SHIBA = 'shiba'

    size = StrictEnumContainer({BULLDOG: 40, PUG: 30})
    # error: StrictEnumContainer "size" is missing keys for enum members: SHIBA  [namespaced-enum]

Rationale

It's a common practice to use enums in a project as a way to denote "characteristics" of certain objects.

Consider the following example:

from enum import Enum

class DogBreed(Enum):
    BULLDOG = 'bulldog'
    PUG = 'pug'
    SHIBA = 'shiba'

    
class Dog:
    def __init__(self, breed: DogBreed) -> None:
        self.breed = breed
        
    @property
    def size(self) -> int:
        """Returns size of the dog (in centimeters)."""
        if self.breed == DogBreed.BULLDOG:
            return 40
        elif self.breed == DogBreed.PUG:
            return 30
        elif self.breed == DogBreed.SHIBA:
            return 35
        else:  # pragma: no cover
            raise ValueError(f"Unknown dog breed: {self.breed}")

The Dog.size property implementation poses a threat when it comes to further additions to the DogBreed enum:

  • programmers would have to remember to update it every time they add a new breed support. What if there are more such properties like color, weight or tail length?
  • the if-elif-else chain is hard to test as the last else clause would require injecting some arbitrary value into the DogBreed enum during runtime in order to ensure proper coverage. In my experience, most developers would prefer to add # pragma: no cover instead.
  • the size characteristic should arguably be not part of the Dog class as it is more specific to the DogBreed enum - the Dog class should be aware of how to retrieve that data.

That's why I decided to create a NamespacedEnum class that allows for writing self-contained enums, that can help in maintaining the integrity of the written code. The example above could be rewritten in a following manner:

from namespaced_enums import NamespacedEnum, StrictEnumContainer


class DogBreed(NamespacedEnum):
    BULLDOG = 'bulldog'
    PUG = 'pug'
    SHIBA = 'shiba'

    size = StrictEnumContainer({
        BULLDOG: 40,
        PUG: 30,
        SHIBA: 35,
    })

    
class Dog:
    def __init__(self, breed: DogBreed) -> None:
        self.breed = breed

    @property
    def size(self) -> int:
        """Returns size of the dog (in centimeters)."""
        return DogBreed.size[self.breed]

Metadata

Release files for namespaced-enums 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for namespaced-enums 0.1.0
File Size Uploaded Provenance
namespaced_enums-0.1.0.tar.gz 12.8 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for namespaced-enums 0.1.0
File Interpreter ABI Platform
namespaced_enums-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 22.1 kB

Release files / namespaced_enums-0.1.0.tar.gz

Download URL namespaced_enums-0.1.0.tar.gz
Size 12.8 kB
Tags Source
SHA-256 checksum
How to use checksums
9a7dde383662d5f50ddbabff50b0e96a002b6a4fe9a13390cebbc310006fa583
BLAKE2b-256 checksum
How to use checksums
b61549f40c784c8d39b5122ce5a82fa901c2d5e3feb1d3e3a319c3ea7d22a523
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 21, 2026.

Transparency log

Release files / namespaced_enums-0.1.0-py3-none-any.whl

Download URL namespaced_enums-0.1.0-py3-none-any.whl
Size 9.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2c3ab6ce924f83dfc12f1564dd4f59f065dabe04fad43268914854afef7dad86
BLAKE2b-256 checksum
How to use checksums
cd47e6f2b7b9db37e14fc758e9f47e6199b46771cc54fd98496b08c32620d55b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 21, 2026.

Transparency log

Security for namespaced-enums 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for namespaced_enums-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 21, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page