Metadata-Version: 2.4
Name: larzactor
Version: 0.1.0
Summary: Actor-model concurrency without locks: mailboxes, tell/ask, supervision. Safe stateful concurrency in pure Python, zero dependencies.
Author: larz-scripter
License: MIT
Project-URL: Homepage, https://github.com/larz-scripter/larzactor
Project-URL: Repository, https://github.com/larz-scripter/larzactor
Project-URL: Issues, https://github.com/larz-scripter/larzactor/issues
Keywords: actor,actor-model,concurrency,threading,mailbox,message-passing,supervision,no-locks,parallelism,zero-dependency
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
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: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# larzactor

**Actor-model concurrency without locks. Pure Python, zero dependencies.**

An actor owns its state and a mailbox; it processes one message at a time in its
own thread, so **its state is never touched by two threads at once** - no locks,
no data races, by construction. You talk to actors only by sending messages.

```python
from larzactor import Actor, ActorSystem

class Counter(Actor):
    def __init__(self):
        self.count = 0
    def receive(self, message):
        if message == "inc":
            self.count += 1
        elif message == "get":
            return self.count

system = ActorSystem()
ref = system.spawn(Counter)
for _ in range(1000):
    ref.tell("inc")
ref.ask("get")            # 1000 - exact, even under concurrent senders
system.shutdown()
```

## Why

- **Concurrency you can reason about.** State lives inside an actor and is only
  ever touched by that actor's thread, so you write ordinary sequential code with
  no locks - and get correct results even when many threads send at once (the
  tests hammer one actor from 4 threads and count is exact).
- **tell and ask.** `tell` is fire-and-forget; `ask` sends and waits for the
  handler's return value (re-raising any exception it threw).
- **Supervision.** A handler that raises doesn't kill the actor - a supervisor
  `on_error` hook is notified and the actor keeps processing.
- **Zero dependencies.** Plain threads + queues.

## Install

```bash
pip install larzactor
```

## Usage

```python
from larzactor import Actor, ActorSystem

system = ActorSystem(on_error=lambda ref, msg, exc: log(exc))

ref = system.spawn(MyActor, *init_args)      # Actor subclass
ref = system.spawn(lambda msg: msg * 2)      # or a plain callable

ref.tell(message)                            # fire-and-forget
result = ref.ask(query, timeout=5)           # request/response
ref.stop()
system.shutdown()
```

## Tests

```bash
python -m unittest discover -s tests -v   # 8 tests incl. a no-lost-updates race test
```

## The Larz stack

One of 30+ pure-Python, zero-dependency libraries at
[github.com/larz-scripter](https://github.com/larz-scripter).

## License

MIT (c) larz-scripter
