Metadata-Version: 2.4
Name: vcti-enum
Version: 1.1.0
Summary: Value-generated string enums with automatic naming conventions for Python
Author: Visual Collaboration Technologies Inc.
Requires-Python: <3.15,>=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Requires-Dist: pytest-cov; extra == "test"
Requires-Dist: pydantic; extra == "test"
Provides-Extra: lint
Requires-Dist: ruff; extra == "lint"
Provides-Extra: typecheck
Requires-Dist: mypy; extra == "typecheck"
Dynamic: license-file

# Enum Framework

## Overview

`vcti-enum` provides string-valued `StrEnum` base classes whose member
values are auto-generated from a naming convention (lowercase,
camelCase, PascalCase, etc.), plus an `EnumCoder` for flexible
serialization to and from custom string representations.

The Pydantic helpers (`setup_enum_for_pydantic`, `@enum_for_pydantic`)
exist to **decouple the internal enum representation from the external
API surface**. Internally the enum can be an `IntEnum`, a `StrEnum`,
or anything else; externally the model speaks stable strings that
appear in JSON, schemas, and config files. Changing the internal
representation never changes the wire contract.

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.

---

## Installation

```bash
pip install vcti-enum
```

### In `requirements.txt`

```
vcti-enum>=1.1.0
```

### In `pyproject.toml` dependencies

```toml
dependencies = [
    "vcti-enum>=1.1.0",
]
```

---

## Quick Start

### Value-generated enums

```python
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)
```

### EnumCoder for serialization

```python
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"]
```

### Pydantic integration

The mental model is three layers — *internal enum*, *encoded
external string*, *bridge encoder*. Pydantic models only ever see
the encoded strings, so the internal enum can change freely.

```python
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          # bundled type + default + description

ExportConfig().format                 # "json"
ExportConfig(format="csv").format     # "csv"
```

`FileFormat.FIELD` is an `Annotated[Literal["json", "csv"], Field(...)]`
built by the decorator. It bundles the field type, the default, and a
generated description. The equivalent hand-written form is:

```python
class ExportConfig(BaseModel):
    format: Literal["json", "csv"] = Field(
        default="json",
        description="Supported values: (json/csv). Default: json",
    )
```

### Decoupling internal representation: IntEnum example

```python
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              # "light"
UiSettings(theme="dark").theme  # "dark"
Theme.CODER.decode("light")     # Theme.LIGHT (the internal IntEnum member)
```

The model field is a `Literal["light", "dark"]` — *not* an
`IntEnum`. You could renumber `LIGHT = 100` tomorrow and nothing on
the API side notices.

---

## 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 classes extend `StrEnum` -- members are string instances.

---

## 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 description string (kept for back-compat) |

### 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` | Encoded string of the default member |
| `ENCODED_LIST` | `list[str]` | All encoded strings, as a list |
| `ENCODED_TUPLE` | `tuple[str, ...]` | All encoded strings, as a tuple |
| `FIELD` | `Annotated[Literal[...], Field(...)]` | Bundled type + default + description |

### Deprecated aliases (kept for back-compat)

| Deprecated | Use instead |
|---|---|
| `DEFAULT_VALUE` | `ENCODED_DEFAULT` |
| `LIST` | `ENCODED_LIST` |
| `TUPLE` | `ENCODED_TUPLE` |
| `LITERAL` | `FIELD` (the `Literal[...]` is wrapped inside) |
| `get_enum_field_description(E)` | description is auto-baked into `E.FIELD` |

---

## Documentation

- [Design](docs/design.md) -- Concepts, factory pattern, encoder layering, architecture decisions
- [Source Guide](docs/source-guide.md) -- File descriptions and execution flow traces
- [Extension Guide](docs/extending.md) -- How to add new enum base classes
- [API Reference](docs/api.md) -- Autodoc for all modules
