aiomisc - это библиотека с различными утилитами для asyncio#

Coveralls Actions Актуальная версия https://img.shields.io/pypi/wheel/aiomisc.svg https://img.shields.io/pypi/pyversions/aiomisc.svg https://img.shields.io/pypi/l/aiomisc.svg

Вам, как программисту, знакомы проблемы, связанные с дизайном и обслуживанием программ. Одним из мест, которое может быть особенно сложным, является создание архитектуры программы, использующего асинхронный ввод-вывод.

Вот тут на сцену выходит aiomisc. Это библиотека Python, которая предоставляет набор служебных функций и классов для работы с асинхронным IO более интуитивно понятным и эффективным способом. Она построена используя библиотеку asyncio и предназначена для облегчения написания асинхронного кода разработчиками, который является надежным и масштабируемым.

С aiomisc вы можете воспользоваться такими мощными функциями, как: worker пул, connection пул, шаблон «предохранитель», и механизмы повторов такие как asyncbackoff и asyncretry чтобы сделать ваш асинхронный код более надежным и простым в обслуживании. В этой документации мы более подробно рассмотрим, что может предложить aiomisc и как он может помочь вам упростить разработку сервисов с asyncio.

Установка#

Возможна установка стандартными способами, такими как PyPI или установка из репозитория git напрямую.

Установка с PyPI:

pip3 install aiomisc

Установка из репозитория на github.com:

# Using git tool
pip3 install git+https://github.com/aiokitchen/aiomisc.git

# Alternative way using http
pip3 install \
    https://github.com/aiokitchen/aiomisc/archive/refs/heads/master.zip

Пакет содержит несколько дополнений, и вы можете установить дополнительные зависимости, если вы укажете их таким образом.

Вместе с uvloop

pip3 install "aiomisc[uvloop]"

Вместе с aiohttp:

pip3 install "aiomisc[aiohttp]"

Полная таблица дополнений ниже:

пример

описание

pip install aiomisc[aiohttp]

Для запуска приложений написанных с aiohttp.

pip install aiomisc[asgi]

Для запуска ASGI приложений

pip install aiomisc[carbon]

Чтобы посылать метрики в carbon (часть graphite)

pip install aiomisc[cron]

планирование задач с библиотекой croniter

pip install aiomisc[raven]

Чтобы посылать исключения в sentry используя raven

pip install aiomisc[rich]

Можете использовать rich для логирования

pip install aiomisc[uvicorn]

For running ASGI application using uvicorn

pip install aiomisc[uvloop]

используйте uvloop как основной event-loop

Вы можете комбинировать эти значения разделяя их запятыми, пример:

pip3 install "aiomisc[aiohttp,cron,rich,uvloop]"

Быстрый старт#

В этом разделе будет рассказано, как эта библиотека создает и использует цикл обработки событий и создает службы. Конечно, обо всем тут не напишешь, но о многом можно прочитать в разделе Учебник, и всегда можно обратиться к разделу Модули и разделу Описание API для справки.

Eventloop и entrypoint#

Сначала рассмотрим этот простой пример:

import asyncio
import logging

import aiomisc

log = logging.getLogger(__name__)

async def main():
    log.info('Starting')
    await asyncio.sleep(3)
    log.info('Exiting')


if __name__ == '__main__':
    with aiomisc.entrypoint(log_level="info", log_format="color") as loop:
        loop.run_until_complete(main())

Этот код объявляет асинхронную функцию main(), которая завершается через 3 секунды. Казалось бы, ничего интересного, но все дело в entrypoint.

Что делает entrypoint, казалось бы не так уж и много, она создает event-loop и передает управление пользователю. Однако под капотом настраивается журналирование в отдельном потоке, создается пул потоков, запускаются сервисы, но об этом позже и сервисов в данном примере нет.

В принципе вы можете не использовать точку входа, а просто создать eventloop и установите его по умолчанию для текущего потока:

import asyncio
import aiomisc

# * Installs uvloop event loop is it's has been installed.
# * Creates and set `aiomisc.thread_pool.ThreadPoolExecutor`
#   as a default executor
# * Sets just created event-loop as a current event-loop for this thread.
aiomisc.new_event_loop()

async def main():
    await asyncio.sleep(1)

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

