Skip to main content

DomoActors-Py

A Production-Ready Actor Model Toolkit for Python

License: LGPL-3.0 Python 3.10+ PyPI Test Coverage

Overview

DomoActors-Py is a sophisticated actor framework for Python that provides:

  • Comprehensive Type Hints: Full type annotations with Protocol classes for IDE support and static analysis
  • Fault Tolerance: Hierarchical supervision with configurable strategies
  • Message-Driven Concurrency: FIFO per-actor mailboxes with async dispatch
  • Flexible Addressing: UUIDv7 for distributed systems, numeric for simple scenarios
  • Comprehensive Testing: Observable state, await utilities, dead letter listeners
  • Zero External Dependencies: Pure Python implementation using only standard library

Project Status

✅ Production Ready - Fully implemented with comprehensive test coverage (197+ tests, 86% coverage)

Complete Feature Set:

  • ✅ Core actor model (Actor, Protocol, Stage)
  • ✅ Hierarchical supervision (Resume, Restart, Stop, Escalate)
  • ✅ Self-messaging patterns with recursive mailbox dispatch
  • ✅ Bounded and unbounded mailboxes with overflow policies
  • ✅ Lifecycle hooks (before_start, after_stop, before_restart, etc.)
  • ✅ Scheduling (one-time and repeating tasks)
  • ✅ Dead letters handling
  • ✅ Observable state for testing
  • ✅ Full parity with DomoActors-TS

Example Applications:

  • ✅ Complete banking system with interactive CLI
  • ✅ Transaction coordination with retry logic
  • ✅ Context-aware error handling

Quick Start

Installation

Install DomoActors-Py from PyPI using pip:

pip install domo-actors

Requirements: Python 3.10 or higher (no external dependencies!)

Alternative: Install from Source

For development or the latest unreleased features:

git clone https://github.com/VaughnVernon/DomoActors-Py.git
cd DomoActors-Py
pip install -e .

Verify Installation

from domo_actors import stage, Actor, Protocol
print("DomoActors-Py installed successfully!")

Basic Example

import asyncio
from domo_actors import Actor, ActorProtocol, Protocol, ProtocolInstantiator
from domo_actors import Definition, stage, Uuid7Address


# 1. Define the protocol interface
class Counter(ActorProtocol):
    async def increment(self) -> None: ...
    async def get_value(self) -> int: ...


# 2. Implement the actor
class CounterActor(Actor):
    def __init__(self):
        super().__init__()
        self._count = 0

    async def increment(self) -> None:
        self._count += 1

    async def get_value(self) -> int:
        return self._count


# 3. Create protocol instantiator
class CounterInstantiator(ProtocolInstantiator):
    def instantiate(self, definition: Definition) -> Actor:
        return CounterActor()


class CounterProtocol(Protocol):
    def type(self) -> str:
        return "Counter"

    def instantiator(self) -> ProtocolInstantiator:
        return CounterInstantiator()


# 4. Use the actor
async def main():
    # Create actor (stage() returns singleton instance)
    counter: Counter = stage().actor_for(
        CounterProtocol(),
        Definition("Counter", Uuid7Address(), ())
    )

    # Use actor
    await counter.increment()
    await counter.increment()
    value = await counter.get_value()
    print(f"Count: {value}")  # Output: Count: 2

    # Cleanup
    await stage().close()


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

Core Concepts

Actors

Actors are the fundamental unit of computation. They:

  • Process messages sequentially through their mailbox
  • Maintain private state
  • Can create child actors
  • Never share state directly

Messages

Messages are asynchronous and delivered via FIFO mailboxes. DomoActors uses a dynamic proxy pattern that automatically converts method calls into messages.

Supervision

Actors are organized in a hierarchy. When an actor fails, its supervisor decides what to do:

  • Restart: Create a new instance
  • Resume: Continue processing messages
  • Stop: Terminate the actor
  • Escalate: Pass the decision to the parent supervisor

Lifecycle Hooks

Actors provide lifecycle hooks for customization:

  • before_start(): Initialize actor state
  • before_stop(): Cleanup before termination
  • before_restart(reason): Cleanup before restart
  • after_restart(): Re-initialize after restart

Features

Dynamic Proxy Pattern

DomoActors uses Python's __getattr__ magic method to implement a dynamic proxy pattern similar to JavaScript's Proxy API:

# Method calls are automatically converted to messages
await actor.do_something(arg1, arg2)
# Behind the scenes:
# 1. Creates a DeferredPromise
# 2. Wraps call in a LocalMessage
# 3. Enqueues in mailbox
# 4. Returns promise to caller

