Поздравляю вас с Новым годом кролика и желаю сбычи всех мечт! Это статья о том, что версия django на гринлетах скоро получит новую, более изящную форму. Какую — смотрите ниже. fibers — это новое название, потому что предыдущее — greenhack — никуда не годилось.
async def myview(request): async with fibers: # Здесь можно использовать django obj = MyObject.objects.get() print(obj.related_obj) # Здесь можно писать асинхронный код await notify_somebody(request)
Теперь, использовать django внутри асинхронной функции можно под (асинхронным) контекстным менеджером — fibers. По-моему, юзабилити сильно выигрывает. И семантически — тоже понятнее, что происходит, нет?
Вы спросите — почему было сразу так не сделать. Дело в том, что мне только недавно пришло в голову, что это технически возможно. Конечно — при условии, если вспомнить, как реализованы корутины в питоне (это генераторы), и воспользоваться этой особенностью реализации. Для нетерпеливых — вот ссылка на gist.
Как же можно воспользоваться тем, что корутины — это генераторы? Приведу простой пример:
async def hi(): print('hi')
Несмотря на то, что перед нами асинхронная функция, необязательно иметь event loop, чтобы запустить её. Можно, вместо этого, сделать так:
try: gen = hi() gen.send(None) except StopIteration: pass
У контекстного менеджера fibers — такой же принцип. Он устроен так, что весь код внутри него можно выполнить вызовом gen.send() (вот эта строчка). Дальше, мы оборачиваем этот вызов в гринлет и применяем наш обычный подход.
Я обещал написать простейший Awaitable. Но, поскольку многие читатели и сами могут это сделать (хоть для практических нужд это не нужно), мой вариант будет немного необычным:
class Simple: def __await__(self): return 'hi' yield
await Simple()вернёт'hi'. Слово yield делает функцию генератором, но, конечно, до него не доходит выполнение.__await__— это генератор. В отличие от корутин, он может делать как yield, так и yield from. Тогда как корутины могут делать только await, что соответствует yield from.
Контекстный менеджер fibers использует кастомные Awaitable, которые делают yield специальных значений 'start' и 'end'. Event loop их не поймёт. Поэтому нам нужно обернуть какую-нибудь корутину (верхнего уровня, например), чтобы она не пропускала их наружу. Сделать это относительно несложно: например, если у нас — ASGI приложение, то можно обернуть это приложение целиком.
Иллюстрацию подхода можно посмотреть в gist. Код, действительно, достаточно черновой, но дело в том, что это просто ещё один «фронтенд» к библиотеке greenhack — для него даже не потребовалось вносить туда изменения. «Вещь в себе», короче говоря.
Когда всё будет готово? Всё упирается в асинхронный бэкенд для джанго. Он готов, но не протестирован: нужно пройти testsuite для бэкендов django. Там около 70 тестов — когда они будут проходить, проект будет готов к продакшну. Обещать по срокам ничего не буду, но буду писать недельные отчёты (нет, не на хабре, на гитхабе). К сожалению, рабочие проекты не связаны с этой темой.
Тем временем, в django вмерджили бэкенд для драйвера psycopg3. Таким образом, если раньше я пользовался форком django для своего бэкенда, то теперь в этом необходимости нет. Вообще, конечно, аплодисменты тем, кто делает такие масштабные пул-реквесты, когда это люди со стороны. У меня, я чувствую, и строчки не получилось бы протащить в репозиторий django. К счастью, в случае с fibers это и не нужно.
Кстати, названием проект обязан языку Ruby, точнее, Ruby Fibers. Дело в том, что в Ruby нет async/await, для асинхронности там в принципе не используются «функции другого цвета». Это обеспечивает ту самую совместимость блокирующего кода с асинхронным, от которой в питоне отказались. К лучшему или нет — я не берусь сказать. Но я знаю, как это «исправить» — для тех случаев, когда эта совместимость нужна.
ссылка на оригинал статьи https://habr.com/ru/post/708946/
Добавить комментарий