stateforward.hsm


Namestateforward.hsm JSON
Version 0.1.1 PyPI version JSON
download
home_pageNone
SummaryHierarchical State Machine implementation for Python with asyncio support
upload_time2025-11-03 20:47:42
maintainerNone
docs_urlNone
authorStateforward Team
requires_python>=3.13
licenseMIT
keywords async asyncio finite-state-machine hierarchical hsm state-machine
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # stateforward.hsm

A high-performance, hierarchical state machine (HSM) implementation for Python with asyncio support. This library provides a declarative, async-first approach to building complex state machines with efficient O(1) runtime performance.

## Features

* **Hierarchical States**: Support for nested state machines with proper scoping
* **Async/Await**: Full asyncio support for actions, guards, effects, and activities
* **Precomputed Lookups**: O(1) performance through precomputed transition and element maps
* **Event-Driven**: Deterministic event processing with guards and effects
* **Timers**: Built-in support for one-shot and recurring timers
* **Activities**: Long-running async operations that auto-cancel on state exit
* **Error Handling**: Automatic error event dispatching for robust error recovery
* **Type Safety**: Strong typing with comprehensive type hints

## Installation

```bash
pip install stateforward.hsm
```

## Quick Start

```python
import asyncio
import hsm

class Counter(hsm.Instance):
    def __init__(self):
        super().__init__()
        self.value = 0

    @staticmethod
    async def increment(ctx, self, event):
        self.value += 1
        print(f"Count: {self.value}")

    model = hsm.define('Counter',
        hsm.initial(hsm.target('counting')),
        hsm.state('counting',
            hsm.transition(
                hsm.on('inc'),
                hsm.target('.'),  # Self-transition
                hsm.effect(increment)
            )
        )
    )

async def main():
    instance = Counter()
    ctx = hsm.Context()

    # Start the state machine
    sm = await hsm.start(ctx, instance, Counter.model)
    print(f"Initial state: {sm.state()}")

    # Dispatch events
    await sm.dispatch(hsm.Event('inc'))
    await sm.dispatch(hsm.Event('inc'))

    print(f"Final count: {instance.value}")

    # Clean shutdown
    await hsm.stop(sm)

if __name__ == "__main__":
    asyncio.run(main())
```

## Core Concepts

### Declarative Definition

State machines are defined using a declarative API that precomputes all lookups for optimal runtime performance:

```python
model = hsm.define(
    "MyMachine",
    hsm.initial(hsm.target("ready")),
    hsm.state("ready",
        hsm.transition(hsm.on("start"), hsm.target("running"))
    ),
    hsm.state("running",
        hsm.transition(hsm.on("stop"), hsm.target("ready"))
    )
)
```

### States and Hierarchy

* **`hsm.state(name, ...children)`**: Regular states that can contain nested states
* **`hsm.initial(hsm.target(path))`**: Defines the default entry point
* **`hsm.final(name)`**: Terminal states for completion handling
* **`hsm.choice(name, ...transitions)`**: Conditional branching logic

### Transitions

Transitions define how the machine responds to events:

```python
hsm.transition(
    hsm.on("event_name"),           # Event trigger
    hsm.source("current_state"),    # Optional explicit source
    hsm.target("next_state"),       # Destination state
    hsm.guard(async_guard_func),    # Optional condition
    hsm.effect(async_effect_func)   # Optional side effect
)
```

### Actions and Behaviors

All behavioral functions are async and receive `(ctx, instance, event)`:

* **`hsm.entry(action)`**: Executed when entering a state
* **`hsm.exit(action)`**: Executed when exiting a state
* **`hsm.effect(action)`**: Executed during transitions
* **`hsm.guard(guard_func)`**: Returns boolean to allow/block transitions
* **`hsm.activity(activity_func)`**: Long-running operations that auto-cancel

### Timers

Built-in timer support for time-based transitions:

