Skip to content

Make enums iterable#42

Open
JoshKarpel wants to merge 2 commits intomainfrom
make-enums-iterable
Open

Make enums iterable#42
JoshKarpel wants to merge 2 commits intomainfrom
make-enums-iterable

Conversation

@JoshKarpel
Copy link
Copy Markdown
Owner

No description provided.

@JoshKarpel JoshKarpel self-assigned this Mar 18, 2026
@JoshKarpel JoshKarpel marked this pull request as ready for review March 18, 2026 02:48
Copilot AI review requested due to automatic review settings March 18, 2026 02:48
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR makes the public waxy enum symbols iterable from Python (e.g., list(waxy.BoxSizing)), by adding a Rust-side variants() classmethod for each enum and wrapping the exported enums in Python so iter(enum) yields the variants in definition order.

Changes:

  • Added variants() classmethods to each PyO3 enum in src/enums.rs.
  • Wrapped exported enums in python/waxy/__init__.py to provide class-level iteration, and updated stubs/tests accordingly.
  • Documented the feature in the changelog and design notes.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/enums.rs Adds variants() classmethod returning definition-ordered variant lists for each enum.
python/waxy/__init__.py Re-exports enums via a Python proxy that implements __iter__ by calling the Rust variants().
python/waxy/__init__.pyi Changes enum stubs to enum.IntEnum member syntax (int-valued) for type-checker iteration support.
tests/test_enums.py Updates enum variant expectations and adds an iteration test.
docs/changelog.md Notes the new iterability and variants() method in Unreleased.
CLAUDE.md Adds rationale/design notes for the enum iterability approach.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +458 to 468
class Display(enum.IntEnum):
"""How the node should be displayed."""

Block: Display
Block = 0
"""Block layout."""
Flex: Display
Flex = 1
"""Flexbox layout."""
Grid: Display
Grid = 2
"""CSS Grid layout."""
Nil: Display
Nil = 3
"""No display (maps to CSS `display: none`)."""
Comment on lines +72 to +81
@pytest.mark.parametrize(
("enum_class", "variants"),
ENUM_VARIANTS,
ids=[cls.__name__ for cls, _ in ENUM_VARIANTS],
)
def test_enum_iterable(enum_class: _EnumLike, variants: list[str]) -> None:
result = list(enum_class)
assert len(result) == len(variants)
for variant, name in zip(result, variants, strict=True):
assert variant == getattr(enum_class, name)
Comment on lines 40 to +68
)
from waxy._waxy import (
AlignContent as _AlignContent,
)
from waxy._waxy import (
AlignItems as _AlignItems,
)
from waxy._waxy import (
BoxSizing as _BoxSizing,
)
from waxy._waxy import (
Display as _Display,
)
from waxy._waxy import (
FlexDirection as _FlexDirection,
)
from waxy._waxy import (
FlexWrap as _FlexWrap,
)
from waxy._waxy import (
GridAutoFlow as _GridAutoFlow,
)
from waxy._waxy import (
Overflow as _Overflow,
)
from waxy._waxy import (
Position as _Position,
)
from waxy._waxy import (
Comment on lines +112 to +116
AlignContent = _EnumProxy(_AlignContent)
AlignItems = _EnumProxy(_AlignItems)
BoxSizing = _EnumProxy(_BoxSizing)
Display = _EnumProxy(_Display)
FlexDirection = _EnumProxy(_FlexDirection)
- **Value types** (`Length`, `Percent`, `Auto`, `MinContent`, `MaxContent`, `Definite`, `Fraction`, `FitContent`, `Minmax`, `GridLine`, `GridSpan`) are standalone frozen pyclasses, not enum variants. They support `match`/`case` pattern matching via `__match_args__`. Module-level constants `AUTO`, `MIN_CONTENT`, `MAX_CONTENT` are provided for the zero-argument types.
- **Exception hierarchy**: `WaxyException(Exception)` is the root. `TaffyException(WaxyException)` covers taffy errors. `InvalidNodeId` is `TaffyException + KeyError` (raised when accessing a removed node). Validation exceptions are `WaxyException + ValueError` via multi-inheritance (achieved by setting `__bases__` in `register()` in `src/errors.rs`): `InvalidPercent` (Percent outside [0.0, 1.0]), `InvalidLength` (NaN), `InvalidGridLine` (index 0), `InvalidGridSpan` (count 0).
- **`Display.Nil`** maps to taffy's `Display::None`. We use `#[pyo3(name = "Nil")]` because `None` is a Python keyword.
- **Enum iterability** — PyO3 enum pyclasses don't support Python subclassing (`subclass`) and their `FromPyObject` impl uses downcast (not integer extraction), so Python `IntEnum` wrappers can't be passed back to Rust. Instead, each enum is wrapped at module level in `_EnumProxy` (defined in `python/waxy/__init__.py`). The proxy's `__iter__` calls the Rust `variants()` classmethod (added to each enum in `src/enums.rs`) and its `__instancecheck__` delegates to the underlying Rust class. Variant attribute access (e.g., `Display.Flex`) goes through `_EnumProxy.__getattr__` and returns the actual Rust singleton, so all Rust interop is unaffected. The `.pyi` stub declares the enums as `enum.IntEnum` subclasses (with `= value` member syntax) so that type checkers understand iteration, isinstance, and equality.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants