Skip to main content

Crate pyenum

Crate pyenum 

Source
Expand description

§pyenum

Expose Rust enum types to Python as genuine enum.Enum subclasses via PyO3 — #[derive(PyEnum)], functional-API class construction, a per-interpreter cache, and IntoPyObject / FromPyObject plumbing all generated by the derive.

§Quickstart

use pyenum::{PyEnum, PyModuleExt};
use pyo3::prelude::*;

#[derive(Clone, Copy, PyEnum)]
pub enum Color {
    Red,
    Green,
    Blue,
}

#[pyfunction]
fn preferred() -> Color { Color::Green }

#[pymodule]
fn demo(m: &Bound<'_, PyModule>) -> PyResult<()> {
    m.add_enum::<Color>()?;
    m.add_function(wrap_pyfunction!(preferred, m)?)
}

From Python:

import demo, enum
assert issubclass(demo.Color, enum.Enum)
assert demo.preferred() is demo.Color.Green

§Choosing a Python base

Select the Python enum base via #[pyenum(base = "…")]. The derive accepts five values matching enum.* attribute names — Enum (default), IntEnum, StrEnum, Flag, IntFlag.

use pyenum::PyEnum;

#[derive(Clone, Copy, PyEnum)]
#[pyenum(base = "IntEnum")]
pub enum HttpStatus {
    Ok = 200,
    NotFound = 404,
}

#[derive(Clone, Copy, PyEnum)]
#[pyenum(base = "StrEnum")]
pub enum Greeting {
    // Default — variant name lowercased via Python's `auto()`.
    Hello,
    // Opt-in to a specific string, preserving case.
    #[pyenum(value = "Bye!")]
    Bye,
}

#[derive(Clone, Copy, PyEnum)]
#[pyenum(base = "IntFlag")]
pub enum Perm {
    Read = 1,
    Write = 2,
    Execute = 4,
}

§Conversion at the PyO3 boundary

The derive emits IntoPyObject and FromPyObject impls, so the Rust enum type appears directly in PyO3 signatures:

#[pyfunction]
fn roundtrip(c: Color) -> Color { c }

Passing demo.roundtrip(demo.Color.Red) from Python returns demo.Color.Red as a real enum member (is-identical, not a fresh clone).

§Performance

  • The Python class is built once per interpreter via pyo3::sync::PyOnceLock.
  • Each variant’s Py<PyAny> member object is cached alongside the class; PyEnumTrait::to_py_member indexes the cache by Rust-variant ordinal and PyEnumTrait::from_py_member uses pointer equality (Bound::is) — no Python getattr, no allocation on the steady-state path.
  • Measured ~64 ns per conversion; SC-004 target is < 1 µs.

§What the derive rejects at compile time

The proc-macro surfaces the following as compile_error! diagnostics spanned at the offending variant or attribute — never at run time:

  • Non-unit variants (tuple or struct fields).
  • Generic or lifetime-parameterised enums.
  • Empty enums (no variants).
  • Variant names colliding with Python keywords, dunders, or enum.Enum-reserved members (name, value, _missing_, …).
  • Base/value shape mismatches (e.g. a string #[pyenum(value = ...)] on an IntEnum).
  • Duplicate resolved values — including the auto() vs. explicit discriminant case on every integer-shaped base, and duplicate auto-lowercased names on StrEnum.

§Scope

  • PyO3: pinned to 0.28. Cargo’s pyo3-ffi links = "python" rule forbids multiple PyO3 lines in the same dependency graph, so no multi-version feature matrix is exposed.
  • CPython: 3.10+. enum.StrEnum requires 3.11+; using #[pyenum(base = "StrEnum")] on a 3.10 interpreter raises RuntimeError at first class construction.
  • Out of scope for v1: projecting Rust impl methods onto the Python class, module-less standalone export, and free-threaded (--disable-gil) Python guarantees.

Structs§

PyEnumSpec
Static metadata emitted by #[derive(PyEnum)] for each derived enum.

Enums§

PyEnumBase
Python enum base type selector.
VariantLiteral
A single variant’s declared value, ready to be materialised into a Python-side (name, value) tuple for the functional enum.* constructor.

Traits§

PyEnumTrait
Bridge between a #[derive(PyEnum)] Rust type and its cached Python class.
PyModuleExt
Extension method for Bound<'_, PyModule> — the idiomatic way to register a derived enum inside a #[pymodule] body.

Functions§

add_enum
Register the Python class for T onto module under T::SPEC.name.

Derive Macros§

PyEnum
Derive a pyenum::PyEnum implementation for a unit-variant Rust enum.