```python
from datetime import timedelta

@staticmethod
async def one_second(ctx, self, event):
    return timedelta(seconds=1)

hsm.transition(
    hsm.after(one_second),  # One-shot timer
    hsm.target("next_state")
)

hsm.transition(
    hsm.every(one_second),  # Recurring timer
    hsm.target(".")
)
```

### Error Handling

Exceptions in actions automatically dispatch `hsm_error` events:

```python
class RobustMachine(hsm.Instance):
    @staticmethod
    async def risky_action(ctx, self, event):
        if random.random() < 0.1:
            raise ValueError("Something went wrong!")

    @staticmethod
    async def handle_error(ctx, self, event):
        print(f"Error occurred: {event.data}")
        # Recovery logic here

    model = hsm.define('RobustMachine',
        hsm.state('working',
            hsm.effect(risky_action),
            hsm.transition(hsm.on('hsm_error'), hsm.target('error_state'))
        ),
        hsm.state('error_state',
            hsm.entry(handle_error)
        )
    )
```

## Advanced Usage

### Class-Based Pattern

The recommended pattern is to define your state machine as a class:

```python
class TrafficLight(hsm.Instance):
    def __init__(self):
        super().__init__()
        self.cycles = 0

    @staticmethod
    async def change_to_green(ctx, self, event):
        print("🟢 Green light")
        self.cycles += 1

    model = hsm.define('TrafficLight',
        hsm.initial(hsm.target('red')),
        hsm.state('red',
            hsm.transition(hsm.on('timer'), hsm.target('green'))
        ),
        hsm.state('green',
            hsm.entry(change_to_green),
            hsm.transition(hsm.on('timer'), hsm.target('yellow'))
        ),
        hsm.state('yellow',
            hsm.transition(hsm.on('timer'), hsm.target('red'))
        )
    )
```

### Path Resolution

State paths are resolved relative to the transition's source state:

* **Child**: `'child_state'`
* **Parent**: `'..'`
* **Sibling**: `'../sibling_state'`
* **Absolute**: `'/MachineName/state'`
* **Self**: `'.'`

### Runtime Lifecycle

```python
# Create instance and context
instance = MyMachine()
ctx = hsm.Context()

# Start the machine
sm = await hsm.start(ctx, instance, MyMachine.model)

# Check current state
current_state = sm.state()

# Dispatch events
await sm.dispatch(hsm.Event('my_event', data={'key': 'value'}))

# Stop the machine
await hsm.stop(sm)
```

## API Reference

### Core Functions

* **`hsm.define(name, ...elements)`**: Create a state machine model
* **`hsm.start(ctx, instance, model)`**: Start a state machine instance
* **`hsm.stop(instance)`**: Stop and cleanup a state machine

### State Elements

* **`hsm.state(name, ...children)`**: Define a state
* **`hsm.initial(target, ...effects)`**: Define initial state
* **`hsm.final(name)`**: Define final state
* **`hsm.choice(name, ...transitions)`**: Define choice pseudostate

### Transitions

* **`hsm.transition(...conditions)`**: Define a transition
* **`hsm.on(event_name)`**: Event condition
* **`hsm.after(duration_func)`**: Timer condition (one-shot)
* **`hsm.every(duration_func)`**: Timer condition (recurring)
* **`hsm.target(path)`**: Target state
* **`hsm.guard(func)`**: Guard condition
* **`hsm.effect(func)`**: Transition effect

### Actions

* **`hsm.entry(func)`**: Entry action
* **`hsm.exit(func)`**: Exit action
* **`hsm.activity(func)`**: Long-running activity

### Runtime

* **`hsm.Instance`**: Base class for state machine instances
* **`hsm.Context`**: Execution context with cancellation support
* **`hsm.Event(name, data=None)`**: Event object

## Performance

This implementation uses precomputed lookup tables for O(1) transition resolution, making it suitable for high-performance applications. All behavioral functions are async-first, enabling efficient concurrent execution.

