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

packages = \
['tomodachi',
 'tomodachi.cli',
 'tomodachi.discovery',
 'tomodachi.envelope',
 'tomodachi.envelope.proto_build',
 'tomodachi.envelope.proto_build.protobuf',
 'tomodachi.helpers',
 'tomodachi.invoker',
 'tomodachi.protocol',
 'tomodachi.run',
 'tomodachi.transport',
 'tomodachi.validation']

package_data = \
{'': ['*'], 'tomodachi.envelope': ['protobuf/*']}

install_requires = \
['aioamqp>=0.13.0,<0.15.0',
 'aiobotocore>=0.10.4,<2.0.0',
 'aiohttp>=3.5.4,<3.8.0',
 'asahi>=0.1.0,<0.2.0',
 'botocore>=1.12.252',
 'cchardet>=2.1.6',
 'colorama>=0.3.9,<0.5.0',
 'pytz',
 'tzlocal>=1.4,<3.0']

extras_require = \
{':python_version < "3.8"': ['typing-extensions>=3.7.4'],
 'aiodns': ['aiodns>=1.2.0'],
 'amqp': ['asahi-extras'],
 'aws': ['asahi-extras'],
 'brotli': ['Brotli>=1.0.9,<2.0.0'],
 'color': ['asahi-extras'],
 'http': ['asahi-extras'],
 'protobuf': ['protobuf>=3.13.0'],
 'scheduler': ['asahi-extras'],
 'uvloop': ['uvloop>=0.14.0,<1.0.0']}

entry_points = \
{'console_scripts': ['tomodachi = tomodachi.cli:cli_entrypoint']}

setup_kwargs = {
    'name': 'tomodachi',
    'version': '0.21.6',
    'description': 'Microservice library on asyncio - HTTP server, websockets, pub/sub messaging for AWS SNS+SQS and RabbitMQ',
    'long_description': '``tomodachi`` - a lightweight microservices library on Python asyncio\n=====================================================================\n  A Python 3 microservice library / framework using ``asyncio`` (async / await) with\n  HTTP, websockets, RabbitMQ / AMQP and AWS SNS+SQS built-in support for event based\n  messaging and intra-service communication.\n\n.. image:: https://github.com/kalaspuff/tomodachi/workflows/Python%20package/badge.svg\n    :target: https://github.com/kalaspuff/tomodachi/actions\n.. image:: https://img.shields.io/pypi/v/tomodachi.svg\n    :target: https://pypi.python.org/pypi/tomodachi\n.. image:: https://codecov.io/gh/kalaspuff/tomodachi/branch/master/graph/badge.svg\n    :target: https://codecov.io/gh/kalaspuff/tomodachi\n.. image:: https://img.shields.io/pypi/pyversions/tomodachi.svg\n    :target: https://pypi.python.org/pypi/tomodachi\n\nTomodachi is a tiny framework designed to build fast microservices listening on\nHTTP or communicating over event driven message buses like RabbitMQ, AMQP,\nAWS (Amazon Web Services) SNS+SQS, etc. It\'s designed to be extendable to make\nuse of any type of transport layer available.\n\n*Tomodachi* [**友達**] *means friends – a suitable name for microservices working\ntogether.* 😻 👬 👭 👫 😻\n\n\nProject documentation\n---------------------\n\n- Getting started / installation: https://tomodachi.dev/docs\n\n- Example code: https://tomodachi.dev/docs/examples\n\n- Endpoint built-ins:\n\n  + HTTP endpoints: https://tomodachi.dev/docs/http\n\n  + AWS SNS+SQS event messaging: https://tomodachi.dev/docs/aws-sns-sqs\n\n  + AMQP messaging (RabbitMQ): https://tomodachi.dev/docs/amqp-rabbitmq\n\n  + Scheduled functions and cron: https://tomodachi.dev/docs/scheduled-functions-cron\n\n- Options and configuration parameters: https://tomodachi.dev/docs/options\n\n- FAQ: https://tomodachi.dev/docs/faq\n\n.. image:: https://img.shields.io/badge/tomodachi.dev-documentation-ff69b4\n    :target: https://tomodachi.dev/docs/getting-started\n\n\nUsage\n-----\n``tomodachi`` is used to execute service code via command line interface or within\ncontainer images.\n\n.. code::\n\n    Usage: tomodachi <command> [options] [arguments]\n\n    Options:\n      -h, --help                                Show this help message and exit\n      -v, --version                             Print tomodachi version\n      --dependency-versions                     Print versions of dependencies\n\n    Available commands:\n      ---\n      Command: run\n      Starts service(s) defined in the .py files specified as <service> argument(s)\n\n      $ tomodachi run <service ...> [-c <config-file ...>] [--production]\n      | --loop [auto|asyncio|uvloop]            Event loop implementation [asyncio]\n      | --production                            Disable restart on file changes\n      | -c, --config <files>                    Use configuration from JSON files\n      | -l, --log <level>, --log-level <level>  Specify log level\n\n\n.. image:: https://raw.githubusercontent.com/kalaspuff/tomodachi/master/docs/assets/microservice-in-30-seconds-white.gif\n\n``README``\n==========\n\n*This documentation README includes a guide of how to get started with services,\nwhat built-in  functionality exists in this library, lists of available configuration\nparameters and a few examples of service code.*\n\n**Use https://tomodachi.dev/docs for extensive project documentation.**\n\n----\n\n| **Please note: this library is a work in progress.**\n\nConsider `tomodachi` as beta software. `tomodachi` is still an experimental\nproject with an unregular release schedule. The package is not yet available\nas `1.0.0` and there may be breaking changes between `0.x` versions.\n\n----\n\nGetting started 🏃\n------------------\n\nFirst off – installation using ``poetry`` is fully supported and battle-tested (``pip`` works just as fine)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nInstall ``tomodachi`` in your preferred way, wether it be ``poetry``, ``pip``,\n``pipenv``, etc. Installing the distribution will give your environment access to the\n``tomodachi`` package for imports as well as a shortcut to the CLI alias, which\nlater is used to run the microservices you build.\n\n.. code:: bash\n\n    local ~$ pip install tomodachi\n    > ...\n    > Installing collected packages: ..., ..., ..., tomodachi\n    > Successfully installed ... ... ... tomodachi-x.x.xx\n\n    local ~$ tomodachi --version\n    > tomodachi x.xx.xx\n\n\nProbably goes without saying – services you build, their dependencies,\ntogether with runtime utilities like this one, should preferably always be\ninstalled and run in isolated environments like Docker containers or virtual\nenvironments.\n\n\nBuilding blocks for a service class and microservice entrypoint\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n1. ``import tomodachi`` and create a class that inherits ``tomodachi.Service``,\n   it can be called anything… or just ``Service`` to keep it simple.\n2. Add a ``name`` attribute to the class and give it a string value. Having\n   a ``name`` attribute isn\'t required, but good practice.\n3. Define an awaitable function in the service class – in this example we\'ll\n   use it as an entrypoint to trigger code in the service by decorating it\n   with one of the available invoker decorators. Note that a service class\n   must have at least one decorated function available to even be recognized\n   as a service by ``tomodachi run``.\n4. Decide on how to trigger the function – for example using HTTP, pub/sub\n   or on a timed interval, then decorate your function with one of these\n   trigger / subscription decorators, which also invokes what capabilities\n   the service initially has.\n\n\n*Further down you\'ll find a desciption of how each of the built-in invoker decorators\nwork and which keywords and parameters you can use to change their behaviour.*\n\n*Note: Publishing and subscribing to events and messages may require user credentials\nor hosting configuration to be able to access queues and topics.*\n\n\n**For simplicity, let\'s do HTTP:**\n\n* On each POST request to ``/sheep``, the service will wait for up to one whole second\n  (pretend that it\'s performing I/O – waiting for response on a slow sheep counting\n  database modification, for example) and then issue a 200 OK with some data.\n* It\'s also possible to query the amount of times the POST tasks has run by doing a\n  ``GET`` request to the same url, ``/sheep``.\n* By using ``@tomodachi.http`` an HTTP server backed by ``aiohttp`` will be started\n  on service start. ``tomodachi`` will act as a middleware to route requests to the\n  correct handlers, upgrade websocket connections and then also gracefully await\n  connections with still executing tasks, when the service is asked to stop – up until\n  a configurable amount of time has passed.\n\n\n.. code:: python\n\n    import asyncio\n    import random\n\n    import tomodachi\n\n\n    class Service(tomodachi.Service):\n        name = "sleepy-sheep-counter"\n\n        _sheep_count = 0\n\n        @tomodachi.http("POST", r"/sheep")\n        async def add_to_sheep_count(self, request):\n            await asyncio.sleep(random.random())\n            self._sheep_count += 1\n            return 200, str(self._sheep_count)\n\n        @tomodachi.http("GET", r"/sheep")\n        async def return_sheep_count(self, request):\n            return 200, str(self._sheep_count)\n\n\nRun services with:\n\n.. code:: bash\n\n \xa0  local ~/code/service$ tomodachi run <path to .py file with service class code>\n\n\n----\n\nBeside the currently existing built-in ways of interfacing with a service, it\'s\npossible to build additional function decorators to suit the use-cases one may have.\n\nTo give a few possible examples / ideas of functionality that could be coded to call\nfunctions with data in similar ways:\n\n* Using Redis as a task queue with configurable keys to push or pop onto.\n* Subscribing to Kinesis or Kafka event streams and act on the data received.\n* An abstraction around otherwise complex functionality or to unify API design.\n* As an example to above sentence; GraphQL resolver functionality with built-in\n  tracability and authentication management, with a unified API to application devs.\n\n\n----\n\nAdditional examples will follow with different ways to trigger functions in the service.\n----------------------------------------------------------------------------------------\n\nOf course the different ways can be used within the same class, for example\nthe very common use-case of having a service listening on HTTP while also performing\nsome kind of async pub/sub tasks.\n\n\nBasic HTTP based service 🌟\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\nCode for a simple service which would service data over HTTP, pretty similar, but with a few more concepts added.\n\n.. code:: python\n\n    import tomodachi\n\n\n    class Service(tomodachi.Service):\n        name = "http-example"\n\n        # Request paths are specified as regex for full flexibility\n        @tomodachi.http("GET", r"/resource/(?P<id>[^/]+?)/?")\n        async def resource(self, request, id):\n            # Returning a string value normally means 200 OK\n            return f"id = {id}"\n\n        @tomodachi.http("GET", r"/health")\n        async def health_check(self, request):\n            # Return can also be a tuple, dict or even an aiohttp.web.Response\n            # object for more complex responses - for example if you need to\n            # send byte data, set your own status code or define own headers\n            return {\n                "body": "Healthy",\n                "status": 200,\n            }\n\n        # Specify custom 404 catch-all response\n        @tomodachi.http_error(status_code=404)\n        async def error_404(self, request):\n            return "error 404"\n\n\nRabbitMQ or AWS SNS+SQS event based messaging service 🐰\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nExample of a service that calls a function when messages are published on an AMQP topic exchange.\n\n.. code:: python\n\n    import tomodachi\n\n\n    class Service(tomodachi.Service):\n        name = "amqp-example"\n\n        # The "message_envelope" attribute can be set on the service class to build / parse data.\n        # message_envelope = ...\n\n        # A route / topic on which the service will subscribe to via RabbitMQ / AMQP\n        @tomodachi.amqp("example.topic")\n        async def example_func(self, message):\n            # Received message, fordarding the same message as response on another route / topic\n            await tomodachi.amqp_publish(self, message, routing_key="example.response")\n\n\nAWS SNS+SQS event based messaging service 📡\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nExample of a service using AWS SNS+SQS managed pub/sub messaging. AWS SNS and AWS SQS together\nbrings managed message queues for microservices, distributed systems, and serverless applications hosted\non AWS. ``tomodachi`` services can customize their enveloping functionality to both unwrap incoming messages\nand/or to produce enveloped messages for published events / messages. Pub/sub patterns are great for\nscalability in distributed architectures, when for example hosted in Docker on Kubernetes.\n\n.. code:: python\n\n    import tomodachi\n\n\n    class Service(tomodachi.Service):\n        name = "aws-example"\n\n        # The "message_envelope" attribute can be set on the service class to build / parse data.\n        # message_envelope = ...\n\n        # Using the @tomodachi.aws_sns_sqs decorator to make the service create an AWS SNS topic,\n        # an AWS SQS queue and to make a subscription from the topic to the queue as well as start\n        # receive messages from the queue using SQS.ReceiveMessages.\n        @tomodachi.aws_sns_sqs("example-topic", queue_name="example-queue")\n        async def example_func(self, message):\n            # Received message, forwarding the same message as response on another topic\n            await tomodachi.aws_sns_sqs_publish(self, message, topic="another-example-topic")\n\n\nScheduling, inter-communication between services, etc. ⚡️\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nThere are other examples available with code of how to use services with self-invoking\nmethods called on a specified interval or at specific times / days, as well as additional examples\nfor inter-communication pub/sub between different services on both AMQP or AWS SNS+SQS as shown\nabove. See more at the `examples folder <https://github.com/kalaspuff/tomodachi/blob/master/examples/>`_.\n\n\n----\n\nRun the service 😎\n------------------\n.. code:: bash\n\n \xa0  # cli alias is set up automatically on installation\n \xa0  local ~/code/service$ tomodachi run service.py\n\n    # shortcut to cli endpoint could be used if cloned from repo and not yet installed\n    local ~/code/tomodachi$ python tomodachi.py run example/http_simple_service.py\n\n\n*Defaults to output information on stdout.*\n\n.. code:: bash\n\n \xa0  local ~/code/service$ tomodachi run service.py\n    >\n    > ---\n    > Starting tomodachi services (pid: 1) ...\n    > * service.py\n    >\n    > Current version: tomodachi x.x.xx on Python 3.x.x\n    > Event loop implementation: asyncio\n    > Local time: October 04, 2020 - 13:38:01,201509 UTC\n    > Timestamp in UTC: 2020-10-04T13:38:01.201509Z\n    >\n    > File watcher is active - code changes will automatically restart services\n    > Quit running services with <ctrl+c>\n    >\n    > 2020-10-04 13:38:01,234 (services.service): Initializing service "example" [id: <uuid>]\n    > 2020-10-04 13:38:01,248 (transport.http): Listening [http] on http://127.0.0.1:9700/\n    > 2020-10-04 13:38:01,248 (services.service): Started service "example" [id: <uuid>]\n\n\n*HTTP service acts like a normal web server.*\n\n.. code:: bash\n\n    local ~$ curl -v "http://127.0.0.1:9700/resource/1234"\n    > HTTP/1.1 200 OK\n    > Content-Type: text/plain; charset=utf-8\n    > Server: tomodachi\n    > Content-Length: 9\n    > Date: Mon, 02 Oct 2017 13:38:02 GMT\n    >\n    > id = 1234\n\n\nExample of a microservice containerized in Docker 🐳\n----------------------------------------------------\nA great way to distribute and operate microservices are usually to run them in containers or\neven more interestingly, in clusters of compute nodes. Here follows an example of getting a\n``tomodachi`` based service up and running in Docker.\n\nWe\'re building the service\' container image using just two small files, the ``Dockerfile`` and\nthe actual code for the microservice, ``service.py``. In reality a service would probably not be\nquite this small, but as a template to get started.\n\n**Dockerfile**\n\n.. code:: dockerfile\n\n    FROM python:3.8-slim\n    RUN pip install tomodachi\n    RUN mkdir /app\n    WORKDIR /app\n    COPY service.py .\n    ENV PYTHONUNBUFFERED=1\n    CMD ["tomodachi", "run", "service.py", "--production"]\n\n**service.py**\n\n.. code:: python\n\n    import json\n\n    import tomodachi\n\n\n    class Service(tomodachi.Service):\n        name = "example"\n        options = {\n            "http.port": 80,\n            "http.content_type": "application/json; charset=utf-8",\n        }\n\n        _healthy = True\n\n        @tomodachi.http("GET", r"/")\n        async def index_endpoint(self, request):\n            # tomodachi.get_execution_context() can be used for\n            # debugging purposes or to add additional service context\n            # in logs or alerts.\n            execution_context = tomodachi.get_execution_context()\n\n            return json.dumps({\n                "data": "hello world!",\n                "execution_context": execution_context,\n            })\n\n        @tomodachi.http("GET", r"/health/?", ignore_logging=True)\n        async def health_check(self, request):\n            if self._healthy:\n                return 200, json.dumps({"status": "healthy"})\n            else:\n                return 503, json.dumps({"status": "not healthy"})\n\n        @tomodachi.http_error(status_code=400)\n        async def error_400(self, request):\n            return json.dumps({"error": "bad-request"})\n\n        @tomodachi.http_error(status_code=404)\n        async def error_404(self, request):\n            return json.dumps({"error": "not-found"})\n\n        @tomodachi.http_error(status_code=405)\n        async def error_405(self, request):\n            return json.dumps({"error": "method-not-allowed"})\n\nBuilding and running the container, forwarding host\'s port 31337 to port 80.\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code:: bash\n\n \xa0  local ~/code/service$ docker build . -t tomodachi-microservice\n    > Sending build context to Docker daemon  9.216kB\n    > Step 1/7 : FROM python:3.10-slim\n    > 3.8-slim: Pulling from library/python\n    > ...\n    >  ---> 3f7f3ab065d4\n    > Step 7/7 : CMD ["tomodachi", "run", "service.py", "--production"]\n    >  ---> Running in b8dfa9deb243\n    > Removing intermediate container b8dfa9deb243\n    >  ---> 8f09a3614da3\n    > Successfully built 8f09a3614da3\n    > Successfully tagged tomodachi-microservice:latest\n\n.. code:: bash\n\n \xa0  local ~/code/service$ docker run -ti -p 31337:80 tomodachi-microservice\n    > 2020-10-04 13:38:01,234 (services.service): Initializing service "example" [id: <uuid>]\n    > 2020-10-04 13:38:01,248 (transport.http): Listening [http] on http://127.0.0.1:80/\n    > 2020-10-04 13:38:01,248 (services.service): Started service "example" [id: <uuid>]\n\nMaking requests to the running container.\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code:: bash\n\n    local ~$ curl http://127.0.0.1:31337/ | jq\n    > {\n    >   "data": "hello world!",\n    >   "execution_context": {\n    >     "tomodachi_version": "x.x.xx",\n    >     "python_version": "3.x.x",\n    >     "system_platform": "Linux",\n    >     "process_id": 1,\n    >     "init_timestamp": "2020-10-04T13:38:01.201509Z",\n    >     "event_loop": "asyncio",\n    >     "http_enabled": true,\n    >     "http_current_tasks": 1,\n    >     "http_total_tasks": 1,\n    >     "aiohttp_version": "x.x.xx"\n    >   }\n    > }\n\n    local ~$ curl http://127.0.0.1:31337/health -i\n    > HTTP/1.1 200 OK\n    > Content-Type: application/json; charset=utf-8\n    > Server: tomodachi\n    > Content-Length: 21\n    > Date: Sun, 04 Oct 2020 13:40:44 GMT\n    >\n    > {"status": "healthy"}\n\n    local ~$ curl http://127.0.0.1:31337/no-route -i\n    > HTTP/1.1 404 Not Found\n    > Content-Type: application/json; charset=utf-8\n    > Server: tomodachi\n    > Content-Length: 22\n    > Date: Sun, 04 Oct 2020 13:41:18 GMT\n    >\n    > {"error": "not-found"}\n\n\n**It\'s actually as easy as that to get something spinning. The hard part is usually to figure out (or decide) what to build next.**\n\nOther popular ways of running microservices are of course to use them as serverless\nfunctions, with an ability of scaling to zero (Lambda, Cloud Functions, Knative, etc.\nmay come to mind). Currently ``tomodachi`` works best in a container setup and until\nproper serverless supporting execution context is available in the library, it\nshould be adviced to hold off and use other tech for those kinds of deployments.\n\n----\n\nAvailable built-ins used as endpoints 🚀\n========================================\nAs shown, there\'s different ways to trigger your microservice function in which the most common ones are either directly via HTTP or via event based messaging (for example AMQP or AWS SNS+SQS). Here\'s a list of the currently available built-ins you may use to decorate your service functions.\n\nHTTP endpoints:\n---------------\n``@tomodachi.http(method, url, ignore_logging=[200])``\n  Sets up an **HTTP endpoint** for the specified ``method`` (``GET``, ``PUT``, ``POST``, ``DELETE``) on the regexp ``url``.\n  Optionally specify ``ignore_logging`` as a dict or tuple containing the status codes you do not wish to log the access of. Can also be set to ``True`` to ignore everything except status code 500.\n\n``@tomodachi.http_static(path, url)``\n  Sets up an **HTTP endpoint for static content** available as ``GET`` / ``HEAD`` from the ``path`` on disk on the base regexp ``url``.\n\n``@tomodachi.websocket(url)``\n  Sets up a **websocket endpoint** on the regexp ``url``. The invoked function is called upon websocket connection and should return a two value tuple containing callables for a function receiving frames (first callable) and a function called on websocket close (second callable). The passed arguments to the function beside the class object is first the ``websocket`` response connection which can be used to send frames to the client, and optionally also the ``request`` object.\n\n``@tomodachi.http_error(status_code)``\n  A function which will be called if the **HTTP request would result in a 4XX** ``status_code``. You may use this for example to set up a custom handler on "404 Not Found" or "403 Forbidden" responses.\n\n\nAWS SNS+SQS messaging:\n----------------------\n``@tomodachi.aws_sns_sqs(topic=None, competing=True, queue_name=None, filter_policy=None, **kwargs)``\n  This would set up an **AWS SQS queue**, subscribing to messages on the **AWS SNS topic** ``topic`` (if a ``topic`` is specified), whereafter it will start consuming messages from the queue.\n\n  The ``competing`` value is used when the same queue name should be used for several services of the same type and thus "compete" for who should consume the message. Since ``tomodachi`` version 0.19.x this value has a changed default value and will now default to ``True`` as this is the most likely use-case for pub/sub in distributed architectures.\n\n  Unless ``queue_name`` is specified an auto generated queue name will be used. Additional prefixes to both ``topic`` and ``queue_name`` can be assigned by setting the ``options.aws_sns_sqs.topic_prefix`` and ``options.aws_sns_sqs.queue_name_prefix`` dict values.\n\n  The ``filter_policy`` value of specified as a keyword argument will be applied on the SNS subscription (for the specified topic and queue) as the ``"FilterPolicy`` attribute. This will apply a filter on SNS messages using the chosen "message attributes" and/or their values specified in the filter. Make note that the filter policy dict structure differs somewhat from the actual message attributes, as values to the keys in the filter policy must be a dict (object) or list (array). Example: A filter policy value of ``{"event": ["order_paid"], "currency": ["EUR", "USD"]}`` would set up the SNS subscription to receive messages on the topic only where the message attribute ``"event"`` is ``"order_paid"`` and the ``"currency"`` value is either ``"EUR"`` or ``"USD"``.\n\n  If ``filter_policy`` is not specified as an argument (default), the queue will receive messages on the topic as per already specified if using an existing subscription, or receive all messages on the topic if a new subscription is set up (default). Changing the ``filter_policy`` on an existing subscription may take several minutes to propagate. Read more about the filter policy format on AWS. https://docs.aws.amazon.com/sns/latest/dg/sns-subscription-filter-policies.html\n\n  Related to the above mentioned filter policy, the ``aws_sns_sqs_publish`` function (which is used for publishing messages) can specify "message attributes" using the ``message_attributes`` keyword argument. Values should be specified as a simple ``dict`` with keys and values. Example: ``{"event": "order_paid", "paid_amount": 100, "currency": "EUR"}``.\n\n  Depending on the service ``message_envelope`` (previously named ``message_protocol``) attribute if used, parts of the enveloped data would be distributed to different keyword arguments of the decorated function. It\'s usually safe to just use ``data`` as an argument. You can also specify a specific ``message_envelope`` value as a keyword argument to the decorator for specifying a specific enveloping method to use instead of the global one set for the service.\n\n  If you\'re utilizing ``from tomodachi.envelope import ProtobufBase`` and using ``ProtobufBase`` as the specified service ``message_envelope`` you may also pass a keyword argument ``proto_class`` into the decorator, describing the protobuf (Protocol Buffers) generated Python class to use for decoding incoming messages. Custom enveloping classes can be built to fit your existing architecture or for even more control of tracing and shared metadata between services.\n\n  Encryption at rest for AWS SNS and/or AWS SQS can optionally be configured by specifying the KMS key alias or KMS key id as tomodachi service options ``options.aws_sns_sqs.sns_kms_master_key_id`` (to configure encryption at rest on the SNS topics for which the tomodachi service handles the SNS -> SQS subscriptions) and ``options.aws_sns_sqs.sqs_kms_master_key_id`` (to configure encryption at rest for the SQS queues which the service is consuming). Note that an option value set to an empty string (``""``) or ``False`` will unset the KMS master key id and thus disable encryption at rest. If instead an option is completely unset or set to ``None`` value no changes will be done to the KMS related attributes on an existing topic or queue. It\'s generally not advised to change the KMS master key id/alias values for resources currently in use. If it\'s expected that the services themselves, via their IAM credentials or assumed role, are responsible for creating queues and topics, these options could be desirable to use. Do not use these options if you instead are using IaC tooling to handle the topics, queues and subscriptions or that they for example are created / updated as a part of deployments. Read more at https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-server-side-encryption.html and https://docs.aws.amazon.com/sns/latest/dg/sns-server-side-encryption.html#sse-key-terms.\n\nAMQP messaging (RabbitMQ):\n--------------------------\n``@tomodachi.amqp(routing_key, exchange_name="amq.topic", competing=True, queue_name=None, **kwargs)``\n  Sets up the method to be called whenever a **AMQP / RabbitMQ message is received** for the specified ``routing_key``. By default the ``\'amq.topic\'`` topic exchange would be used, it may also be overridden by setting the ``options.amqp.exchange_name`` dict value on the service class.\n\n  The ``competing`` value is used when the same queue name should be used for several services of the same type and thus "compete" for who should consume the message. Since ``tomodachi`` version 0.19.x this value has a changed default value and will now default to ``True`` as this is the most likely use-case for pub/sub in distributed architectures.\n\n  Unless ``queue_name`` is specified an auto generated queue name will be used. Additional prefixes to both ``routing_key`` and ``queue_name`` can be assigned by setting the ``options.amqp.routing_key_prefix`` and ``options.amqp.queue_name_prefix`` dict values.\n\n  Depending on the service ``message_envelope`` (previously named ``message_protocol``) attribute if used, parts of the enveloped data would be distributed to different keyword arguments of the decorated function. It\'s usually safe to just use ``data`` as an argument. You can also specify a specific ``message_envelope`` value as a keyword argument to the decorator for specifying a specific enveloping method to use instead of the global one set for the service.\n\n  If you\'re utilizing ``from tomodachi.envelope import ProtobufBase`` and using ``ProtobufBase`` as the specified service ``message_envelope`` you may also pass a keyword argument ``proto_class`` into the decorator, describing the protobuf (Protocol Buffers) generated Python class to use for decoding incoming messages. Custom enveloping classes can be built to fit your existing architecture or for even more control of tracing and shared metadata between services.\n\n\nScheduled functions / cron / triggered on time interval:\n--------------------------------------------------------\n``@tomodachi.schedule(interval=None, timestamp=None, timezone=None, immediately=False)``\n  A **scheduled function** invoked on either a specified ``interval`` (you may use the popular cron notation as a str for fine-grained interval or specify an integer value of seconds) or a specific ``timestamp``. The ``timezone`` will default to your local time unless explicitly stated.\n\n  When using an integer ``interval`` you may also specify wether the function should be called ``immediately`` on service start or wait the full ``interval`` seconds before its first invokation.\n\n``@tomodachi.heartbeat``\n  A function which will be **invoked every second**.\n\n``@tomodachi.minutely``, ``@tomodachi.hourly``, ``@tomodachi.daily``, ``@tomodachi.monthly``\n  A scheduled function which will be invoked once **every minute / hour / day / month**.\n\n**A word on scheduled tasks in distributed contexts:** What is your use-case for scheduling function triggers or functions that trigger on an interval. These types of scheduling may not be optimal in clusters with many pods in the same replication set, as all the services running the same code will very likely execute at the same timestamp / interval (which in same cases may correlated with exactly when they were last deployed). As such these functions are quite naive and should only be used with some care, so that it triggering the functions several times doesn\'t incur unnecessary costs or come as a bad surprise if the functions aren\'t completely idempotent. To perform a task on a specific timestamp or on an interval where only one of the available services of the same type in a cluster should trigger is a common thing to solve and there are several solutions to pick from., some kind of distributed consensus needs to be reached. Tooling exists, but what you need may differ depending on your use-case. There\'s algorithms for distributed consensus and leader election, Paxos or Raft, that luckily have already been implemented to solutions like the strongly consistent and distributed key-value stores *etcd* and *TiKV*. Even primitive solutions such as *Redis*  ``SETNX`` commands would work, but could be costly or hard to manage access levels around. If you\'re on k8s there\'s even a simple "leader election" API available that just creates a 15 seconds lease. Solutions are many and if you are in need, go hunting and find one that suits your use-case, there\'s probably tooling and libraries available to call it from your service functions.\n\nImplementing proper consensus mechanisms and in turn leader election can be complicated. In distributed environments the architecture around these solutions needs to account for leases, decision making when consensus was not reached, how to handle crashed executors, quick recovery on master node(s) disruptions, etc.\n\n----\n\n*To extend the functionality by building your own trigger decorators for your endpoints, studying the built-in invoker classes should the first step of action. All invoker classes should extend the class for a common developer experience:* ``tomodachi.invoker.Invoker``.\n\n----\n\nAdditional configuration options 🤩\n===================================\nA ``tomodachi.Service`` extended service class may specify a class attribute named ``options`` (as a ``dict``) for additional configuration.\n\n.. code:: python\n\n    import json\n\n    import tomodachi\n\n\n    class Service(tomodachi.Service):\n        name = "http-example"\n        options = {\n            "http.port": 80,\n            "http.content_type": "application/json; charset=utf-8",\n            "http.real_ip_from": [\n                "127.0.0.1/32",\n                "10.0.0.0/8",\n                "172.16.0.0/12",\n                "192.168.0.0/16",\n            ],\n            "http.keepalive_timeout": 5,\n            "http.max_keepalive_requests": 20,\n            "watcher.ignored_dirs": ["node_modules"],\n        }\n\n        @tomodachi.http("GET", r"/health")\n        async def health_check(self, request):\n            return 200, json.dumps({"status": "healthy"})\n\n        # Specify custom 404 catch-all response\n        @tomodachi.http_error(status_code=404)\n        async def error_404(self, request):\n            return json.dumps({"error": "not-found"})\n\n\n=========================================================  ==================================================================================================================================================================================================================================================================================================================================================================================================================================================================================  ===========================================\n⁝⁝ **HTTP server parameters** ⁝⁝ ``options["http"][key]``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      ``_____________________________``\n---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------  -------------------------------------------\n**Configuration key**                                      **Description**                                                                                                                                                                                                                                                                                                                                                                                                                                                                     **Default value**\n---------------------------------------------------------  ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------  -------------------------------------------\n``http.port``                                              TCP port (integer value) to listen for incoming connections.                                                                                                                                                                                                                                                                                                                                                                                                                        ``9700``\n``http.host``                                              Network interface to bind TCP server to. ``"0.0.0.0"`` will bind to all IPv4 interfaces. ``None`` or ``""`` will assume all network interfaces.                                                                                                                                                                                                                                                                                                                                     ``"0.0.0.0"``\n``http.reuse_port``                                        If set to ``True`` (which is also the default value on Linux) the HTTP server will bind to the port using the socket option ``SO_REUSEPORT``. This will allow several processes to bind to the same port, which could be useful when running services via a process manager such as ``supervisord`` or when it\'s desired to run several processes of a service to utilize additional CPU cores, etc. Note that the ``reuse_port`` option cannot be used on non-Linux platforms.     ``True`` on Linux, otherwise ``False``\n``http.keepalive_timeout``                                 Enables connections to use keep-alive if set to an integer value over ``0``. Number of seconds to keep idle incoming connections open.                                                                                                                                                                                                                                                                                                                                              ``0``\n``http.max_keepalive_requests``                            An optional number (int) of requests which is allowed for a keep-alive connection. After the specified number of requests has been done, the connection will be closed. An option value of ``0`` or ``None`` (default) will allow any number of requests over an open keep-alive connection.                                                                                                                                                                                        ``None``\n``http.max_keepalive_time``                                An optional maximum time in seconds (int) for which keep-alive connections are kept open. If a keep-alive connection has been kept open for more than ``http.max_keepalive_time`` seconds, the following request will be closed upon returning a response. The feature is not used by default and won\'t be used if the value is ``0`` or ``None``. A keep-alive connection may otherwise be open unless inactive for more than the keep-alive timeout.                              ``None``\n``http.client_max_size``                                   The client’s maximum size in a request, as an integer, in bytes.                                                                                                                                                                                                                                                                                                                                                                                                                    ``(1024 ** 2) * 100``\n``http.termination_grace_period_seconds``                  The number of seconds to wait for functions called via HTTP to gracefully finish execution before terminating the service, for example if service received a `SIGINT` or `SIGTERM` signal while requests were still awaiting response results.                                                                                                                                                                                                                                      ``30``\n``http.real_ip_header``                                    Header to read the value of the client\'s real IP address from if service operates behind a reverse proxy. Only used if ``http.real_ip_from`` is set and the proxy\'s IP correlates with the value from ``http.real_ip_from``.                                                                                                                                                                                                                                                        ``"X-Forwarded-For"``\n``http.real_ip_from``                                      IP address(es) or IP subnet(s) / CIDR. Allows the ``http.real_ip_header`` header value to be used as client\'s IP address if connecting reverse proxy\'s IP equals a value in the list or is within a specified subnet. For example ``["127.0.0.1/32", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]`` would permit header to be used if closest reverse proxy is ``"127.0.0.1"`` or within the three common private network IP address ranges.                                    ``[]``\n``http.content_type``                                      Default content-type header to use if not specified in the response.                                                                                                                                                                                                                                                                                                                                                                                                                ``"text/plain; charset=utf-8"``\n``http.access_log``                                        If set to the default value (boolean) ``True`` the HTTP access log will be output to stdout (logger ``transport.http``). If set to a ``str`` value, the access log will additionally also be stored to file using value as filename.                                                                                                                                                                                                                                                ``True``\n``http.server_header``                                     ``"Server"`` header value in responses.                                                                                                                                                                                                                                                                                                                                                                                                                                             ``"tomodachi"``\n---------------------------------------------------------  ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------  -------------------------------------------\n------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n⁝⁝ **Credentials and prefixes for AWS SNS+SQS pub/sub** ⁝⁝ ``options["aws_sns_sqs"][key]``\n------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n**Configuration key**                                      **Description**                                                                                                                                                                                                                                                                                                                                                                                                                                                                     **Default value**\n---------------------------------------------------------  ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------  -------------------------------------------\n``aws_sns_sqs.region_name``                                The AWS region to use for SNS+SQS pub/sub API requests.                                                                                                                                                                                                                                                                                                                                                                                                                             ``None``\n``aws_sns_sqs.aws_access_key_id``                          The AWS access key to use for SNS+SQS pub/sub API requests.                                                                                                                                                                                                                                                                                                                                                                                                                         ``None``\n``aws_sns_sqs.aws_secret_access_key``                      The AWS secret to use for SNS+SQS pub/sub API requests.                                                                                                                                                                                                                                                                                                                                                                                                                             ``None``\n``aws_sns_sqs.topic_prefix``                               A prefix to any SNS topics used. Could be good to differentiate between different dev environments.                                                                                                                                                                                                                                                                                                                                                                                 ``""``\n``aws_sns_sqs.queue_name_prefix``                          A prefix to any SQS queue names used. Could be good to differentiate between different dev environments.                                                                                                                                                                                                                                                                                                                                                                            ``""``\n``aws_sns_sqs.sns_kms_master_key_id``                      If set, will set the KMS key (alias or id) to use for encryption at rest on the SNS topics created by the service or subscribed to by the service. Note that an option value set to an empty string (``""``) or ``False`` will unset the KMS master key id and thus disable encryption at rest. If instead an option is completely unset or set to ``None`` value no changes will be done to the KMS related attributes on an existing topic.                                       ``None`` (no changes to KMS settings)\n``aws_sns_sqs.sqs_kms_master_key_id``                      If set, will set the KMS key (alias or id) to use for encryption at rest on the SQS queues created by the service or for which the service consumes messages on. Note that an option value set to an empty string (``""``) or ``False`` will unset the KMS master key id and thus disable encryption at rest. If instead an option is completely unset or set to ``None`` value no changes will be done to the KMS related attributes on an existing queue.                         ``None`` (no changes to KMS settings)\n``aws_sns_sqs.sqs_kms_data_key_reuse_period``              If set, will set the KMS data key reuse period value on the SQS queues created by the service or for which the service consumes messages on. If the option is completely unset or set to ``None`` value no change will be done to the KMSDataKeyReusePeriod attribute of an existing queue, which can be desired if it\'s specified during deployment, manually or as part of infra provisioning. Unless changed, SQS queues using KMS use the default value ``300`` (seconds).      ``None``\n---------------------------------------------------------  ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------  -------------------------------------------\n------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n⁝⁝ **Configure custom AWS endpoints for development** ⁝⁝ ``options["aws_endpoint_urls"][key]``\n------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n**Configuration key**                                      **Description**                                                                                                                                                                                                                                                                                                                                                                                                                                                                     **Default value**\n---------------------------------------------------------  ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------  -------------------------------------------\n``aws_endpoint_urls.sns``                                  Configurable endpoint URL for AWS SNS – primarily used for integration testing during development using fake services / fake endpoints.                                                                                                                                                                                                                                                                                                                                             ``None``\n``aws_endpoint_urls.sqs``                                  Configurable endpoint URL for AWS SQS – primarily used for integration testing during development using fake services / fake endpoints.                                                                                                                                                                                                                                                                                                                                             ``None``\n---------------------------------------------------------  ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------  -------------------------------------------\n------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n⁝⁝ **AMQP / RabbitMQ pub/sub settings** ⁝⁝ ``options["amqp"][key]``\n------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n**Configuration key**                                      **Description**                                                                                                                                                                                                                                                                                                                                                                                                                                                                     **Default value**\n---------------------------------------------------------  ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------  -------------------------------------------\n``amqp.host``                                              Host address / hostname for RabbitMQ server.                                                                                                                                                                                                                                                                                                                                                                                                                                        ``"127.0.0.1"``\n``amqp.port``                                              Host post for RabbitMQ server.                                                                                                                                                                                                                                                                                                                                                                                                                                                      ``5672``\n``amqp.login``                                             Login credentials.                                                                                                                                                                                                                                                                                                                                                                                                                                                                  ``"guest"``\n``amqp.password``                                          Login credentials.                                                                                                                                                                                                                                                                                                                                                                                                                                                                  ``"guest"``\n``amqp.exchange_name``                                     The AMQP exchange name to use in the service.                                                                                                                                                                                                                                                                                                                                                                                                                                       ``"amq_topic"``\n``amqp.routing_key_prefix``                                A prefix to add to any AMQP routing keys provided in the service.                                                                                                                                                                                                                                                                                                                                                                                                                   ``""``\n``amqp.queue_name_prefix``                                 A prefix to add to any AMQP queue names provided in the service.                                                                                                                                                                                                                                                                                                                                                                                                                    ``""``\n``amqp.virtualhost``                                       AMQP virtualhost settings.                                                                                                                                                                                                                                                                                                                                                                                                                                                          ``"/"``\n``amqp.ssl``                                               TLS can be enabled for supported host connections.\t                                                                                                                                                                                                                                                                                                                                                                                                                                 ``False``\n``amqp.heartbeat``                                         The heartbeat timeout value defines after what period of time the peer TCP connection should be considered unreachable (down) by RabbitMQ and client libraries.                                                                                                                                                                                                                                                                                                                     ``60``\n``amqp.queue_ttl``                                         TTL set on newly created queues.                                                                                                                                                                                                                                                                                                                                                                                                                                                    ``86400``\n---------------------------------------------------------  ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------  -------------------------------------------\n------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n⁝⁝ **Options for code auto reload on file changes in development** ⁝⁝ ``options["watcher"][key]``\n------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n**Configuration key**                                      **Description**                                                                                                                                                                                                                                                                                                                                                                                                                                                                     **Default value**\n---------------------------------------------------------  ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------  -------------------------------------------\n``watcher.ignored_dirs``                                   Directories / folders that the automatic code change watcher should ignore. Could be used during development to save on CPU resources if any project folders contains a large number of file objects that doesn\'t need to be watched for code changes. Already ignored directories are ``"__pycache__"``, ``".git"``, ``".svn"``, ``"__ignored__"``, ``"__temporary__"`` and ``"__tmp__"``.                                                                                         ``[]``\n``watcher.watched_file_endings``                           Additions to the list of file endings that the watcher should monitor for file changes. Already followed file endings are ``".py"``, ``".pyi"``, ``".json"``, ``".yml"``, ``".html"`` and ``".phtml"``.                                                                                                                                                                                                                                                                             ``[]``\n=========================================================  ==================================================================================================================================================================================================================================================================================================================================================================================================================================================================================  ===========================================\n\n\nDecorated functions using ``@tomodachi.decorator`` 🎄\n-----------------------------------------------------\nInvoker functions can of course be decorated using custom functionality. For ease of use you can then in turn decorate your decorator with the the built-in ``@tomodachi.decorator`` to ease development.\nIf the decorator would return anything else than ``True`` or ``None`` (or not specifying any return statement) the invoked function will *not* be called and instead the returned value will be used, for example as an HTTP response.\n\n.. code:: python\n\n    import tomodachi\n\n\n    @tomodachi.decorator\n    async def require_csrf(instance, request):\n        token = request.headers.get("X-CSRF-Token")\n        if not token or token != request.cookies.get("csrftoken"):\n            return {\n                "body": "Invalid CSRF token",\n                "status": 403\n            }\n\n\n    class Service(tomodachi.Service):\n        name = "example"\n\n        @tomodachi.http("POST", r"/create")\n        @require_csrf\n        async def create_data(self, request):\n            # Do magic here!\n            return "OK"\n\n\n----\n\nRequirements 👍\n===============\n* Python_ (``3.7+``, ``3.8+``, ``3.9+``)\n* aiohttp_ (``aiohttp`` is the currently supported HTTP server implementation for ``tomodachi``)\n* aiobotocore_ and botocore_ (used for AWS SNS+SQS pub/sub messaging)\n* aioamqp_ (used for RabbitMQ / AMQP pub/sub messaging)\n* uvloop_ (optional: alternative event loop implementation)\n\n.. _Python: https://www.python.org\n.. _asyncio: http://docs.python.org/3.9/library/asyncio.html\n.. _aiohttp: https://github.com/aio-libs/aiohttp\n.. _aiobotocore: https://github.com/aio-libs/aiobotocore\n.. _botocore: https://github.com/boto/botocore\n.. _aioamqp: https://github.com/Polyconseil/aioamqp\n.. _uvloop: https://github.com/MagicStack/uvloop\n\n\n``LICENSE`` 🙋\n==============\n``tomodachi`` is offered under the MIT License.\n\n* MIT License: https://github.com/kalaspuff/tomodachi/blob/master/LICENSE\n\n\n``CHANGELOG`` 🧳\n================\nChanges are recorded in the repo as well as together with the GitHub releases.\n\n* In repository: https://github.com/kalaspuff/tomodachi/blob/master/CHANGES.rst\n\n* Release tags: https://github.com/kalaspuff/tomodachi/releases\n\n\n``GITHUB / SOURCE`` 🦄\n======================\nThe latest developer version of ``tomodachi`` is always available at GitHub.\n\n* Clone repo: ``git@github.com:kalaspuff/tomodachi.git``\n\n* GitHub: https://github.com/kalaspuff/tomodachi\n\n* Latest release: https://github.com/kalaspuff/tomodachi/releases/latest\n\n\nAny questions?\n==============\nWhat is the best way to run a ``tomodachi`` service?\n  Docker containers are great and can be scaled out in Kubernetes, Nomad or other orchestration engines. Some may instead run several services on the same environment, on the same machine if their workloads are smaller or more consistent. Remember to gather your output and monitor your instances or clusters.\n\n  For real workloads: Go for a Dockerized environment if possible – async task queues are usually nice and services could scale up and down for keeping up with incoming demand; if you require network access like HTTP from users or API clients directly to the service, then it\'s usually preferred to put some kind of ingress (nginx, haproxy or other type of load balancer) to proxy requests to the service pods. Let the ingress then handle public TLS, http2 / http3, client facing keep-alives and WebSocket protocol upgrades and let the service instead take care of the business logic.\n\nAre there any more example services?\n  There are a few examples in the `examples <https://github.com/kalaspuff/tomodachi/blob/master/examples>`_ folder, including using ``tomodachi`` in an `example Docker environment <https://github.com/kalaspuff/tomodachi/tree/master/examples/docker_examples/http_service>`_ with or without docker-compose. There are examples to publish events / messages to an AWS SNS topic and subscribe to an AWS SQS queue. There\'s also a similar code available of how to work with pub/sub for RabbitMQ via the AMQP transport protocol.\n\nWhy should I use this?\n  ``tomodachi`` is a perfect place to start when experimenting with your architecture or trying out a concept for a new service. It may not have all the features you desire and it may never do, but I believe it\'s great for bootstrapping microservices in async Python.\n\nI have some great additions!\n  Sweet! Please send me a PR with your ideas. There\'s now automatic tests that are running as GitHub actions to verify linting and regressions. Get started at the short `contribution guide <https://github.com/kalaspuff/tomodachi/blob/master/CONTRIBUTING.rst>`_.\n\nBeta software in production?\n  There are some projects and organizations that already are running services based on ``tomodachi`` in production. The library is provided as is with an unregular release schedule, and as with most software, there will be unfortunate bugs or crashes. Consider this currently as beta software (with an ambition to be stable enough for production). Would be great to hear about other use-cases in the wild!\n\n  Another good idea is to drop in Sentry or other exception debugging solutions. These are great to catch errors if something wouldn\'t work as expected in the internal routing or if your service code raises unhandled exceptions.\n\nWho built this and why?\n  My name is **Carl Oscar Aaro** [`@kalaspuff <https://github.com/kalaspuff>`_] and I\'m a coder from Sweden. When I started writing the first few lines of this library back in 2016, my intention was just to learn more about Python\'s ``asyncio``, the event loop, event sourcing and message queues. A lot has happened since – now running services in both production and development clusters, while also using microservices for quick proof of concepts and experimentation. 🎉\n\n\n* https://github.com/kalaspuff\n* https://www.linkedin.com/in/carloscaraaro/\n\n\nContributions\n=============\nPlease help out to add features that you deem are missing and/or fix\nbugs in the repo.\n\nTo add a PR, for the repository, commit your changes to your own clone\nand make a PR on GitHub for your clone against master branch.\n\nRead more in the `contribution guide <https://github.com/kalaspuff/tomodachi/blob/master/CONTRIBUTING.rst>`_.\n',
    'author': 'Carl Oscar Aaro',
    'author_email': 'hello@carloscar.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/kalaspuff/tomodachi',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'extras_require': extras_require,
    'entry_points': entry_points,
    'python_requires': '>=3.7,<4.0',
}


setup(**setup_kwargs)
