Package: propan
PropanPropan- justan another one HTTPadeclarative Python Messaging Framework. It's inspired byFastAPIandKombu, simplify Message Brokers around code writing and provides a helpful development toolkit, which existed only in HTTP-frameworks world until now.It's designed to create reactive microservices aroundMessaging Architecture.It is a modern, high-level framework on top of popular specific Python brokers libraries, based onpydanticandFastAPI,pytestconcepts.:warning::warning::warning: Deprecation notice :warning::warning::warning:This project is superceeded byFastStream.FastStreamis a new package based on the ideas and experiences gained fromFastKafkaandPropan. 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.I’ll continue to maintainPropanpackage, but new development will be inFastStream. If you are starting a new service,FastStreamis the recommended way to do it.For nowFastStreamsupportsKafkaandRabbitMQ. Other brokers support will be added in a few months.You can find a detail migration guide in thedocumentationDocumentation:https://lancetnik.github.io/Propan/The key features areSimple: Designed to be easy to use and learn.Intuitive: Great editor support. Autocompletion everywhere.Dependencies management: Minimization of code duplication. Access to dependencies at any level of the call stack.Integrations:Propanis fully compatible withany HTTP frameworkyou wantMQ independent: Single interface to popular MQ:Redis(based onredis-py)RabbitMQ(based onaio-pika)Kafka(based onaiokafka)SQS(based onaiobotocore)Nats(based onnats-py)RPC: The framework supports RPC requests over MQ, which will allow performing long operations on remote services asynchronously.Great to develop: CLI tool provides great development experience:framework-independent way to manage the project environmentapplication codehot reloadrobust application templatesDocumentation:Propanautomatically generates and presents an interactiveAsyncAPIdocumentation for your projectTestability:Propanallows you to test your app without external dependencies: you do not have to set up a Message Broker, you can use a virtual one!Supported MQ brokersasyncsyncRabbitMQ:heavy_check_mark:stable:heavy_check_mark::hammer_and_wrench: WIP :hammer_and_wrench:Redis:heavy_check_mark:stable:heavy_check_mark::mag: planning :mag:Nats:heavy_check_mark:stable:heavy_check_mark::mag: planning :mag:Kafka:warning:beta:warning::mag: planning :mag:SQS:warning:beta:warning::mag: planning :mag:NatsJS:warning:beta:warning::mag: planning :mag:ZeroMQ:hammer_and_wrench: WIP :hammer_and_wrench::mag: planning :mag:MQTT:mag: planning :mag::mag: planning :mag:Redis Streams:mag: planning :mag::mag: planning :mag:Pulsar:mag: planning :mag::mag: planning :mag:ActiveMQ:mag: planning :mag::mag: planning :mag:AzureSB:mag: planning :mag::mag: planning :mag:⭐ Support the project ⭐If you are interested in this project, please give me feedback by:giving therepositorya startweet aboutPropanand let me and others know why you use itjoiningDiscord serverYour support helps me to stay in touch with you and encourages to
continue developing and improving the library. Thank you for your
support!Really, share information about this project with others. The bigger community we have - the better project will be!Declarative?With declarative tools you can definewhat you need to get. With traditional imperative tools you must writewhat you need to do.Take a look at classic imperative tools, such asaio-pika,pika,redis-py,nats-py, etc.This is theQuickstartwith theaio-pika:importasyncioimportaio_pikaasyncdefmain():connection=awaitaio_pika.connect_robust("amqp://guest:guest@127.0.0.1/")queue_name="test_queue"asyncwithconnection:channel=awaitconnection.channel()queue=awaitchannel.declare_queue(queue_name)asyncwithqueue.iterator()asqueue_iter:asyncformessageinqueue_iter:asyncwithmessage.process():print(message.body)asyncio.run(main())aio-pikais a great tool with a really easy learning curve. But it's still imperative. You need toconnect, declarechannel,queues,exchangesby yourself. Also, you need to manageconnection,message,queuecontext to avoid any troubles.It is not a bad way, but it can be much easier.frompropanimportPropanApp,RabbitBrokerbroker=RabbitBroker("amqp://guest:guest@localhost:5672/")app=PropanApp(broker)@broker.handle("test_queue")asyncdefbase_handler(body):print(body)This is thePropandeclarative way to write the same code. That is so much easier, isn't it?QuickstartInstall usingpip:pipinstall"propan[async-rabbit]"# orpipinstall"propan[async-nats]"# orpipinstall"propan[async-redis]"# orpipinstall"propan[async-kafka]"# orpipinstall"propan[async-sqs]"Basic usageCreate an application with the following code atserve.py:frompropanimportPropanAppfrompropanimportRabbitBroker# from propan import RedisBroker# from propan import NatsBroker# from propan import SQSBroker# from propan import KafkaBrokerbroker=RabbitBroker("amqp://guest:guest@localhost:5672/")# broker = NatsBroker("nats://localhost:4222")# broker = RedisBroker("redis://localhost:6379")# broker = SQSBroker("http://localhost:9324", ...)# broker = KafkaBroker("localhost:9092")app=PropanApp(broker)@broker.handle("test")asyncdefbase_handler(body):print(body)And just run it:propanrunserve:app--workers3Type castingPropan usespydanticto cast incoming function arguments to types according to their annotation.frompydanticimportBaseModelfrompropanimportPropanApp,RabbitBrokerbroker=RabbitBroker("amqp://guest:guest@localhost:5672/")app=PropanApp(broker)classSimpleMessage(BaseModel):key:int@broker.handle("test2")asyncdefsecond_handler(body:SimpleMessage):assertisinstance(body.key,int)DependenciesPropana has dependencies management policy close topytest fixturesandFastAPI Dependsat the same time.
You can specify in functions arguments which dependencies
you would to use. Framework passes them from the global Context object.Also, you can specify your own dependencies, call dependencies functions andmore.frompropanimportPropanApp,RabbitBroker,Context,Dependsrabbit_broker=RabbitBroker("amqp://guest:guest@localhost:5672/")app=PropanApp(rabbit_broker)asyncdefdependency(user_id:int)->bool:returnTrue@rabbit_broker.handle("test")asyncdefbase_handler(user_id:int,dep:bool=Depends(dependency),broker:RabbitBroker=Context()):assertdepisTrueassertbrokerisrabbit_brokerRPC over MQAlso,Propanallows you to useRPCrequests over your broker with a simple way:frompropanimportPropanApp,RabbitBrokerbroker=RabbitBroker("amqp://guest:guest@localhost:5672/")app=PropanApp(rabbit_broker)@broker.handle("ping")asyncdefbase_handler():return"pong"@app.after_startupasyncdefself_ping():assert(awaitbroker.publish("","ping",callback=True))=="pong"Project DocumentationPropanautomatically generates documentation for your project according to theAsyncAPIspecification. You can work with both generated artifacts and place a Web view of your documentation on resources available to related teams.The 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 -Propanhas already done everything for you!CLI powerPropanhas its own CLI tool that provided the following features:project generationmultiprocessing workersproject hot reloadingdocumentation generating and hostingcustom command line arguments passingContext passingFor example: pass your current.envproject setting to contextpropanrunserve:app--env=.env.devfrompropanimportPropanApp,RabbitBrokerfrompropan.annotationsimportContextRepofrompydantic_settingsimportBaseSettingsbroker=RabbitBroker()app=PropanApp(broker)classSettings(BaseSettings):url:str="amqp://guest:guest@localhost:5672/"@app.on_startupasyncdefsetup(env:str,context:ContextRepo):settings=Settings(_env_file=env)awaitbroker.connect(settings.url)context.set_global("settings",settings)Project templateAlso,Propan CLIis able to generate a production-ready application template:propancreateasyncrabbit[projectname]Notice: project template requirepydantic[dotenv]installation.Run the created project:# Run rabbimq firstdockercompose--file[projectname]/docker-compose.yamlup-d# Run projectpropanrun[projectname].app.serve:app--env=.env--reloadNow you can enjoy a new development experience!HTTP Frameworks integrationsAny FrameworkYou can usePropanMQBrokerswithoutPropanApp.
Juststartandstopthem according to your application lifespan.frompropanimportNatsBrokerfromsanicimportSanicapp=Sanic("MyHelloWorldApp")broker=NatsBroker("nats://localhost:4222")@broker.handle("test")asyncdefbase_handler(body):print(body)@app.after_server_startasyncdefstart_broker(app,loop):awaitbroker.start()@app.after_server_stopasyncdefstop_broker(app,loop):awaitbroker.close()FastAPI PluginAlso,Propancan be used as part ofFastAPI.Just import aPropanRouteryou need and declare the message handler
using the@eventdecorator. This decorator is similar to the decorator@handlefor the corresponding brokers.fromfastapiimportDepends,FastAPIfrompydanticimportBaseModelfrompropan.fastapiimportRabbitRouterrouter=RabbitRouter("amqp://guest:guest@localhost:5672")app=FastAPI(lifespan=router.lifespan_context)classIncoming(BaseModel):username:strdefcall():returnTrue@router.event("test")asyncdefhello(m:Incoming,d=Depends(call)):return{"response":f"Hello,{m.username}!"}app.include_router(router)ExamplesTo see more framework usages go toexamples/ContributorsThanks for all of these amazing peoples made the project better!