## License

MIT License - see [LICENSE](LICENSE) file for details.

## Contributing

Contributions are welcome! Please see the main [HSM repository](https://github.com/stateforward/hsm) for contribution guidelines.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "stateforward.hsm",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.13",
    "maintainer_email": null,
    "keywords": "async, asyncio, finite-state-machine, hierarchical, hsm, state-machine",
    "author": "Stateforward Team",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/d1/0b/68ac6cfcda70304d82759f5950faa71105ac9e595a6a54a2cf54b2aa88db/stateforward_hsm-0.1.1.tar.gz",
    "platform": null,
    "description": "# stateforward.hsm\n\nA high-performance, hierarchical state machine (HSM) implementation for Python with asyncio support. This library provides a declarative, async-first approach to building complex state machines with efficient O(1) runtime performance.\n\n## Features\n\n* **Hierarchical States**: Support for nested state machines with proper scoping\n* **Async/Await**: Full asyncio support for actions, guards, effects, and activities\n* **Precomputed Lookups**: O(1) performance through precomputed transition and element maps\n* **Event-Driven**: Deterministic event processing with guards and effects\n* **Timers**: Built-in support for one-shot and recurring timers\n* **Activities**: Long-running async operations that auto-cancel on state exit\n* **Error Handling**: Automatic error event dispatching for robust error recovery\n* **Type Safety**: Strong typing with comprehensive type hints\n\n## Installation\n\n```bash\npip install stateforward.hsm\n```\n\n## Quick Start\n\n```python\nimport asyncio\nimport hsm\n\nclass Counter(hsm.Instance):\n    def __init__(self):\n        super().__init__()\n        self.value = 0\n\n    @staticmethod\n    async def increment(ctx, self, event):\n        self.value += 1\n        print(f\"Count: {self.value}\")\n\n    model = hsm.define('Counter',\n        hsm.initial(hsm.target('counting')),\n        hsm.state('counting',\n            hsm.transition(\n                hsm.on('inc'),\n                hsm.target('.'),  # Self-transition\n                hsm.effect(increment)\n            )\n        )\n    )\n\nasync def main():\n    instance = Counter()\n    ctx = hsm.Context()\n\n    # Start the state machine\n    sm = await hsm.start(ctx, instance, Counter.model)\n    print(f\"Initial state: {sm.state()}\")\n\n    # Dispatch events\n    await sm.dispatch(hsm.Event('inc'))\n    await sm.dispatch(hsm.Event('inc'))\n\n    print(f\"Final count: {instance.value}\")\n\n    # Clean shutdown\n    await hsm.stop(sm)\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\n## Core Concepts\n\n### Declarative Definition\n\nState machines are defined using a declarative API that precomputes all lookups for optimal runtime performance:\n\n```python\nmodel = hsm.define(\n    \"MyMachine\",\n    hsm.initial(hsm.target(\"ready\")),\n    hsm.state(\"ready\",\n        hsm.transition(hsm.on(\"start\"), hsm.target(\"running\"))\n    ),\n    hsm.state(\"running\",\n        hsm.transition(hsm.on(\"stop\"), hsm.target(\"ready\"))\n    )\n)\n```\n\n### States and Hierarchy\n\n* **`hsm.state(name, ...children)`**: Regular states that can contain nested states\n* **`hsm.initial(hsm.target(path))`**: Defines the default entry point\n* **`hsm.final(name)`**: Terminal states for completion handling\n* **`hsm.choice(name, ...transitions)`**: Conditional branching logic\n\n### Transitions\n\nTransitions define how the machine responds to events:\n\n```python\nhsm.transition(\n    hsm.on(\"event_name\"),           # Event trigger\n    hsm.source(\"current_state\"),    # Optional explicit source\n    hsm.target(\"next_state\"),       # Destination state\n    hsm.guard(async_guard_func),    # Optional condition\n    hsm.effect(async_effect_func)   # Optional side effect\n)\n```\n\n### Actions and Behaviors\n\nAll behavioral functions are async and receive `(ctx, instance, event)`:\n\n* **`hsm.entry(action)`**: Executed when entering a state\n* **`hsm.exit(action)`**: Executed when exiting a state\n* **`hsm.effect(action)`**: Executed during transitions\n* **`hsm.guard(guard_func)`**: Returns boolean to allow/block transitions\n* **`hsm.activity(activity_func)`**: Long-running operations that auto-cancel\n\n### Timers\n\nBuilt-in timer support for time-based transitions:\n\n```python\nfrom datetime import timedelta\n\n@staticmethod\nasync def one_second(ctx, self, event):\n    return timedelta(seconds=1)\n\nhsm.transition(\n    hsm.after(one_second),  # One-shot timer\n    hsm.target(\"next_state\")\n)\n\nhsm.transition(\n    hsm.every(one_second),  # Recurring timer\n    hsm.target(\".\")\n)\n```\n\n### Error Handling\n\nExceptions in actions automatically dispatch `hsm_error` events:\n\n```python\nclass RobustMachine(hsm.Instance):\n    @staticmethod\n    async def risky_action(ctx, self, event):\n        if random.random() < 0.1:\n            raise ValueError(\"Something went wrong!\")\n\n    @staticmethod\n    async def handle_error(ctx, self, event):\n        print(f\"Error occurred: {event.data}\")\n        # Recovery logic here\n\n    model = hsm.define('RobustMachine',\n        hsm.state('working',\n            hsm.effect(risky_action),\n            hsm.transition(hsm.on('hsm_error'), hsm.target('error_state'))\n        ),\n        hsm.state('error_state',\n            hsm.entry(handle_error)\n        )\n    )\n```\n\n## Advanced Usage\n\n### Class-Based Pattern\n\nThe recommended pattern is to define your state machine as a class:\n\n```python\nclass TrafficLight(hsm.Instance):\n    def __init__(self):\n        super().__init__()\n        self.cycles = 0\n\n    @staticmethod\n    async def change_to_green(ctx, self, event):\n        print(\"\ud83d\udfe2 Green light\")\n        self.cycles += 1\n\n    model = hsm.define('TrafficLight',\n        hsm.initial(hsm.target('red')),\n        hsm.state('red',\n            hsm.transition(hsm.on('timer'), hsm.target('green'))\n        ),\n        hsm.state('green',\n            hsm.entry(change_to_green),\n            hsm.transition(hsm.on('timer'), hsm.target('yellow'))\n        ),\n        hsm.state('yellow',\n            hsm.transition(hsm.on('timer'), hsm.target('red'))\n        )\n    )\n```\n\n### Path Resolution\n\nState paths are resolved relative to the transition's source state:\n\n* **Child**: `'child_state'`\n* **Parent**: `'..'`\n* **Sibling**: `'../sibling_state'`\n* **Absolute**: `'/MachineName/state'`\n* **Self**: `'.'`\n\n### Runtime Lifecycle\n\n```python\n# Create instance and context\ninstance = MyMachine()\nctx = hsm.Context()\n\n# Start the machine\nsm = await hsm.start(ctx, instance, MyMachine.model)\n\n# Check current state\ncurrent_state = sm.state()\n\n# Dispatch events\nawait sm.dispatch(hsm.Event('my_event', data={'key': 'value'}))\n\n# Stop the machine\nawait hsm.stop(sm)\n```\n\n## API Reference\n\n### Core Functions\n\n* **`hsm.define(name, ...elements)`**: Create a state machine model\n* **`hsm.start(ctx, instance, model)`**: Start a state machine instance\n* **`hsm.stop(instance)`**: Stop and cleanup a state machine\n\n### State Elements\n\n* **`hsm.state(name, ...children)`**: Define a state\n* **`hsm.initial(target, ...effects)`**: Define initial state\n* **`hsm.final(name)`**: Define final state\n* **`hsm.choice(name, ...transitions)`**: Define choice pseudostate\n\n### Transitions\n\n* **`hsm.transition(...conditions)`**: Define a transition\n* **`hsm.on(event_name)`**: Event condition\n* **`hsm.after(duration_func)`**: Timer condition (one-shot)\n* **`hsm.every(duration_func)`**: Timer condition (recurring)\n* **`hsm.target(path)`**: Target state\n* **`hsm.guard(func)`**: Guard condition\n* **`hsm.effect(func)`**: Transition effect\n\n### Actions\n\n* **`hsm.entry(func)`**: Entry action\n* **`hsm.exit(func)`**: Exit action\n* **`hsm.activity(func)`**: Long-running activity\n\n### Runtime\n\n* **`hsm.Instance`**: Base class for state machine instances\n* **`hsm.Context`**: Execution context with cancellation support\n* **`hsm.Event(name, data=None)`**: Event object\n\n## Performance\n\nThis implementation uses precomputed lookup tables for O(1) transition resolution, making it suitable for high-performance applications. All behavioral functions are async-first, enabling efficient concurrent execution.\n\n## License\n\nMIT License - see [LICENSE](LICENSE) file for details.\n\n## Contributing\n\nContributions are welcome! Please see the main [HSM repository](https://github.com/stateforward/hsm) for contribution guidelines.\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Hierarchical State Machine implementation for Python with asyncio support",
    "version": "0.1.1",
    "project_urls": {
        "Documentation": "https://github.com/stateforward/hsm-python",
        "Homepage": "https://github.com/stateforward/hsm",
        "Issues": "https://github.com/stateforward/hsm-python/issues",
        "Repository": "https://github.com/stateforward/hsm-python"
    },
    "split_keywords": [
        "async",
        " asyncio",
        " finite-state-machine",
        " hierarchical",
        " hsm",
        " state-machine"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "f04d37543b452818a35b5397b618a1ceed43f667d68b524c4b5c7a11e414d82a",
                "md5": "2ec16db25799e723f4142aaa96d1b805",
                "sha256": "a52a60d90a368572d8e63524dc5e4df0340238c90669ed00291da262e2715257"
            },
            "downloads": -1,
            "filename": "stateforward_hsm-0.1.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "2ec16db25799e723f4142aaa96d1b805",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.13",
            "size": 15990,
            "upload_time": "2025-11-03T20:47:41",
            "upload_time_iso_8601": "2025-11-03T20:47:41.498906Z",
            "url": "https://files.pythonhosted.org/packages/f0/4d/37543b452818a35b5397b618a1ceed43f667d68b524c4b5c7a11e414d82a/stateforward_hsm-0.1.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "d10b68ac6cfcda70304d82759f5950faa71105ac9e595a6a54a2cf54b2aa88db",
                "md5": "b49bd6d2c71338706055ba2e8263f15e",
                "sha256": "a8a2b1f495aee9b8a45f3fecc4f965951da1d907835b5f16d90d27762c3834a4"
            },
            "downloads": -1,
            "filename": "stateforward_hsm-0.1.1.tar.gz",
            "has_sig": false,
            "md5_digest": "b49bd6d2c71338706055ba2e8263f15e",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.13",
            "size": 575807,
            "upload_time": "2025-11-03T20:47:42",
            "upload_time_iso_8601": "2025-11-03T20:47:42.415274Z",
            "url": "https://files.pythonhosted.org/packages/d1/0b/68ac6cfcda70304d82759f5950faa71105ac9e595a6a54a2cf54b2aa88db/stateforward_hsm-0.1.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-11-03 20:47:42",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "stateforward",
    "github_project": "hsm-python",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "stateforward.hsm"
}
        
Elapsed time: 2.30939s