Приведенный выше пример полезен, если в вашем коде уже неявно используется созданный eventloop, тогда вам придется изменить меньше кода, просто добавьте aiomisc.new_event_loop() и все вызовы asyncio.get_event_loop() вернет созданный экземпляр.

Однако можно обойтись и одним вызовом. Следующий пример закрывает неявно созданный eventloop asyncio и устанавливает новый:

import asyncio
import aiomisc

async def main():
    await asyncio.sleep(3)

if __name__ == '__main__':
    loop = aiomisc.new_event_loop()
    loop.run_until_complete(main())

Сервисы#

Главное, что делает точка входа, — это запускает и корректно останавливает «Сервисы».

Концепция «Сервис», в этой библиотеке, означает класс, наследованный от класса aiosmic.Service и реализующий метод async def start(self) -> None и, опционально, метод async def stop(self, exc: Optional[ Exception]) -> None.

Концепция остановки службы не обязательно заключается в нажатии пользователем клавиш Ctrl+C, на самом деле это просто выход из контекстного менеджера entrypoint.

Пример ниже иллюстрирует, как может выглядеть ваш сервис:

from aiomisc import entrypoint, Service

class MyService(Service):
    async def start(self):
        do_something_when_start()

    async def stop(self, exc):
        do_graceful_shutdown()


with entrypoint(MyService()) as loop:
    loop.run_forever()

Точка входа может запускать любое количество экземпляров службы, и все они будут запускаться конкурентно.

Также есть способ, если метод start является полезной нагрузкой для сервиса, и тогда нет необходимости реализовывать метод stop, так как задача с функцией start будет отменена на этапе выхода из entrypoint. Но в этом случае вам придется уведомить entrypoint о том, что инициализация экземпляра службы завершена и ее можно продолжить.

Примерно так:

import asyncio
from threading import Event
from aiomisc import entrypoint, Service

event = Event()

class MyService(Service):
    async def start(self):
        # Send signal to entrypoint for continue running
        self.start_event.set()
        await asyncio.sleep(3600)


with entrypoint(MyService()) as loop:
    assert event.is_set()

Примечание

entrypoint передает управление телу контекстного менеджера только после того, как все экземпляры службы запущены. Как упоминалось выше, стартом считается завершение метода start или установка стартового события с помощью self.start_event.set().

Вся мощь этой библиотеки это набор уже реализованных или абстрактных сервисов таких как: AIOHTTPService, ASGIService, TCPServer, UDPServer, TCPClient, PeriodicService, CronService и так далее.

К сожалению в данном разделе нет возможности уделить этому больше внимания, обратите внимание на раздел Учебник, там больше примеров и пояснений, ну и конечно вы всегда можете узнать ответ на Описание API или в исходном коде. Авторы постарались сделать исходный код максимально понятным и простым, поэтому не стесняйтесь исследовать его.

Версионирование#

Это программное обеспечение следует методологиии Семантического Версионирования

Кратко: учитывая номер версии МАЖОРНАЯ.МИНОРНАЯ.ПАТЧ, следует увеличивать:

  • МАЖОРНУЮ версию, когда сделаны обратно несовместимые изменения API.

  • МИНОРНУЮ версию, когда вы добавляете новую функциональность, не нарушая обратной совместимости.

  • ПАТЧ-версию, когда вы делаете обратно совместимые исправления.

  • Дополнительные обозначения для предрелизных и билд-метаданных возможны как дополнения к МАЖОРНАЯ.МИНОРНАЯ.ПАТЧ формату.

В этом проекте версия пакета назначается автоматически с помощью poem-plugins, он использует тег в репозитории как МАЖОР и МИНОР, а также счетчик, который берет количество коммитов между тегом и головой ветки.

Как разрабатывать этот проект?#

Этот проект, как и многие другие open source проекты, разрабатывается энтузиастами, и вы можете присоединиться к разработке, создавайте issues в github или присылайте свои правки как merge request.

Чтобы начать разработку в этом репозитории, вам необходимо сделать следующее:

Должно быть установлено

  • Python 3.7+ как python3

  • Установлен Poetry как poetry

Для настройки окружения разработчика выполните:

# installing all dependencies
poetry install

# setting up pre-commit hooks
poetry run pre-commit install

# adding poem-plugins to the poetry
poetry self add poem-plugins