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, andLITERALare no longer attached. UseENCODED_DEFAULT,ENCODED_LIST,ENCODED_TUPLE, andFIELDrespectively.
Documentation
- Design — the three-layer model, design decisions, and rejected alternatives
- Source Guide — file-by-file walkthrough and execution-flow traces
- Extension Guide — adding new enum base classes and custom coders
- API Reference — autodoc for all modules
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file vcti_enum-2.0.0.tar.gz.
File metadata
- Download URL: vcti_enum-2.0.0.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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ec64a725d4edce6e3e4230e0dc10e26017047eb9bc006f866662caddfe2af05d
|
|
| MD5 |
9a1e77987f58cc994e05a8eaa093f568
|
|
| BLAKE2b-256 |
55bfadfce524b666d7c1b20ecc73ba2ceefaffcc825909e83a6f05623d853250
|
Provenance
The following attestation bundles were made for vcti_enum-2.0.0.tar.gz:
Publisher:
release.yml on vcollab/vcti-python-enum
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
vcti_enum-2.0.0.tar.gz -
Subject digest:
ec64a725d4edce6e3e4230e0dc10e26017047eb9bc006f866662caddfe2af05d - Sigstore transparency entry: 2193227123
- Sigstore integration time:
-
Permalink:
vcollab/vcti-python-enum@1d01ba44071f348a1a5783aa49ecd8bb16d2e8aa -
Branch / Tag:
refs/tags/v2.0.0 - Owner: https://github.com/vcollab
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1d01ba44071f348a1a5783aa49ecd8bb16d2e8aa -
Trigger Event:
push
-
Statement type:
File details
Details for the file vcti_enum-2.0.0-py3-none-any.whl.
File metadata
- Download URL: vcti_enum-2.0.0-py3-none-any.whl
- Upload date:
- Size: 14.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0435ff04bd8b1f84f1401d855d0b5fce0e3c4b28692b1f8420520043d84b0ae8
|
|
| MD5 |
ca2e27a7eb6cc8a15d202e6e645e782e
|
|
| BLAKE2b-256 |
4f05558e242e3b09cd82bb0e9d477cd9678cb16f9b0946146a940bb88a443523
|
Provenance
The following attestation bundles were made for vcti_enum-2.0.0-py3-none-any.whl:
Publisher:
release.yml on vcollab/vcti-python-enum
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
vcti_enum-2.0.0-py3-none-any.whl -
Subject digest:
0435ff04bd8b1f84f1401d855d0b5fce0e3c4b28692b1f8420520043d84b0ae8 - Sigstore transparency entry: 2193227126
- Sigstore integration time:
-
Permalink:
vcollab/vcti-python-enum@1d01ba44071f348a1a5783aa49ecd8bb16d2e8aa -
Branch / Tag:
refs/tags/v2.0.0 - Owner: https://github.com/vcollab
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1d01ba44071f348a1a5783aa49ecd8bb16d2e8aa -
Trigger Event:
push
-
Statement type: