Metadata-Version: 2.4
Name: stateforge
Version: 0.1.0
Summary: Type-safe async state machines for Python
Project-URL: Homepage, https://github.com/desertaxle/stateforge
Project-URL: Source, https://github.com/desertaxle/stateforge
Author-email: Alexander Streed <ajstreed1@gmail.com>
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: async,fsm,state machine,typing
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# stateforge

[![PyPI version](https://img.shields.io/pypi/v/stateforge)](https://pypi.org/project/stateforge/)
[![Python versions](https://img.shields.io/pypi/pyversions/stateforge)](https://pypi.org/project/stateforge/)
[![CI](https://github.com/desertaxle/stateforge/actions/workflows/ci.yml/badge.svg)](https://github.com/desertaxle/stateforge/actions/workflows/ci.yml)

Type-safe async state machines for Python.

## Installation

```bash
pip install stateforge
```

## Quick example

```python
import asyncio
from enum import Enum, auto
from stateforge import StateMachine, transition

class Light(Enum):
    GREEN = auto()
    YELLOW = auto()
    RED = auto()

class Event(Enum):
    TIMER = auto()
    EMERGENCY = auto()

class TrafficLight(StateMachine[Light, Event]):
    initial_state = Light.GREEN

    @transition(from_=Light.GREEN, on=Event.TIMER, to=Light.YELLOW)
    async def green_to_yellow(self) -> None:
        pass

    @transition(from_=Light.YELLOW, on=Event.TIMER, to=Light.RED)
    async def yellow_to_red(self) -> None:
        pass

    @transition(from_=Light.RED, on=Event.TIMER, to=Light.GREEN)
    async def red_to_green(self) -> None:
        pass

    @transition(from_=[Light.GREEN, Light.YELLOW], on=Event.EMERGENCY, to=Light.RED)
    async def emergency_stop(self) -> None:
        pass

async def main() -> None:
    light = TrafficLight()
    await light.send(Event.TIMER)       # GREEN -> YELLOW
    await light.send(Event.EMERGENCY)   # YELLOW -> RED (emergency)
    print(light.state)                  # Light.RED

asyncio.run(main())
```

## Features

- **Type-safe** — mypy and pyright catch invalid transitions at type-check time
- **Async-native** — built on async/await from the ground up
- **Zero dependencies** — no runtime dependencies
- **Guards** — attach async guard functions to conditionally allow transitions
- **Typed context** — `StateMachine[S, E, C]` with fully-typed mutable context
- **Lifecycle hooks** — `on_enter_state`, `on_exit_state`, `on_transition`

## License

Apache 2.0 — see [LICENSE](https://github.com/desertaxle/stateforge/blob/main/LICENSE).
