Package: ramqp
Rammearkitektur AMQPRammearkitektur AMQP (RAMQP) is an opinionated library for AMQP.It is implemented as a thin wrapper aroundaio_pika, with a generic and a MO
specific AMQPSystem abstract, the MO abstraction being implementing using a thin
wrapper around the generic abstraction.UsageGenericReceiving:importasynciofromramqpimportAMQPSystemfromramqpimportRouterfromramqp.configimportAMQPConnectionSettingsfromramqp.dependsimportRoutingKeyrouter=Router()# Configure the callback function to receive messages for the two routing keys.# If an exception is thrown from the function, the message is not acknowledged.# Thus, it will be retried immediately.@router.register("my.routing.key")@router.register("my.other.routing.key")asyncdefcallback_function(routing_key:RoutingKey)->None:passasyncdefmain()->None:settings=AMQPConnectionSettings(url=...,queue_prefix="my-program")asyncwithAMQPSystem(settings=settings,router=router)asamqp_system:awaitamqp_system.run_forever()asyncio.run(main())Sending:fromramqpimportAMQPSystemwithAMQPSystem(...)asamqp_system:awaitamqp_system.publish_message("my.routing.key",{"key":"value"})Dependency InjectionThe callback handlers supportFastAPI dependency injection.
This allows handlers to request exactly the data that they need, as seen with
FastAPI dependencies or PyTest fixtures. A callback may look like:fromramqp.moimportMORoutingKeyfromramqp.moimportPayloadTypeasyncdefcallback(mo_routing_key:MORoutingKey,payload:PayloadType):...Experienced FastAPI developers might wonder how this works without theDependsfunction. Indeed, this less verbose pattern wasintroduced in FastAPI v0.95,
and works by defining the dependency directly on the type using theAnnotatedmechanism fromPEP593. For example:MORoutingKey=Annotated[MORoutingKey,Depends(get_routing_key)]PayloadType=Annotated[PayloadType,Depends(get_payload_as_type(PayloadType))]whereby the previous example is equivalent toasyncdefcallback(mo_routing_key:MORoutingKey=Depends(get_routing_key),payload:PayloadType=Depends(get_payload_as_type(PayloadType))):....Reference documentation should be made available for these types in the future,
but for now they can be found mainly inramqp/depends.pyandramqp/mo.py.ContextimportasynciofromtypingimportAnnotatedimporthttpxfromfastapiimportDependsfromramqpimportAMQPSystemfromramqpimportRouterfromramqp.dependsimportContextfromramqp.dependsimportfrom_contextrouter=Router()asyncdefmain()->None:asyncwithhttpx.AsyncClient()asclient:context={"client":client,}asyncwithAMQPSystem(...,context=context)asamqp_system:awaitamqp_system.run_forever()HTTPXClient=Annotated[httpx.AsyncClient,Depends(from_context("client"))]@router.register("my.routing.key")asyncdefcallback_function(context:Context,client:HTTPXClient)->None:passasyncio.run(main())SettingsIn most cases,AMQPConnectionSettingsis probably initialised by being
included in theBaseSettingsof the application using the library. Theurlparameter of theAMQPConnectionSettingsobject can be given as a single URL
string or as individual structured fields. Consider the following:frompydanticimportBaseSettingsfromramqp.configimportAMQPConnectionSettings# BaseSettings makes the entire model initialisable using environment variablesclassSettings(BaseSettings):amqp:AMQPConnectionSettingsclassConfig:env_nested_delimiter="__"# allows setting e.g. AMQP__URL__HOST=foosettings=Settings()The above would work with either multiple structured environment variablesAMQP__URL__SCHEME=amqp
AMQP__URL__USER=guest
AMQP__URL__PASSWORD=guest
AMQP__URL__HOST=msg_broker
AMQP__URL__PORT=5672
AMQP__URL__VHOST=os2moor a single URL definitionAMQP__URL=amqp://guest:guest@msg_broker:5672/os2moMO AMQPReceiving:importasynciofromramqp.configimportAMQPConnectionSettingsfromramqp.moimportMOAMQPSystemfromramqp.moimportMORouterfromramqp.moimportMORoutingKeyfromramqp.moimportPayloadTyperouter=MORouter()# Configure the callback function to receive messages for the two routing keys.# If an exception is thrown from the function, the message is not acknowledged.# Thus, it will be retried immediately.@router.register("employee.address.edit")@router.register("employee.it.create")asyncdefcallback_function(mo_routing_key:MORoutingKey,payload:PayloadType)->None:passasyncdefmain()->None:settings=AMQPConnectionSettings(url=...,queue_prefix="my-program")asyncwithMOAMQPSystem(settings=settings,router=router)asamqp_system:awaitamqp_system.run_forever()asyncio.run(main())Sending:fromdatetimeimportdatetimefromuuidimportuuid4fromramqp.moimportMOAMQPSystemfromramqp.moimportPayloadTypepayload=PayloadType(uuid=uuid4(),object_uuid=uuid4(),time=datetime.now())asyncwithMOAMQPSystem(...)asamqp_system:awaitamqp_system.publish_message("employee.address.edit",payload)MetricsRAMQP exports a myriad of prometheus metrics viaprometheus/client_python.These can be exported using:from prometheus_client import start_http_server

start_http_server(8000)Or similar, see the promethues client library for details.DevelopmentPrerequisitesPoetryGetting StartedClone the repository:git clone git@git.magenta.dk:rammearkitektur/ramqp.gitInstall all dependencies:poetry installSet up pre-commit:poetry run pre-commit installRunning the testsYou usepoetryandpytestto run the tests:poetry run pytestYou can also run specific filespoetry run pytest tests/<test_folder>/<test_file.py>and even use filtering with-kpoetry run pytest -k "Manager"You can use the flags-vxwherevprints the test &xmakes the test stop if any tests fails (Verbose, X-fail)Running the integration testsTo run the integration tests, an AMQP instance must be available.If an instance is already available, it can be used by configuring theAMQP_URLenvironmental variable. Alternatively a RabbitMQ can be started in docker, using:docker run -d -p 5672:5672 -p 15672:15672 rabbitmq:3-managementVersioningThis project usesSemantic Versioningwith the following strategy:MAJOR: Incompatible changes to existing data modelsMINOR: Backwards compatible updates to existing data models OR new models addedPATCH: Backwards compatible bug fixesAuthorsMagenta ApShttps://magenta.dkLicenseThis project uses:MPL-2.0This project usesREUSEfor licensing.
All licenses can be found in theLICENSES folderof the project.