Mailbox Types

ArrayMailbox: Unbounded FIFO queue

from domo_actors import ArrayMailbox
mailbox = ArrayMailbox()

BoundedMailbox: Capacity-limited with overflow policies

from domo_actors import BoundedMailbox, OverflowPolicy

mailbox = BoundedMailbox(
    capacity=100,
    overflow_policy=OverflowPolicy.DROP_OLDEST
)

Fault Tolerance

Configure supervision strategies:

from domo_actors import DefaultSupervisor, SupervisionStrategy, SupervisionScope

class MySupervisor(DefaultSupervisor):
    async def supervision_strategy(self) -> SupervisionStrategy:
        return CustomStrategy()

class CustomStrategy(SupervisionStrategy):
    def intensity(self) -> int:
        return 5  # Allow 5 restarts

    def period(self) -> int:
        return 10000  # Within 10 seconds

    def scope(self) -> SupervisionScope:
        return SupervisionScope.ONE  # Only failed actor

Scheduling

Schedule tasks using the built-in scheduler:

from datetime import timedelta
from domo_actors import stage

scheduler = stage().scheduler()

# Schedule once
scheduler.schedule_once(
    delay=timedelta(seconds=5),
    action=lambda: print("Executed!")
)

# Schedule repeated
scheduler.schedule_repeat(
    initial_delay=timedelta(seconds=1),
    interval=timedelta(seconds=5),
    action=lambda: print("Tick!")
)

Testing

DomoActors provides testing utilities:

from domo_actors.actors.testkit import await_assert, await_state_value

async def test_actor():
    # Wait for assertion to pass
    await await_assert(
        lambda: assert_something(),
        timeout=2.0
    )

    # Wait for state value
    await await_state_value(
        actor,
        "status",
        "completed",
        timeout=2.0
    )

Examples

See the examples/ directory for complete examples:

Counter Example

Basic actor creation and message passing demonstrating:

  • Protocol definition
  • Actor implementation
  • Message passing
  • Async/await patterns

Bank Example (Production-Grade)

A complete banking system with interactive CLI (examples/bank/bank.py) demonstrating:

Core Features:

  • Account management (open, deposit, withdraw)
  • Inter-account transfers with 5-phase coordination
  • Transaction history with self-messaging pattern
  • Exponential backoff retry logic

Advanced Patterns:

  • Hierarchical actors: Bank → Account → TransactionHistory
  • Three supervisor types: BankSupervisor, AccountSupervisor, TransferSupervisor
  • Context-aware error handling: Custom error messages with request details
  • Self-messaging: Actors sending messages to themselves for state consistency
  • "Let it crash" philosophy: Validation errors handled by supervisors

Run the example:

cd examples/bank
python bank.py

The bank example is functionally equivalent to the TypeScript version, demonstrating full parity between implementations.

Architecture

┌─────────────────────────────────────┐
│         LocalStage                  │
│  ┌───────────────────────────────┐  │
│  │    PrivateRootActor           │  │
│  │      ┌─────────────────────┐  │  │
│  │      │  PublicRootActor    │  │  │
│  │      │    ┌──────────┐     │  │  │
│  │      │    │  User    │     │  │  │
│  │      │    │  Actors  │     │  │  │
│  │      │    └──────────┘     │  │  │
│  │      └─────────────────────┘  │  │
│  └───────────────────────────────┘  │
│                                     │
│  Directory (Actor Registry)         │
│  Scheduler                          │
│  DeadLetters                        │
│  Supervisors                        │
└─────────────────────────────────────┘

Design Principles

  1. Correctness First: Type-safe interfaces, strict async/await patterns
  2. Fault Tolerance: Hierarchical supervision
  3. Developer Productivity: Protocol interfaces, lifecycle hooks, testing utilities
  4. Zero Dependencies: Pure Python using only standard library

Comparison with TypeScript Version

DomoActors-Py is a faithful port of DomoActors-TS with Python-specific adaptations:

Feature TypeScript Python
Proxy Pattern ES6 Proxy API __getattr__ magic method
Async Model Promises asyncio Futures
Concurrency Single-threaded event loop (async/await) Single-threaded event loop (async/await)
Type System Compile-time static typing Runtime type hints (PEP 484)
Type Checking Built-in (tsc) Optional tools (mypy, pyright)
IDE Support Full IntelliSense Full IntelliSense with type hints
Stage Access stage() singleton stage() singleton
Mailbox Dispatch Recursive dispatch Recursive dispatch
Self-Messaging this.selfAs<T>() self.self_as(T)
Supervision Hierarchical supervisors Hierarchical supervisors
Collections Map/Set dict/set
Actor Lifecycle Hooks (beforeStart, etc.) Hooks (before_start, etc.)

