Skip to main content

Value-generated string enums with automatic naming conventions for Python

Project description

vcti-enum

Value-generated string enums and a serialization bridge that keeps your internal enums decoupled from the strings on the wire.

Overview

vcti-enum does two things. First, it provides StrEnum base classes whose member values are auto-generated from a naming convention — declare the convention once (lowercase, camelCase, PascalCase, …) and every value is derived from the member name, so nothing drifts when you rename or add members.

Second, it separates the enum your program uses from the strings the outside world sees. An EnumCoder bridges the two directions, and the Pydantic helpers wire that bridge into a model field: internally the enum can be an IntEnum, a StrEnum, or anything else, while the JSON, config files, and API schema speak stable strings. Your internal representation can change — renumber, rename, switch base class — and the wire contract never moves.

The three pieces build on each other; use only the layer you need:

Value-generated enums
        │
        ▼
     EnumCoder          ← the serialization bridge; works with any Enum
        │
        ▼
 Pydantic integration   ← optional

The package has zero required dependencies. Pydantic is optional and only needed for the FIELD attribute and the @enum_for_pydantic attachment to take effect.

Contents


Installation

pip install vcti-enum

In requirements.txt

vcti-enum>=2.0.0

In pyproject.toml dependencies

dependencies = [
    "vcti-enum>=2.0.0",
]

Choosing an entry point

The package has three entry points that build on each other. Pick by what you need:

You want to… Use Needs pydantic
Give an enum string values derived from the member names by a convention a base class — EnumValueLowerCase and friends no
Encode/decode any enum to and from custom strings (serialize, parse) EnumCoder no
A Pydantic field that holds the enum member but reads/writes strings on the wire @enum_for_pydantic + MyEnum.FIELD yes

The three-layer idea underneath all of this:

flowchart LR
    subgraph prog["Your program — internal"]
        member["Theme.DARK<br/>enum member · has .value"]
    end
    subgraph wire["JSON · config · API — external"]
        string["'dark'<br/>stable encoded string"]
    end
    member -->|"CODER.encode"| string
    string -->|"CODER.decode"| member

Your code works with the enum member; everyone else sees the string. The encoded string is derived from the member name, not its value.


Quick Start

Value-generated enums

from vcti.enum import EnumValueLowerCase, auto_enum_value

class FileFormat(EnumValueLowerCase):
    JSON = auto_enum_value()    # value: "json"
    CSV = auto_enum_value()     # value: "csv"
    PARQUET = auto_enum_value() # value: "parquet"

FileFormat.JSON.value       # "json"
FileFormat.JSON == "json"   # True (StrEnum members are str)

The value is derived from the member name by the base class's convention, so renaming JSON to JSON_LINES updates its value to "json_lines" automatically.

EnumCoder for serialization

EnumCoder maps an enum to and from strings using any value_generator you supply — independent of the enum's own .value. It works with any Python Enum, not only the value-generated base classes in this package:

from enum import Enum
from vcti.enum import EnumCoder

class Color(Enum):
    RED = 1
    GREEN = 2

coder = EnumCoder(Color, value_generator=lambda e: e.name.lower(), default=Color.RED)

coder.encode(Color.RED)    # "red"
coder.decode("green")      # Color.GREEN
coder.default              # "red"
coder.list                 # ["red", "green"]

Both directions are precomputed at construction, so encode/decode are O(1) lookups and two members mapping to the same string is rejected immediately.

Pydantic integration

@enum_for_pydantic attaches a ready-made, member-bound field annotation, MyEnum.FIELD. Used in a model it validates against the encoded strings, decodes them to the enum member on the way in, and encodes back on the way out — so the model attribute is the real member while the JSON and schema stay strings.

from pydantic import BaseModel

from vcti.enum import EnumValueLowerCase, auto_enum_value, enum_for_pydantic

@enum_for_pydantic(default="JSON")
class FileFormat(EnumValueLowerCase):
    JSON = auto_enum_value()
    CSV = auto_enum_value()

class ExportConfig(BaseModel):
    format: FileFormat.FIELD          # type + default + description bundled

ExportConfig().format                          # FileFormat.JSON (the member)
ExportConfig(format="csv").format              # FileFormat.CSV  (decoded from "csv")
ExportConfig(format="csv").model_dump_json()   # '{"format":"csv"}'

If no default is given, the field is required — building the model without it raises ValidationError.

setup_enum_for_pydantic() is the function form of the decorator; use it when the enum is defined elsewhere — a stdlib IntEnum, a third-party class — and you cannot decorate it in place.

The decoupling pays off most when the internal enum is not a string. Here the server stores a theme as an integer while clients speak "light" / "dark":

from enum import IntEnum
from pydantic import BaseModel

