panini

Python microframework for NATS messaging

https://github.com/lwinterface/panini

Science Score: 13.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • CITATION.cff file
  • codemeta.json file
    Found codemeta.json file
  • .zenodo.json file
  • DOI references
  • Academic publication links
  • Committers with academic emails
  • Institutional organization owner
  • JOSS paper metadata
  • Scientific vocabulary similarity
    Low similarity (12.0%) to scientific vocabulary

Keywords

aio asyncio messaging microservice nats panini

Keywords from Contributors

interactive serializer packaging network-simulation shellcodes hacking autograding observability embedded optim
Last synced: 6 months ago · JSON representation

Repository

Python microframework for NATS messaging

Basic Info
  • Host: GitHub
  • Owner: lwinterface
  • License: mit
  • Language: Python
  • Default Branch: main
  • Homepage:
  • Size: 1.71 MB
Statistics
  • Stars: 94
  • Watchers: 8
  • Forks: 20
  • Open Issues: 37
  • Releases: 19
Archived
Topics
aio asyncio messaging microservice nats panini
Created over 5 years ago · Last pushed almost 2 years ago
Metadata Files
Readme Changelog Contributing License

README.md

Panini

Panini is a python microframework based on the nats.py library. Its goal is to offer developers an easy way to create NATS microservices with a lower barrier of entry. It provides a specific template for creating microservices, similarly to FastAPI, Aiohttp, or Flask. Like all of the above frameworks, Panini has its design limits and edge cases. In the case that you become restricted by Panini's capabilities, we recommend switching to nats.py.

docs Build Status Versions License Apache 2.0

Panini was inspired by Faust project.


Documentation

Documentation is here.

How to install

Before getting started make sure you have all the prerequisites installed:

python pip install panini

A simple listener app example

A minimal app with one stream endpoint, one request endpoint, and one periodic task might look like this:

```python from panini import app as panini_app

app = paniniapp.App( servicename='listener_app', host='127.0.0.1', port=4222, )

@app.listen("some.subject.for.request") async def request_listener(msg): """ request endpoint """ print(f"request {msg.data} from {msg.subject} has been processed") return {"success": True, "message": "request has been processed"}

@app.listen("some.subject.for.stream") async def stream_listener(msg): """ stream endpoint """ print(f"event {msg.data} from {msg.subject} has been processed")

if name == "main": app.start() ```

What's going on here?

  1. Imported Panini.
  2. Initialized app. Created an instance of class App from module panini with any microservice name, NATS host, and port.
  3. First @app.listen registers the listening subject "some.subject.for.request" with request_listener. Every time this app receives a request addressed to "some.subject.for.request", the function request_listener is called to process it, then it sends a return response back to an addressee.
  4. Secondly @app.listen register the listening subject "some.subject.for.stream" with stream_listener. Same as with request_listener but without sending the result back.
  5. app.start() runs an app. No code under this command will ever be called.

Save the above code to file listener_app.py.

Make sure that you have all prerequisites from Install. Open the terminal to run the app:

```python

python3 listener_app.py

Panini service connected to NATS.. id: 3 name: listenerappnondockerenv270377__75017

NATS brokers:

* nats://127.0.0.1:4222

```

That's it. Now let's create something that will generate messages.

A simple app example that generates messages

Our goal here is to trigger endpoints from listener app above:

  • "some.subject.for.request" - request something, receive response
  • "some.subject.for.stream" - send some event without waiting for response

```python from panini import app as panini_app

app = paniniapp.App( servicename='sender_app', host='127.0.0.1', port=4222, )

@app.task(interval=1) async def request_periodically(): message = {"data":"request1234567890"} response = await app.request( subject="some.subject.for.request", message=message, ) print(response)

@app.task(interval=1) async def publish_periodically(): message = {"data":"event1234567890"} await app.publish( subject="some.subject.for.stream", message=message, )

if name == "main": app.start() ```

What's new here:

  1. First, @app.task registers function request_periodically to call it periodically at given interval, each 1 second in the example.
  2. Function app.request sends requests, asynchronously waits for a response.
  3. The second @app.task does the same as the first one but for publishing.
  4. Function app.publish sends a message like a request but without expecting any response. Fire and forget.

Save the code to new file sender_app.py.

Make sure that listener_app.py keeps running, then open a new terminal session to run the sender app:

```python

python3 sender_app.py

Panini service connected to NATS.. id: 3 name: senderappnondockerenv270377__75017

NATS brokers:

* nats://127.0.0.1:4222

{'success': True, 'message': 'request has been processed'} {'success': True, 'message': 'request has been processed'} {'success': True, 'message': 'request has been processed'} {'success': True, 'message': 'request has been processed'} {'success': True, 'message': 'request has been processed'} {'success': True, 'message': 'request has been processed'} {'success': True, 'message': 'request has been processed'} {'success': True, 'message': 'request has been processed'} ```

