# walnats
[Nats](https://nats.io/)-based event-driven background jobs and microservices framework.
Features:
+ Event-driven.
+ 100% type-safe.
+ Immutable and easy to test.
+ Explicit APIs, no magic.
+ Strict separation between publishers and subscribers.
+ Asyncio-based.
+ Nats-powered.
+ Exactly-once delivery.
+ Smart and configurable retries.
+ Many integrations.
+ Compatible. You can use walnats to emit events for non-walnats services or consume events emitted by non-walnats services. The tool is flexible enough to adapt to any format of the messages you use.
## Compared to other tools
Compared to other big Python frameworks for background jobs (like [celery](https://docs.celeryq.dev/en/stable/), [dramatiq](https://dramatiq.io/index.html), [rq](https://python-rq.org/), [huey](https://huey.readthedocs.io/en/latest/), and so on), the main difference from an implementation perspective is that walnats is younger and so had an opportunity to be designed around modern technologies right from the beginning. Namely, mypy-powered type safety, async/await-powered concurrency, and nats-powered persistency and distribution.
And when compared to **all** other Python frameworks for background jobs (including new async/await-based ones like [arq](https://arq-docs.helpmanual.io/), [pytask-io](https://github.com/joegasewicz/pytask-io), and [aiotasks](https://github.com/cr0hn/aiotasks)), the main difference is that Walnats is event-driven. While in all these frameworks the job scheduling is conceptually a function call over the network, in walnats publishers instead emit events to which any subscribers can subscribe at any point. This approach is called "[tell, don't ask](https://wiki.c2.com/?TellDontAsk)".
For example, when your webshop sends a parcel to a client, instead of directly calling `send_email` and `send_sms` actors like you'd do with Celery, with Walnats publisher will emit a single `parcel-sent` event, and that event will be delivered by Walnats to all interested actors. It gives you a few nice benefits:
1. Publisher makes only one network request.
1. When you add a new actor, you don't need to modify the publisher. That's especially cool for microservice architecture when the publisher and the actor can be different services owned by different teams.
1. It's easier to reason about. When you develop a microservice, you only need to know what events there are you can subscribe to and emit your own events without thinking too much about how all other services in the system work with these events.
1. It's easier to observe. Walnats directly translates events into Nats subjects and actors into Nats JetStream consumers. So, any Nats observability tool will give you great insights into what's going on in your system.
If you have a big distributed system, Walnats is for you. If all you want is to send emails in the background from your Django monolith or a little hobby project, you might find another framework a better fit.
Lastly, compared to you just taking [nats.py](https://github.com/nats-io/nats.py) and writing your service from scratch, Walnats does a better job at handling failures, load spikes, and corner cases. Walnats is "[designed for failure](https://www.v-wiki.net/design-for-failure/)". Distributed systems are hard, and you shouldn't embark on this journey alone.
## Installation
```bash
python3 -m pip install walnats
```
## Walnats in 30 seconds
Create a module with events, it should be shared across services:
```python
import walnats
COUNT = walnats.Event('counts', int)
# name ⤴ type ⤴
```
Create publisher (a service that generates events):
```python
import asyncio
import walnats
from .events import COUNT
async def run() -> None:
events = walnats.Events(COUNT)
async with events.connect() as conn:
await conn.register()
# ↑ create Nats JetStream streams
for value in range(1000):
await conn.emit(COUNT, value)
# event ⤴ payload ⤴
print(f'emitted {value}')
await asyncio.sleep(1)
asyncio.run(run())
```
Create a subscriber (a service that listens to events):
```python
import asyncio
import walnats
from .events import COUNT
async def run() -> None:
registry = walnats.Actors(
walnats.Actor('print', COUNT, print),
# name ⤴ event ⤴ handler ⤴
)
async with registry.connect() as conn:
await conn.register()
# ↑ create Nats JetStream consumers
await conn.listen()
# ↑ start all actors
asyncio.run(run())
```
That's it! Now you can run the services:
1. Run publisher: `python3 -m pub`
1. Run subscriber in another terminal window: `python3 -m sub`
1. Run another subscriber to see how the work is distributed across multiple instances: `python3 -m sub`
1. Stop a subscriber (`ctrl+c`) to see that no messages get lost.
This code is available in [examples/readme_demo](./examples/readme_demo/).
Raw data
{
"_id": null,
"home_page": null,
"name": "walnats",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "nats,dramatiq,celery,microservices,rabbitmq,arq,rq,huey,event-driven,distributed",
"author": null,
"author_email": "Gram <git@orsinium.dev>",
"download_url": "https://files.pythonhosted.org/packages/c6/79/a691a57a4d413f597772460a47973a3ec7f6026980105f448189bcd0201d/walnats-1.2.5.tar.gz",
"platform": null,
"description": "# walnats\n\n[Nats](https://nats.io/)-based event-driven background jobs and microservices framework.\n\nFeatures:\n\n+ Event-driven.\n+ 100% type-safe.\n+ Immutable and easy to test.\n+ Explicit APIs, no magic.\n+ Strict separation between publishers and subscribers.\n+ Asyncio-based.\n+ Nats-powered.\n+ Exactly-once delivery.\n+ Smart and configurable retries.\n+ Many integrations.\n+ Compatible. You can use walnats to emit events for non-walnats services or consume events emitted by non-walnats services. The tool is flexible enough to adapt to any format of the messages you use.\n\n## Compared to other tools\n\nCompared to other big Python frameworks for background jobs (like [celery](https://docs.celeryq.dev/en/stable/), [dramatiq](https://dramatiq.io/index.html), [rq](https://python-rq.org/), [huey](https://huey.readthedocs.io/en/latest/), and so on), the main difference from an implementation perspective is that walnats is younger and so had an opportunity to be designed around modern technologies right from the beginning. Namely, mypy-powered type safety, async/await-powered concurrency, and nats-powered persistency and distribution.\n\nAnd when compared to **all** other Python frameworks for background jobs (including new async/await-based ones like [arq](https://arq-docs.helpmanual.io/), [pytask-io](https://github.com/joegasewicz/pytask-io), and [aiotasks](https://github.com/cr0hn/aiotasks)), the main difference is that Walnats is event-driven. While in all these frameworks the job scheduling is conceptually a function call over the network, in walnats publishers instead emit events to which any subscribers can subscribe at any point. This approach is called \"[tell, don't ask](https://wiki.c2.com/?TellDontAsk)\".\n\nFor example, when your webshop sends a parcel to a client, instead of directly calling `send_email` and `send_sms` actors like you'd do with Celery, with Walnats publisher will emit a single `parcel-sent` event, and that event will be delivered by Walnats to all interested actors. It gives you a few nice benefits:\n\n1. Publisher makes only one network request.\n1. When you add a new actor, you don't need to modify the publisher. That's especially cool for microservice architecture when the publisher and the actor can be different services owned by different teams.\n1. It's easier to reason about. When you develop a microservice, you only need to know what events there are you can subscribe to and emit your own events without thinking too much about how all other services in the system work with these events.\n1. It's easier to observe. Walnats directly translates events into Nats subjects and actors into Nats JetStream consumers. So, any Nats observability tool will give you great insights into what's going on in your system.\n\nIf you have a big distributed system, Walnats is for you. If all you want is to send emails in the background from your Django monolith or a little hobby project, you might find another framework a better fit.\n\nLastly, compared to you just taking [nats.py](https://github.com/nats-io/nats.py) and writing your service from scratch, Walnats does a better job at handling failures, load spikes, and corner cases. Walnats is \"[designed for failure](https://www.v-wiki.net/design-for-failure/)\". Distributed systems are hard, and you shouldn't embark on this journey alone.\n\n## Installation\n\n```bash\npython3 -m pip install walnats\n```\n\n## Walnats in 30 seconds\n\nCreate a module with events, it should be shared across services:\n\n```python\nimport walnats\n\nCOUNT = walnats.Event('counts', int)\n# name \u2934 type \u2934\n```\n\nCreate publisher (a service that generates events):\n\n```python\nimport asyncio\nimport walnats\nfrom .events import COUNT\n\nasync def run() -> None:\n events = walnats.Events(COUNT)\n async with events.connect() as conn:\n await conn.register()\n # \u2191 create Nats JetStream streams\n for value in range(1000):\n await conn.emit(COUNT, value)\n # event \u2934 payload \u2934\n print(f'emitted {value}')\n await asyncio.sleep(1)\n\nasyncio.run(run())\n```\n\nCreate a subscriber (a service that listens to events):\n\n```python\nimport asyncio\nimport walnats\nfrom .events import COUNT\n\nasync def run() -> None:\n registry = walnats.Actors(\n walnats.Actor('print', COUNT, print),\n # name \u2934 event \u2934 handler \u2934\n )\n async with registry.connect() as conn:\n await conn.register()\n # \u2191 create Nats JetStream consumers\n await conn.listen()\n # \u2191 start all actors\n\nasyncio.run(run())\n```\n\nThat's it! Now you can run the services:\n\n1. Run publisher: `python3 -m pub`\n1. Run subscriber in another terminal window: `python3 -m sub`\n1. Run another subscriber to see how the work is distributed across multiple instances: `python3 -m sub`\n1. Stop a subscriber (`ctrl+c`) to see that no messages get lost.\n\nThis code is available in [examples/readme_demo](./examples/readme_demo/).\n",
"bugtrack_url": null,
"license": null,
"summary": "Nats-based event-driven background jobs and microservices framework.",
"version": "1.2.5",
"split_keywords": [
"nats",
"dramatiq",
"celery",
"microservices",
"rabbitmq",
"arq",
"rq",
"huey",
"event-driven",
"distributed"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "f2fd79141740cc4f1de7fdc20d58bac626596ce5a31ad6676152ff654b7ec5c0",
"md5": "66c004df790a9065c32071e6f8afb9cd",
"sha256": "447e6cddf34f775dd759ad92a27087150adba4721d5c4ba35db027354b1a648b"
},
"downloads": -1,
"filename": "walnats-1.2.5-py3-none-any.whl",
"has_sig": false,
"md5_digest": "66c004df790a9065c32071e6f8afb9cd",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 46863,
"upload_time": "2023-01-21T10:21:16",
"upload_time_iso_8601": "2023-01-21T10:21:16.847460Z",
"url": "https://files.pythonhosted.org/packages/f2/fd/79141740cc4f1de7fdc20d58bac626596ce5a31ad6676152ff654b7ec5c0/walnats-1.2.5-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "c679a691a57a4d413f597772460a47973a3ec7f6026980105f448189bcd0201d",
"md5": "45e04f8d3622a5abc031fb7977e84130",
"sha256": "f2fe6fd6d3be3609d0e48e67ec76f1a8c8cd9a48fdbf9350a219c7019117eee1"
},
"downloads": -1,
"filename": "walnats-1.2.5.tar.gz",
"has_sig": false,
"md5_digest": "45e04f8d3622a5abc031fb7977e84130",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 613716,
"upload_time": "2023-01-21T10:21:19",
"upload_time_iso_8601": "2023-01-21T10:21:19.874108Z",
"url": "https://files.pythonhosted.org/packages/c6/79/a691a57a4d413f597772460a47973a3ec7f6026980105f448189bcd0201d/walnats-1.2.5.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-01-21 10:21:19",
"github": false,
"gitlab": false,
"bitbucket": false,
"lcname": "walnats"
}