{"id":17312746,"url":"https://github.com/lancetnik/propan","last_synced_at":"2025-03-11T09:06:49.920Z","repository":{"id":49322646,"uuid":"435992155","full_name":"Lancetnik/Propan","owner":"Lancetnik","description":"Propan is a powerful and easy-to-use Python framework for building event-driven applications that interact with any MQ Broker","archived":false,"fork":false,"pushed_at":"2024-05-09T13:12:51.000Z","size":4503,"stargazers_count":489,"open_issues_count":20,"forks_count":27,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-03-02T19:18:47.315Z","etag":null,"topics":["amqp","asyncapi","event-driven","kafka","messaging","nats","propan","python","python-types","rabbitmq","redis","sqs"],"latest_commit_sha":null,"homepage":"https://lancetnik.github.io/Propan/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Lancetnik.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-12-07T18:49:16.000Z","updated_at":"2025-03-01T05:10:26.000Z","dependencies_parsed_at":"2024-05-09T14:15:43.775Z","dependency_job_id":"dd5b99b7-8ddc-49a5-a0c7-de021170b4a1","html_url":"https://github.com/Lancetnik/Propan","commit_stats":{"total_commits":40,"total_committers":3,"mean_commits":"13.333333333333334","dds":0.35,"last_synced_commit":"a85eac5875a17a0c775572ad99dd296e4bf17dd8"},"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lancetnik%2FPropan","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lancetnik%2FPropan/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lancetnik%2FPropan/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lancetnik%2FPropan/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Lancetnik","download_url":"https://codeload.github.com/Lancetnik/Propan/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243004065,"owners_count":20220237,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["amqp","asyncapi","event-driven","kafka","messaging","nats","propan","python","python-types","rabbitmq","redis","sqs"],"created_at":"2024-10-15T12:44:29.936Z","updated_at":"2025-03-11T09:06:49.888Z","avatar_url":"https://github.com/Lancetnik.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://lancetnik.github.io/Propan/\" target=\"_blank\"\u003e\n        \u003cimg src=\"https://lancetnik.github.io/Propan/assets/img/logo-no-background.png\" alt=\"Propan logo\" style=\"height: 250px; width: 600px;\"/\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://github.com/Lancetnik/Propan/actions/workflows/tests.yml\" target=\"_blank\"\u003e\n        \u003cimg src=\"https://github.com/Lancetnik/Propan/actions/workflows/tests.yml/badge.svg\" alt=\"Tests coverage\"/\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://coverage-badge.samuelcolvin.workers.dev/redirect/lancetnik/propan\" target=\"_blank\"\u003e\n        \u003cimg src=\"https://coverage-badge.samuelcolvin.workers.dev/lancetnik/propan.svg\" alt=\"Coverage\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://pypi.org/project/propan\" target=\"_blank\"\u003e\n        \u003cimg src=\"https://img.shields.io/pypi/v/propan?label=pypi%20package\" alt=\"Package version\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://pepy.tech/project/propan\" target=\"_blank\"\u003e\n        \u003cimg src=\"https://static.pepy.tech/personalized-badge/propan?period=total\u0026units=international_system\u0026left_color=grey\u0026right_color=blue\u0026left_text=Downloads\" alt=\"downloads\"/\u003e\n    \u003c/a\u003e\n    \u003cbr/\u003e\n    \u003ca href=\"https://pypi.org/project/propan\" target=\"_blank\"\u003e\n        \u003cimg src=\"https://img.shields.io/pypi/pyversions/propan.svg\" alt=\"Supported Python versions\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://github.com/Lancetnik/Propan/blob/main/LICENSE\" target=\"_blank\"\u003e\n        \u003cimg alt=\"GitHub\" src=\"https://img.shields.io/github/license/Lancetnik/Propan?color=%23007ec6\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://discord.gg/qFm6aSqq59\" target=\"_blank\"\u003e\n      \u003cimg alt=\"Discord\" src=\"https://img.shields.io/discord/1085457301214855171?logo=discord\"\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n# Propan\n\n**Propan** - just *~~an another one HTTP~~* a **declarative Python Messaging Framework**. It's inspired by \u003ca href=\"https://FastAPI.tiangolo.com/ru/\" target=\"_blank\"\u003e*FastAPI*\u003c/a\u003e and \u003ca href=\"https://docs.celeryq.dev/projects/kombu/en/stable/\" target=\"_blank\"\u003e*Kombu*\u003c/a\u003e, simplify Message Brokers around code writing and provides a helpful development toolkit, which existed only in HTTP-frameworks world until now.\n\nIt's designed to create reactive microservices around \u003ca href=\"https://microservices.io/patterns/communication-style/messaging.html\" target=\"_blank\"\u003eMessaging Architecture\u003c/a\u003e.\n\nIt is a modern, high-level framework on top of popular specific Python brokers libraries, based on \u003ca href=\"https://docs.pydantic.dev/\" target=\"_blank\"\u003e*pydantic*\u003c/a\u003e and \u003ca href=\"https://FastAPI.tiangolo.com/ru/\" target=\"_blank\"\u003e*FastAPI*\u003c/a\u003e, \u003ca href=\"https://docs.pytest.org/en/latest/\" target=\"_blank\"\u003e*pytest*\u003c/a\u003e concepts.\n\n---\n\n## :warning::warning::warning: Deprecation notice :warning::warning::warning:\n\nThis project is superceeded by [**FastStream**](https://github.com/airtai/faststream).\n\n**FastStream** is a new package based on the ideas and experiences gained from [**FastKafka**](https://github.com/airtai/fastkafka) and **Propan**. By joining our forces, we picked up the best from both packages and created a unified way to write services capable of processing streamed data regardless of the underlying protocol.\n\nI’ll continue to maintain **Propan** package, but new development will be in **FastStream**. If you are starting a new service, **FastStream** is the recommended way to do it.\n\nFor now **FastStream** supports **Kafka** and **RabbitMQ**. Other brokers support will be added in a few months.\n\nYou can find a detail migration guide in the [documentation](https://lancetnik.github.io/Propan/migration/)\n\n---\n\n**Documentation**: \u003ca href=\"https://lancetnik.github.io/Propan/\" target=\"_blank\"\u003ehttps://lancetnik.github.io/Propan/\u003c/a\u003e\n\n---\n\n### The key features are\n\n* **Simple**: Designed to be easy to use and learn.\n* **Intuitive**: Great editor support. Autocompletion everywhere.\n* [**Dependencies management**](#dependencies): Minimization of code duplication. Access to dependencies at any level of the call stack.\n* [**Integrations**](#http-frameworks-integrations): **Propan** is fully compatible with \u003ca href=\"https://lancetnik.github.io/Propan/integrations/1_integrations-index/\" target=\"_blank\"\u003eany HTTP framework\u003c/a\u003e you want\n* **MQ independent**: Single interface to popular MQ:\n  * **Redis** (based on \u003ca href=\"https://redis.readthedocs.io/en/stable/index.html\" target=\"_blank\"\u003eredis-py\u003c/a\u003e)\n  * **RabbitMQ** (based on \u003ca href=\"https://aio-pika.readthedocs.io/en/latest/\" target=\"_blank\"\u003eaio-pika\u003c/a\u003e)\n  * **Kafka** (based on \u003ca href=\"https://aiokafka.readthedocs.io/en/stable/\" target=\"_blank\"\u003eaiokafka\u003c/a\u003e)\n  * **SQS** (based on \u003ca href=\"https://aiobotocore.readthedocs.io/en/latest/\" target=\"_blank\"\u003eaiobotocore\u003c/a\u003e)\n  * **Nats** (based on \u003ca href=\"https://github.com/nats-io/nats.py\" target=\"_blank\"\u003enats-py\u003c/a\u003e)\n* \u003ca href=\"https://lancetnik.github.io/Propan/getting_started/4_broker/6_rpc/\" target=\"_blank\"\u003e**RPC**\u003c/a\u003e: The framework supports RPC requests over MQ, which will allow performing long operations on remote services asynchronously.\n* [**Great to develop**](#cli-power): CLI tool provides great development experience:\n  * framework-independent way to manage the project environment\n  * application code *hot reload*\n  * robust application templates\n* [**Documentation**](#project-documentation): **Propan** automatically generates and presents an interactive \u003ca href=\"https://www.asyncapi.com/\" target=\"_blank\"\u003e**AsyncAPI**\u003c/a\u003e documentation for your project\n* \u003ca href=\"https://lancetnik.github.io/Propan/getting_started/7_testing\" target=\"_blank\"\u003e**Testability**\u003c/a\u003e: **Propan** allows you to test your app without external dependencies: you do not have to set up a Message Broker, you can use a virtual one!\n\n### Supported MQ brokers\n\n|                   | async                                                   | sync                                        |\n|-------------------|:-------------------------------------------------------:|:-------------------------------------------:|\n| **RabbitMQ**      | :heavy_check_mark: **stable** :heavy_check_mark:        | :hammer_and_wrench: WIP :hammer_and_wrench: |\n| **Redis**         | :heavy_check_mark: **stable** :heavy_check_mark:        | :mag: planning :mag:                        |\n| **Nats**          | :heavy_check_mark: **stable** :heavy_check_mark:        | :mag: planning :mag:                        |\n| **Kafka**         | :warning: **beta** :warning:                            | :mag: planning :mag:                        |\n| **SQS**           | :warning: **beta** :warning:                            | :mag: planning :mag:                        |\n| **NatsJS**        | :warning: **beta** :warning:                            | :mag: planning :mag:                        |\n| **ZeroMQ**        | :hammer_and_wrench: WIP :hammer_and_wrench:             | :mag: planning :mag:                        |\n| **MQTT**          | :mag: planning :mag:                                    | :mag: planning :mag:                        |\n| **Redis Streams** | :mag: planning :mag:                                    | :mag: planning :mag:                        |\n| **Pulsar**        | :mag: planning :mag:                                    | :mag: planning :mag:                        |\n| **ActiveMQ**      | :mag: planning :mag:                                    | :mag: planning :mag:                        |\n| **AzureSB**       | :mag: planning :mag:                                    | :mag: planning :mag:                        |\n\n---\n\n### ⭐ Support the project ⭐\n\nIf you are interested in this project, please give me feedback by:\n\n- giving the [repository](https://github.com/Lancetnik/Propan) a star\n\n- tweet about \u003ca href=\"https://twitter.com/compose/tweet?text=I'm like @PropanFramework because... https://github.com/Lancetnik/Propan\" class=\"external-link\" target=\"_blank\"\u003e**Propan**\u003c/a\u003e and let me and others know why you use it\n\n- joining \u003ca href=\"https://discord.gg/qFm6aSqq59\" target=\"_blank\"\u003eDiscord server\u003c/a\u003e\n\nYour support helps me to stay in touch with you and encourages to\ncontinue developing and improving the library. Thank you for your\nsupport!\n\nReally, share information about this project with others. The bigger community we have - the better project will be!\n\n---\n\n## Declarative?\n\nWith declarative tools you can define **what you need to get**. With traditional imperative tools you must write **what you need to do**.\n\nTake a look at classic imperative tools, such as \u003ca href=\"https://aio-pika.readthedocs.io/en/latest/\" target=\"_blank\"\u003eaio-pika\u003c/a\u003e, \u003ca href=\"https://pika.readthedocs.io/en/stable/\" target=\"_blank\"\u003epika\u003c/a\u003e, \u003ca href=\"https://redis.readthedocs.io/en/stable/index.html\" target=\"_blank\"\u003eredis-py\u003c/a\u003e, \u003ca href=\"https://github.com/nats-io/nats.py\" target=\"_blank\"\u003enats-py\u003c/a\u003e, etc.\n\nThis is the **Quickstart** with the *aio-pika*:\n\n```python\nimport asyncio\nimport aio_pika\n\nasync def main():\n    connection = await aio_pika.connect_robust(\n        \"amqp://guest:guest@127.0.0.1/\"\n    )\n\n    queue_name = \"test_queue\"\n\n    async with connection:\n        channel = await connection.channel()\n\n        queue = await channel.declare_queue(queue_name)\n\n        async with queue.iterator() as queue_iter:\n            async for message in queue_iter:\n                async with message.process():\n                    print(message.body)\n\nasyncio.run(main())\n```\n\n**aio-pika** is a great tool with a really easy learning curve. But it's still imperative. You need to *connect*, declare *channel*, *queues*, *exchanges* by yourself. Also, you need to manage *connection*, *message*, *queue* context to avoid any troubles.\n\nIt is not a bad way, but it can be much easier.\n\n```python\nfrom propan import PropanApp, RabbitBroker\n\nbroker = RabbitBroker(\"amqp://guest:guest@localhost:5672/\")\napp = PropanApp(broker)\n\n@broker.handle(\"test_queue\")\nasync def base_handler(body):\n    print(body)\n```\n\nThis is the **Propan** declarative way to write the same code. That is so much easier, isn't it?\n\n---\n\n## Quickstart\n\nInstall using `pip`:\n\n```shell\npip install \"propan[async-rabbit]\"\n# or\npip install \"propan[async-nats]\"\n# or\npip install \"propan[async-redis]\"\n# or\npip install \"propan[async-kafka]\"\n# or\npip install \"propan[async-sqs]\"\n```\n\n### Basic usage\n\nCreate an application with the following code at `serve.py`:\n\n```python\nfrom propan import PropanApp\nfrom propan import RabbitBroker\n# from propan import RedisBroker\n# from propan import NatsBroker\n# from propan import SQSBroker\n# from propan import KafkaBroker\n\nbroker = RabbitBroker(\"amqp://guest:guest@localhost:5672/\")\n# broker = NatsBroker(\"nats://localhost:4222\")\n# broker = RedisBroker(\"redis://localhost:6379\")\n# broker = SQSBroker(\"http://localhost:9324\", ...)\n# broker = KafkaBroker(\"localhost:9092\")\n\napp = PropanApp(broker)\n\n@broker.handle(\"test\")\nasync def base_handler(body):\n    print(body)\n```\n\nAnd just run it:\n\n```shell\npropan run serve:app --workers 3\n```\n\n---\n\n## Type casting\n\nPropan uses `pydantic` to cast incoming function arguments to types according to their annotation.\n\n```python\nfrom pydantic import BaseModel\nfrom propan import PropanApp, RabbitBroker\n\nbroker = RabbitBroker(\"amqp://guest:guest@localhost:5672/\")\napp = PropanApp(broker)\n\nclass SimpleMessage(BaseModel):\n    key: int\n\n@broker.handle(\"test2\")\nasync def second_handler(body: SimpleMessage):\n    assert isinstance(body.key, int)\n\n```\n\n---\n\n## Dependencies\n\n**Propan** a has dependencies management policy close to `pytest fixtures` and `FastAPI Depends` at the same time.\nYou can specify in functions arguments which dependencies\nyou would to use. Framework passes them from the global Context object.\n\nAlso, you can specify your own dependencies, call dependencies functions and\n[more](https://github.com/Lancetnik/Propan/tree/main/examples/dependencies).\n\n```python\nfrom propan import PropanApp, RabbitBroker, Context, Depends\n\nrabbit_broker = RabbitBroker(\"amqp://guest:guest@localhost:5672/\")\napp = PropanApp(rabbit_broker)\n\nasync def dependency(user_id: int) -\u003e bool:\n    return True\n\n@rabbit_broker.handle(\"test\")\nasync def base_handler(user_id: int,\n                       dep: bool = Depends(dependency),\n                       broker: RabbitBroker = Context()):\n    assert dep is True\n    assert broker is rabbit_broker\n```\n\n---\n\n## RPC over MQ\n\nAlso, **Propan** allows you to use **RPC** requests over your broker with a simple way:\n\n```python\nfrom propan import PropanApp, RabbitBroker\n\nbroker = RabbitBroker(\"amqp://guest:guest@localhost:5672/\")\napp = PropanApp(rabbit_broker)\n\n@broker.handle(\"ping\")\nasync def base_handler():\n    return \"pong\"\n\n@app.after_startup\nasync def self_ping():\n    assert (\n        await broker.publish(\"\", \"ping\", callback=True)\n    ) == \"pong\"\n```\n\n---\n\n## Project Documentation\n\n**Propan** automatically generates documentation for your project according to the \u003ca href=\"https://www.asyncapi.com/\" target=\"_blank\"\u003e**AsyncAPI**\u003c/a\u003e specification. You can work with both generated artifacts and place a Web view of your documentation on resources available to related teams.\n\nThe availability of such documentation significantly simplifies the integration of services: you can immediately see what channels and message format the application works with. And most importantly, it doesn't cost you anything - **Propan** has already done everything for you!\n\n![HTML-page](https://lancetnik.github.io/Propan/assets/img/docs-html-short.png)\n\n---\n\n## CLI power\n\n**Propan** has its own CLI tool that provided the following features:\n\n* project generation\n* multiprocessing workers\n* project hot reloading\n* documentation generating and hosting\n* custom command line arguments passing\n\n### Context passing\n\nFor example: pass your current *.env* project setting to context\n\n```bash\npropan run serve:app --env=.env.dev\n```\n\n```python\nfrom propan import PropanApp, RabbitBroker\nfrom propan.annotations import ContextRepo\nfrom pydantic_settings import BaseSettings\n\nbroker = RabbitBroker()\n\napp = PropanApp(broker)\n\nclass Settings(BaseSettings):\n    url: str = \"amqp://guest:guest@localhost:5672/\"\n\n@app.on_startup\nasync def setup(env: str, context: ContextRepo):\n    settings = Settings(_env_file=env)\n    await broker.connect(settings.url)\n    context.set_global(\"settings\", settings)\n```\n\n### Project template\n\nAlso, **Propan CLI** is able to generate a production-ready application template:\n\n```bash\npropan create async rabbit [projectname]\n```\n\n*Notice: project template require* `pydantic[dotenv]` *installation.*\n\nRun the created project:\n\n```bash\n# Run rabbimq first\ndocker compose --file [projectname]/docker-compose.yaml up -d\n\n# Run project\npropan run [projectname].app.serve:app --env=.env --reload\n```\n\nNow you can enjoy a new development experience!\n\n---\n\n## HTTP Frameworks integrations\n\n### Any Framework\n\nYou can use **Propan** `MQBrokers` without `PropanApp`.\nJust *start* and *stop* them according to your application lifespan.\n\n```python\nfrom propan import NatsBroker\nfrom sanic import Sanic\n\napp = Sanic(\"MyHelloWorldApp\")\nbroker = NatsBroker(\"nats://localhost:4222\")\n\n@broker.handle(\"test\")\nasync def base_handler(body):\n    print(body)\n\n@app.after_server_start\nasync def start_broker(app, loop):\n    await broker.start()\n\n@app.after_server_stop\nasync def stop_broker(app, loop):\n    await broker.close()\n```\n\n### FastAPI Plugin\n\nAlso, **Propan** can be used as part of **FastAPI**.\n\nJust import a **PropanRouter** you need and declare the message handler\nusing the `@event` decorator. This decorator is similar to the decorator `@handle` for the corresponding brokers.\n\n```python\nfrom fastapi import Depends, FastAPI\nfrom pydantic import BaseModel\nfrom propan.fastapi import RabbitRouter\n\nrouter = RabbitRouter(\"amqp://guest:guest@localhost:5672\")\napp = FastAPI(lifespan=router.lifespan_context)\n\nclass Incoming(BaseModel):\n    username: str\n\ndef call():\n    return True\n\n@router.event(\"test\")\nasync def hello(m: Incoming, d = Depends(call)):\n    return { \"response\": f\"Hello, {m.username}!\" }\n\napp.include_router(router)\n```\n\n## Examples\n\nTo see more framework usages go to [**examples/**](https://github.com/Lancetnik/Propan/tree/main/examples)\n\n## Contributors\n\nThanks for all of these amazing peoples made the project better!\n\n\u003ca href=\"https://github.com/Lancetnik/Propan/graphs/contributors\"\u003e\n  \u003cimg src=\"https://contrib.rocks/image?repo=Lancetnik/Propan\"/\u003e\n\u003c/a\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flancetnik%2Fpropan","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flancetnik%2Fpropan","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flancetnik%2Fpropan/lists"}