Note that in the terminal session where you run listener_app.py you should see received requests and events:

python event {'data': 'event1234567890'} from some.subject.for.stream has been processed request {'data': 'request1234567890'} from some.subject.for.request has been processed event {'data': 'event1234567890'} from some.subject.for.stream has been processed request {'data': 'request1234567890'} from some.subject.for.request has been processed event {'data': 'event1234567890'} from some.subject.for.stream has been processed request {'data': 'request1234567890'} from some.subject.for.request has been processed event {'data': 'event1234567890'} from some.subject.for.stream has been processed request {'data': 'request1234567890'} from some.subject.for.request has been processed event {'data': 'event1234567890'} from some.subject.for.stream has been processed request {'data': 'request1234567890'} from some.subject.for.request has been processed event {'data': 'event1234567890'} from some.subject.for.stream has been processed request {'data': 'request1234567890'} from some.subject.for.request has been processed

More possibilities

In the first example, we created an application that listens for messages, in the second example, an application that sends messages. Panini allows you to freely combine sending and receiving messages in one application.

Let's check out what else you can do with Panini using a minimal interface:

  • One-time tasks on start. Similar to the above periodic task but without interval argument

python @app.task() async def publish(): while True: message = get_some_update() await app.publish(subject='some.subject', message=message)

  • Synchronous endpoints

python @app.task(interval=2) def your_periodic_task(): for _ in range(10): app.publish_sync( subject='some.publish.subject', message={'some':'data'} )

  • Accept different datatypes: dict, str, bytes

python @app.timer_task(interval=2) def your_periodic_task(): for _ in range(10): app.publish_sync( subject='some.publish.subject', message=b'messageinbytestosend', data_type=bytes )

  • Create middlewares for NATS messages

```python from panini.middleware import Middleware

class MyMiddleware(Middleware):

async def send_publish(self, subject, message, publish_func, **kwargs):
    print('do something before publish')
    await publish_func(subject, message, **kwargs)
    print('do something after publish')

async def listen_publish(self, msg, cb):
    print('do something before listen')
    await cb(msg)
    print('do something after listen')

async def send_request(self, subject, message, request_func, **kwargs):
    print('do something before send request')
    result = await request_func(subject, message, **kwargs)
    print('do something after send request')
    return result

async def listen_request(self, msg, cb):
    print('do something before listen request')
    result = await cb(msg)
    print('do something after listen request')
    return result

```

  • Create HTTP endpoints with Aiohttp and NATS endpoints all together in one microservice

    ```python from aiohttp import web

    @app.listen('some.publish.subject') async def subjectforrequestslistener(msg): handleincoming_message(msg.subject, msg.data)

    @app.http.get('/get') async def webendpointlistener(request): """ Single HTTP endpoint """ return web.Response(text="Hello, world")

    @app.http.view('/path/to/rest/endpoints') class MyView(web.View): """ HTTP endpoints for REST schema """ async def get(self): request = self.request return web.Response(text="Hello, REST world")

    async def post(self):
        request = self.request
        return web.Response(text="Hello, REST world")
    

    ```

  • Built-in traffic balancing between instances of the microservice if you have high loads

```python app = paniniapp.App( servicename='asyncpublish', host='127.0.0.1', allocationqueue_group='group24', port=4222, )

incoming traffic will be distributed among

all microservices that are in the "group24"

```

Need more examples? Check here.

Testing

We use pytest for testing

To run tests (notice, that nats-server must be running on port 4222 for tests): shell pytest

Contributing

Welcome contributor! We are looking developers to make Panini a great project.

Working on your first Pull Request? You can learn how from this free series, How to Contribute to an Open Source Project on GitHub.

Here's how you can help:

  • suggest new updates or report about bug here
  • review a pull request
  • fix an issue
  • write a tutorial
  • always follow by this guide for your contributions

At this point, you're ready to make your changes! Feel free to ask for help :smile_cat:

Owner

  • Name: lwinterface
  • Login: lwinterface
  • Kind: organization

GitHub Events

Total
  • Watch event: 8
  • Fork event: 2
Last Year
  • Watch event: 8
  • Fork event: 2

Committers

Last synced: over 2 years ago

All Time
  • Total Commits: 430
  • Total Committers: 9
  • Avg Commits per committer: 47.778
  • Development Distribution Score (DDS): 0.635
Past Year
  • Commits: 68
  • Committers: 3
  • Avg Commits per committer: 22.667
  • Development Distribution Score (DDS): 0.456