from vcti.enum import setup_enum_for_pydantic

class Theme(IntEnum):
    LIGHT = 1
    DARK = 2

setup_enum_for_pydantic(
    Theme,
    value_generator=lambda e: e.name.lower(),  # "LIGHT" -> "light"
    default_member="LIGHT",
)

class UiSettings(BaseModel):
    theme: Theme.FIELD

UiSettings().theme                          # Theme.LIGHT — an IntEnum member, .value == 1
UiSettings(theme="dark").theme              # Theme.DARK  — decoded from the wire string
UiSettings(theme="dark").model_dump_json()  # '{"theme":"dark"}'

Renumber LIGHT = 100 tomorrow and nothing on the API side notices — the encoded strings come from the member name, not its value.


Enum base classes

Class Convention FIRST_VALUE becomes
EnumValueSameAsName Unchanged FIRST_VALUE
EnumValueLowerCase lower_case first_value
EnumValueCamelCase camelCase firstValue
EnumValuePascalCase PascalCase FirstValue
EnumValueCapitalizedPhrase Title Case First Value
EnumValueSpaceSeparatedLower space lower first value

All extend StrEnum, so members are string instances. Need another convention? create_enum_class() builds a base from any str -> str function — see the Extension Guide.


Public API

Symbol Import Purpose
auto_enum_value() vcti.enum Triggers automatic value generation
create_enum_class() vcti.enum Factory to create custom enum base classes
EnumValueSameAsName vcti.enum Base class: value = member name
EnumValueLowerCase vcti.enum Base class: value = lowercase
EnumValueCamelCase vcti.enum Base class: value = camelCase
EnumValuePascalCase vcti.enum Base class: value = PascalCase
EnumValueCapitalizedPhrase vcti.enum Base class: value = Title Case
EnumValueSpaceSeparatedLower vcti.enum Base class: value = space separated
EnumCoder vcti.enum Flexible enum serialization/deserialization
setup_enum_for_pydantic() vcti.enum Attach encoder attributes to an enum class
enum_for_pydantic() vcti.enum Decorator form of the setup function
get_enum_field_description() vcti.enum Build a field description string on its own

Attributes attached by setup_enum_for_pydantic / @enum_for_pydantic

Attribute Type What it contains
CODER EnumCoder The encoder instance for this enum
ENCODED_DEFAULT str | None Encoded string of the default member; None if the field is required
ENCODED_LIST list[str] All encoded strings, as a list
ENCODED_TUPLE tuple[str, ...] All encoded strings, as a tuple
FIELD member-bound Annotated[...] Model attribute is the enum member; wire is the encoded string. Required when no default

Removed in 2.0: the 1.x aliases DEFAULT_VALUE, LIST, TUPLE, and LITERAL are no longer attached. Use ENCODED_DEFAULT, ENCODED_LIST, ENCODED_TUPLE, and FIELD respectively.


Documentation

If you want to… Read
Get started using the package Quick Start above
Understand the three-layer model and design decisions docs/design.md
Navigate and understand the source docs/source-guide.md
Add a new enum base class or custom coder docs/extending.md
Look up a specific function or type docs/api.md

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vcti_enum-2.0.1.tar.gz (21.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vcti_enum-2.0.1-py3-none-any.whl (14.8 kB view details)

Uploaded Python 3

File details

Details for the file vcti_enum-2.0.1.tar.gz.

File metadata

  • Download URL: vcti_enum-2.0.1.tar.gz
  • Upload date:
  • Size: 21.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for vcti_enum-2.0.1.tar.gz
Algorithm Hash digest
SHA256 f899f41fa75217014f420bc545ca38b411a840acd2bdfb8012a5b384a49e6e4a
MD5 acec47d96db22617d55d5b5652a6964e
BLAKE2b-256 3303517b19e67f2e031c2d86f2bb2b3c25fe8bdd8a6387675ed150ba559723cf

See more details on using hashes here.

Provenance

The following attestation bundles were made for vcti_enum-2.0.1.tar.gz:

Publisher: release.yml on vcollab/vcti-python-enum

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file vcti_enum-2.0.1-py3-none-any.whl.

File metadata

  • Download URL: vcti_enum-2.0.1-py3-none-any.whl
  • Upload date:
  • Size: 14.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for vcti_enum-2.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 7c559fca7095b08cb0b40205847710bd0981c4990c44d518ee662b98a680074b
MD5 454495f8e699393c3d55d76932ac8678
BLAKE2b-256 ca604d78d529fb5670c7ecddce00b3df51b048bde97227fcf3bb43f81d0e90b4

See more details on using hashes here.

Provenance

The following attestation bundles were made for vcti_enum-2.0.1-py3-none-any.whl:

Publisher: release.yml on vcollab/vcti-python-enum

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page