Metadata-Version: 2.4
Name: astage
Version: 0.1.0
Summary: Async model actor library
Project-URL: Homepage, https://github.com/olaals/astage
Project-URL: Repository, https://github.com/olaals/astage
Project-URL: Issue Tracker, https://github.com/olaals/astage/issues
License: MIT License
        
        Copyright (c) 2025 Ola Alstad
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Python: >=3.12
Description-Content-Type: text/markdown

# astage

Initial MVP for Async actor model library for Python

Currently zero external dependencies outside of the Python standard library.

Features:
- Concurrent actor model with asyncio
- Maps message types to handler methods on the actor
- Tell and ask methods for sending messages to the actor
- Allows setting backpressure on the actor mailbox

## Example 
```python
import asyncio
from dataclasses import dataclass
from astage import Actor, handler

@dataclass
class IncrementMessage:
    value: int

@dataclass
class EchoMessage:
    text: str

class CounterActor(Actor):
    def __init__(self):
        super().__init__()
        self.count = 0

    @handler
    async def increment(self, message: IncrementMessage):
        self.count += message.value
        print(f"Count is now: {self.count}")

    @handler
    async def echo(self, message: EchoMessage):
        return f"Echo: {message.text}"

async def main():
    actor = CounterActor()

    # start the actor which will run in a non-blocking asyncio.Task
    handle = await actor.start()

    # tell: send a message without waiting for a response
    await handle.tell(IncrementMessage(5))
    # Expected: Count is now: 5
    await handle.tell(IncrementMessage(5))
    # Expected: Count is now: 10

    # ask: send a message and wait for a response
    result = await handle.ask(EchoMessage("Hello, Actor!"))
    print(result)  # Expected: Echo: Hello, Actor!

    # it is possible to use tell and ask on all @handler methods
    # the return value of the ask will be the return value of the handler
    response = await handle.ask(IncrementMessage(5))
    print(response)  # Expected: None

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

The message types also works with Pydantic classes, although Pydantic is not a dependency of the library.

```python
import asyncio
from pydantic import BaseModel
from astage import Actor, handler

class IncrementMessage(BaseModel):
    value: int

class CounterActor(Actor):
    def __init__(self):
        super().__init__()
        self.count = 0

    @handler
    async def increment(self, message: IncrementMessage):
        self.count += message.value
        return f"Count is now {self.count}"

async def main():
    actor = CounterActor()
    handle = await actor.start()
    
    # create a message using a Pydantic class
    pydantic_msg = IncrementMessage(value=10)

    # ask: send a message and await a response
    result = await handle.ask(pydantic_msg)
    print(result)  # Expected: Count is now 10

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