Metadata-Version: 2.4
Name: larzbus
Version: 0.1.0
Summary: In-process pub/sub event bus with wildcard topics, async delivery, and dead-letter handling. Pure Python, zero dependencies.
Author: larz-scripter
License: MIT
Project-URL: Homepage, https://github.com/larz-scripter/larzbus
Project-URL: Repository, https://github.com/larz-scripter/larzbus
Project-URL: Documentation, https://github.com/larz-scripter/larzbus#readme
Project-URL: Issues, https://github.com/larz-scripter/larzbus/issues
Keywords: event-bus,pubsub,pub-sub,events,signals,observer,messaging,wildcard,dead-letter,zero-dependency,pure-python
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

# larzbus

**An in-process pub/sub event bus. Pure Python, zero dependencies.**

Decouple your app: publishers announce that something happened, subscribers
react, and neither knows about the other — with three things ad-hoc callback
lists lack.

```python
from larzbus import EventBus

bus = EventBus()

@bus.on("user.*")
def audit(event):
    print(event.topic, event.data)

bus.publish("user.created", {"id": 42})     # audit sees "user.created"
```

## What makes it different

- **Wildcard topics.** Subscribe to `user.*` (one segment) or `order.**` (all
  remaining) and get every matching event — with the concrete topic on
  `event.topic`.
- **Async delivery when you want it.** `bus.publish(topic, data, async_=True)`
  runs handlers on a background thread, so one slow subscriber never blocks the
  publisher.
- **Dead-letter path.** A handler that raises doesn't take down the publish — the
  other handlers still run, and the event + exception go to your `on_error`
  handlers and a `dead_letters` log.
- **Zero dependencies**, thread-safe.

## Install

```bash
pip install larzbus
```

## Usage

```python
from larzbus import EventBus

bus = EventBus()

# subscribe (decorator or direct); wildcards allowed
@bus.on("order.*")
def on_order(event):
    ...

sub = bus.subscribe("payment.completed", handler)
bus.once("startup", run_once)          # auto-unsubscribes after first delivery
sub.unsubscribe()

# publish
results = bus.publish("order.created", {"id": 1})   # list of handler returns
bus.publish("email.queued", payload, async_=True)    # non-blocking

# dead-letter
@bus.on_error
def failed(topic, event, exc):
    log.warning("handler failed on %s: %s", topic, exc)

bus.dead_letters      # [(topic, event, exception), ...]
```

## Wildcards

| pattern | matches |
|---|---|
| `user.created` | exactly that topic |
| `user.*` | `user.created`, `user.deleted` (one more segment) |
| `order.**` | `order.created`, `order.item.added`, … (any depth) |

## Tests

```bash
python -m unittest discover -s tests -v   # 17 tests, zero deps
```

## The Larz stack

Pure-Python, zero-dependency building blocks: **[larz](https://github.com/larz-scripter/larz)** · **[larzchain](https://github.com/larz-scripter/larzchain)** · **[larzmoney](https://github.com/larz-scripter/larzmoney)** · **[larzcrypt](https://github.com/larz-scripter/larzcrypt)** · **[larzdb](https://github.com/larz-scripter/larzdb)** · **[larzagent](https://github.com/larz-scripter/larzagent)** · **[larzchart](https://github.com/larz-scripter/larzchart)** · **[larzmark](https://github.com/larz-scripter/larzmark)** · **[larztask](https://github.com/larz-scripter/larztask)** · **[larzvault](https://github.com/larz-scripter/larzvault)** · **[larzvm](https://github.com/larz-scripter/larzvm)** · **[larzcache](https://github.com/larz-scripter/larzcache)** · **[larzvalidate](https://github.com/larz-scripter/larzvalidate)** · **[larzid](https://github.com/larz-scripter/larzid)** · **[larzrpc](https://github.com/larz-scripter/larzrpc)** · **[larzstate](https://github.com/larz-scripter/larzstate)** · **[larzhttp](https://github.com/larz-scripter/larzhttp)** · **[larzconf](https://github.com/larz-scripter/larzconf)** · **[larzcron](https://github.com/larz-scripter/larzcron)** · **[larzlimit](https://github.com/larz-scripter/larzlimit)** · **[larzlog](https://github.com/larz-scripter/larzlog)** · **[larzcli](https://github.com/larz-scripter/larzcli)** · **[larzretry](https://github.com/larz-scripter/larzretry)** · **[larztime](https://github.com/larz-scripter/larztime)** · **[larzpdf](https://github.com/larz-scripter/larzpdf)** · **[larzpack](https://github.com/larz-scripter/larzpack)** · **[larztemplate](https://github.com/larz-scripter/larztemplate)** · **[larzcolor](https://github.com/larz-scripter/larzcolor)** · **[larztable](https://github.com/larz-scripter/larztable)** · **[larzjson](https://github.com/larz-scripter/larzjson)** · **larzbus**

## License

MIT © larz-scripter
