# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['ramqp', 'ramqp.mo']

package_data = \
{'': ['*']}

install_requires = \
['aio-pika>=8.3.0,<9.0.0',
 'anyio>=3.6.2,<4.0.0',
 'fastapi>=0.89.1,<0.90.0',
 'more-itertools>=9.0.0,<10.0.0',
 'prometheus-client>=0.16.0,<0.17.0',
 'pydantic>=1.10.4,<2.0.0',
 'ra-utils>=1.8.0,<2.0.0',
 'structlog>=22.3.0,<23.0.0']

setup_kwargs = {
    'name': 'ramqp',
    'version': '6.7.3',
    'description': 'Rammearkitektur AMQP library (aio_pika wrapper)',
    'long_description': '<!--\nSPDX-FileCopyrightText: 2021 Magenta ApS <https://magenta.dk>\nSPDX-License-Identifier: MPL-2.0\n-->\n\n# Rammearkitektur AMQP\nRammearkitektur AMQP (RAMQP) is an opinionated library for AMQP.\n\nIt is implemented as a thin wrapper around `aio_pika`, with a generic and a MO\nspecific AMQPSystem abstract, the MO abstraction being implementing using a thin\nwrapper around the generic abstraction.\n\n## Usage\n\n### Generic\nReceiving:\n\n```python\nimport asyncio\nfrom typing import Any\n\nfrom ramqp import AMQPSystem\nfrom ramqp import Router\nfrom ramqp.config import ConnectionSettings\n\nrouter = Router()\n\n# Configure the callback function to receive messages for the two routing keys.\n# If an exception is thrown from the function, the message is not acknowledged.\n# Thus, it will be retried immediately.\n@router.register("my.routing.key")\n@router.register("my.other.routing.key")\nasync def callback_function(routing_key: str, **kwargs: Any) -> None:\n    pass\n\n\nasync def main() -> None:\n    settings = ConnectionSettings(queue_prefix="my-program")\n    async with AMQPSystem(settings=settings, router=router) as amqp_system:\n        await amqp_system.run_forever()\n\n\nasyncio.run(main())\n```\n**NOTE**: `**kwargs` is required in all handlers for forward compatibility: the\nframework can add new keywords in the future, and existing handlers should\naccept them without breaking, if they do not use them. \nIt can be named `**_`  to prevent the "unused variable" warning by linters.\n\n\nSending:\n```python\nfrom ramqp import AMQPSystem\n\nwith AMQPSystem() as amqp_system:\n    await amqp_system.publish_message("my.routing.key", {"key": "value"})\n```\n\n### MO AMQP\nReceiving:\n\n```python\nimport asyncio\nfrom typing import Any\n\nfrom ramqp.config import ConnectionSettings\nfrom ramqp.mo import MOAMQPSystem\nfrom ramqp.mo import MORouter\nfrom ramqp.mo.models import MORoutingKey\nfrom ramqp.mo.models import ObjectType\nfrom ramqp.mo.models import PayloadType\nfrom ramqp.mo.models import RequestType\nfrom ramqp.mo.models import ServiceType\n\nrouter = MORouter()\n\n\n# Configure the callback function to receive messages for the two routing keys.\n# If an exception is thrown from the function, the message is not acknowledged.\n# Thus, it will be retried immediately.\n@router.register(ServiceType.EMPLOYEE, ObjectType.ADDRESS, RequestType.EDIT)\n@router.register("employee.it.create")\nasync def callback_function(\n    mo_routing_key: MORoutingKey, payload: PayloadType, **kwargs: Any\n) -> None:\n    pass\n\n\nasync def main() -> None:\n    settings = ConnectionSettings(queue_prefix="my-program")\n    async with MOAMQPSystem(settings=settings, router=router) as amqp_system:\n        await amqp_system.run_forever()\n\n\nasyncio.run(main())\n```\n\nSending:\n\n```python\nfrom datetime import datetime\nfrom uuid import uuid4\n\nfrom ramqp.mo import MOAMQPSystem\nfrom ramqp.mo.models import ObjectType\nfrom ramqp.mo.models import PayloadType\nfrom ramqp.mo.models import RequestType\nfrom ramqp.mo.models import ServiceType\n\npayload = PayloadType(uuid=uuid4(), object_uuid=uuid4(), time=datetime.now())\n\nasync with MOAMQPSystem() as amqp_system:\n    await amqp_system.publish_message(\n        ServiceType.EMPLOYEE, ObjectType.ADDRESS, RequestType.EDIT, payload\n    )\n```\n\n### FastAPI and Additional Context\nThe `run_forever()` function is not very useful if you would like to serve an\nAPI at the same time. To solve this issue, the main entrypoint of the program is\nthe ASGI application, with the AMQP system tied to its lifespan. The example\nbelow also shows how additional context can be injected into the AMQP system,\nand subsequently retrieved in the handlers:\n\n```python\nfrom contextlib import asynccontextmanager\nfrom functools import partial\nfrom typing import Any\n\nfrom fastapi import APIRouter\nfrom fastapi import FastAPI\nfrom starlette.requests import Request\n\nfrom ramqp.config import ConnectionSettings\nfrom ramqp.mo import MOAMQPSystem\nfrom ramqp.mo import MORouter\n\namqp_router = MORouter()\nfastapi_router = APIRouter()\n\n\n@amqp_router.register("employee.it.create")\nasync def callback_function(context: dict, **kwargs: Any) -> None:\n    print(context["greeting"])  # Hello, world!\n\n\n@fastapi_router.get("/hello")\nasync def hello(request: Request) -> None:\n    # Passing the \'amqp_system\' in the context would allow FastAPI to actually publish\n    # this greeting to the whole world.\n    return request.app.state.context["greeting"]  # Hello, world!\n\n\ndef create_app() -> FastAPI:\n    context = {"greeting": "Hello, world!"}\n\n    app = FastAPI()\n    app.include_router(fastapi_router)\n    app.state.context = context  # share context with FastAPI\n\n    settings = ConnectionSettings(queue_prefix="my-program")\n    amqp_system = MOAMQPSystem(\n        settings=settings,\n        router=amqp_router,\n        context=context,  # share context with the AMQP system\n    )\n\n    app.router.lifespan_context = partial(lifespan, context, amqp_system)\n    return app\n\n\n@asynccontextmanager\nasync def lifespan(context: dict, amqp_system: MOAMQPSystem, app: FastAPI):\n    # Add to the context as required\n\n    async with amqp_system:\n        # Yield to keep the AMQP system open until the ASGI application is closed.\n        # Control will be returned to here when the ASGI application is shut down.\n        # Consider using AsyncExitStack in case of multiple context managers.\n        yield\n```\nSave the example as `example.py` and try it with\n`uvicorn --factory example:create_app`.\n\n\n### Metrics\nRAMQP exports a myraid of prometheus metrics via `prometheus/client_python`.\n\nThese can be exported using:\n```\nfrom prometheus_client import start_http_server\n\nstart_http_server(8000)\n```\nOr similar, see the promethues client library for details.\n\n\n## Development\n\n### Prerequisites\n- [Poetry](https://github.com/python-poetry/poetry)\n\n### Getting Started\n1. Clone the repository:\n```\ngit clone git@git.magenta.dk:rammearkitektur/ramqp.git\n```\n\n2. Install all dependencies:\n```\npoetry install\n```\n\n3. Set up pre-commit:\n```\npoetry run pre-commit install\n```\n\n### Running the tests\nYou use `poetry` and `pytest` to run the tests:\n\n`poetry run pytest`\n\nYou can also run specific files\n\n`poetry run pytest tests/<test_folder>/<test_file.py>`\n\nand even use filtering with `-k`\n\n`poetry run pytest -k "Manager"`\n\nYou can use the flags `-vx` where `v` prints the test & `x` makes the test stop if any tests fails (Verbose, X-fail)\n\n#### Running the integration tests\nTo run the integration tests, an AMQP instance must be available.\n\nIf an instance is already available, it can be used by configuring the `AMQP_URL`\nenvironmental variable. Alternatively a RabbitMQ can be started in docker, using:\n```\ndocker run -d -p 5672:5672 -p 15672:15672 rabbitmq:3-management\n```\n\n## Versioning\nThis project uses [Semantic Versioning](https://semver.org/) with the following strategy:\n- MAJOR: Incompatible changes to existing data models\n- MINOR: Backwards compatible updates to existing data models OR new models added\n- PATCH: Backwards compatible bug fixes\n\n\n## Authors\nMagenta ApS <https://magenta.dk>\n\n\n## License\nThis project uses: [MPL-2.0](MPL-2.0.txt)\n\nThis project uses [REUSE](https://reuse.software) for licensing.\nAll licenses can be found in the [LICENSES folder](LICENSES/) of the project.\n',
    'author': 'Magenta ApS',
    'author_email': 'info@magenta.dk',
    'maintainer': 'None',
    'maintainer_email': 'None',
    'url': 'https://magenta.dk/',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.10,<4.0',
}


setup(**setup_kwargs)