Architectural Parity

Both implementations share the same core architecture:

  • Actor hierarchy: PrivateRootActor → PublicRootActor → User actors
  • Supervision strategies: Resume, Restart, Stop, Escalate
  • Message delivery: FIFO per-actor mailboxes
  • Address types: UUIDv7 for distributed, numeric for simple scenarios
  • Zero external dependencies: Both use only standard library features

Python-Specific Features

  • Context managers: Can use async with for resource management
  • Type hints: Comprehensive PEP 484 type annotations throughout the codebase
  • Static type checking: Compatible with mypy, pyright, and IDE type checkers
  • Protocol classes: Structural typing similar to TypeScript interfaces
  • AsyncIO integration: Native asyncio support, works with existing async code
  • Testing: pytest-asyncio integration for async test cases

Concurrency Model

DomoActors-Py uses single-threaded async/await, just like JavaScript/TypeScript:

# Single event loop thread
async def actor_method(self):
    await self.some_operation()  # Yields to event loop
    # Continues on same thread

Key Points:

  • ✅ Single-threaded event loop (like JavaScript)
  • ✅ Cooperative multitasking at await points
  • ✅ No parallelism, only concurrency
  • ✅ Actors process messages sequentially
  • ✅ Perfect parity with DomoActors-TS behavior

Note: Python does support multi-threading (threading module) and multi-processing (multiprocessing module), but DomoActors-Py uses pure asyncio to maintain identical semantics with the TypeScript version.

Type Hints and Static Analysis

DomoActors-Py includes comprehensive type hints:

# Full type annotations
def actor_for(
    self,
    protocol: Protocol,
    definition: Definition,
    parent: Optional[Actor] = None,
    supervisor_name: Optional[str] = None
) -> T:
    ...

# Generic type variables
T = TypeVar('T')

# Protocol classes for structural typing
class Counter(ActorProtocol):
    async def increment(self) -> None: ...
    async def get_value(self) -> int: ...

Benefits:

  • ✅ IDE autocomplete and IntelliSense
  • ✅ Catch type errors before runtime with mypy/pyright
  • ✅ Self-documenting code
  • ✅ Refactoring safety

Check types statically:

mypy domo_actors  # Type check the library

Requirements

  • Python 3.10 or higher
  • No external dependencies for core functionality
  • pytest and pytest-asyncio for running tests

Development

# Install dev dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Format code
black domo_actors tests examples

# Type check
mypy domo_actors

# Lint
ruff check domo_actors tests examples

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass
  5. Submit a pull request

Support

Acknowledgments

DomoActors-Py is inspired by and builds upon concepts from:

  • XOOM/Actors (Java actor framework by Vaughn Vernon)
  • DomoActors-TS (TypeScript version)

License

GNU Lesser General Public License v3.0 (LGPL-3.0)

See: ./LICENSE.md

Copyright © 2012-2026 Vaughn Vernon. All rights reserved. Copyright © 2012-2026 Kalele, Inc. All rights reserved.

About the Creator and Author

Vaughn Vernon

Metadata

Release files for domo-actors 1.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for domo-actors 1.0.1
File Size Uploaded Provenance
domo_actors-1.0.1.tar.gz 39.5 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for domo-actors 1.0.1
File Interpreter ABI Platform
domo_actors-1.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 96.8 kB

Release files / domo_actors-1.0.1.tar.gz

Download URL domo_actors-1.0.1.tar.gz
Size 39.5 kB
Tags Source
SHA-256 checksum
How to use checksums
205aa05c09ecce31431c8787a6cc99f45b8d23e897caaee6d854d5c820b81212
BLAKE2b-256 checksum
How to use checksums
ba320e74f1b21ea573de553294e61bc0e65a281f019ec15c8ef77dc2ab347a49
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.6

Release files / domo_actors-1.0.1-py3-none-any.whl

Download URL domo_actors-1.0.1-py3-none-any.whl
Size 57.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
75b5fc43bc78d50c6a3284665fac2eb2e295f9bf1cdd6792f646b2b42f0f4221
BLAKE2b-256 checksum
How to use checksums
b5851aa48a310fb0c980354ff461fcaf70ae9a82b17ca73c9b7905a21d5f172c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.6

Security for domo-actors 1.0.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.0.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page