Top Committers
Name Email Commits
artas728 a****v@g****m 157
d.tuytuyshkin@gmail.com d****n@g****m 154
Igor Khvan i****7@g****m 41
oleksii-v A****4 33
artas m****8@g****m 27
Dmitriy z****m@g****m 9
dependabot[bot] 4****] 6
vzverev v****v@f****g 2
Eugene Sokolyanskyi e****4@g****m 1
Committer Domains (Top 20 + Academic)

Issues and Pull Requests

Last synced: 6 months ago

All Time
  • Total issues: 35
  • Total pull requests: 95
  • Average time to close issues: 5 months
  • Average time to close pull requests: 9 days
  • Total issue authors: 8
  • Total pull request authors: 10
  • Average comments per issue: 1.34
  • Average comments per pull request: 0.13
  • Merged pull requests: 67
  • Bot issues: 0
  • Bot pull requests: 12
Past Year
  • Issues: 0
  • Pull requests: 0
  • Average time to close issues: N/A
  • Average time to close pull requests: N/A
  • Issue authors: 0
  • Pull request authors: 0
  • Average comments per issue: 0
  • Average comments per pull request: 0
  • Merged pull requests: 0
  • Bot issues: 0
  • Bot pull requests: 0
Top Authors
Issue Authors
  • danylott (12)
  • oleksii-volotskov (7)
  • artas728 (6)
  • kirill-ratkin (4)
  • roblight (3)
  • alexreznikoff (1)
  • tekumara (1)
Pull Request Authors
  • artas728 (52)
  • i2gor87 (14)
  • dependabot[bot] (11)
  • danylott (10)
  • bugimprover (2)
  • kirill-ratkin (1)
  • oleksii-volotskov (1)
  • gelleson (1)
  • nickolay-github (1)
  • vlad-zverev (1)
Top Labels
Issue Labels
new feature (5) bug (4) help wanted (1) discussion required (1) enhancement (1) documentation (1)
Pull Request Labels
dependencies (11)

Packages

  • Total packages: 1
  • Total downloads:
    • pypi 326 last-month
  • Total dependent packages: 0
  • Total dependent repositories: 4
  • Total versions: 53
  • Total maintainers: 2
pypi.org: panini

A python messaging framework for microservices based on NATS

  • Versions: 53
  • Dependent Packages: 0
  • Dependent Repositories: 4
  • Downloads: 326 Last month
Rankings
Dependent repos count: 7.5%
Stargazers count: 8.1%
Downloads: 8.3%
Average: 8.6%
Forks count: 8.9%
Dependent packages count: 10.1%
Maintainers (2)
Last synced: 6 months ago

Dependencies

examples/dockercompose_project/microservice1/requirements.txt pypi
  • aiohttp ==3.7.4
  • aiohttp-cors ==0.7.0
  • async-timeout ==3.0.1
  • asyncio-nats-client ==0.11.2
  • colorclass ==2.2.0
  • panini ==0.1.2
  • requests ==2.24.0
  • six ==1.15.0
  • yarl ==1.6.1
examples/dockercompose_project/microservice2/requirements.txt pypi
  • aiohttp ==3.7.4
  • aiohttp-cors ==0.7.0
  • async-timeout ==3.0.1
  • asyncio-nats-client ==0.11.2
  • colorclass ==2.2.0
  • panini ==0.1.2
  • requests ==2.24.0
  • six ==1.15.0
  • yarl ==1.6.1
requirements/defaults.txt pypi
  • aiohttp ==3.8.1
  • aiohttp-cors ==0.7.0
  • async-timeout ==4.0.0
  • nats-py ==2.0.0
  • nats-python ==0.8.0
  • nest-asyncio ==1.5.1
  • prometheus-client ==0.9.0
  • pytest ==6.2.5
  • pytest-asyncio ==0.17.2
  • python-json-logger ==2.0.1
  • requests ==2.24.0
  • six ==1.16.0
  • ujson ==5.1.0
  • websocket-client ==1.2.3
  • yarl ==1.7.2
setup.py pypi
  • async-timeout ==4.0.0
  • nats-py ==2.0.0
  • nats-python >=0.8.0
  • nest-asyncio ==1.5.1
  • prometheus-client ==0.9.0
  • python-json-logger >=2.0.1
  • requests >=2.24.0
  • six >=1.15.0
  • ujson ==5.1.0
  • websocket-client >=1.2.3
  • yarl >=1.6.1
docker-compose.yml docker
  • nats latest
examples/dockercompose_project/docker-compose.yml docker
  • nats latest
examples/dockercompose_project/microservice1/Dockerfile docker
  • python 3.8.3-buster build
examples/dockercompose_project/microservice2/Dockerfile docker
  • python 3.8.3-buster build