Werkzeug: Есть ли у werkzeug планы по поддержке ASGI?

Созданный на 7 июн. 2018  ·  21Комментарии  ·  Источник: pallets/werkzeug

Werkzeug предлагает много полезных методов, было бы намного проще, если бы он поддерживал ASGI, чем если бы мы начинали с нуля.

Самый полезный комментарий

Да, Werkzeug и Flask в конечном итоге будут поддерживать ASGI. У меня нет графика для этого, хотя я был бы рад помочь просмотреть PR, если кто-то начал его.

Однако я не собираюсь быть тем, кто это реализует, мне нужна помощь сообщества. Смотрите последнее обновление ниже: https://github.com/pallets/werkzeug/issues/1322#issuecomment -600926145

Все 21 Комментарий

Да, Werkzeug и Flask в конечном итоге будут поддерживать ASGI. У меня нет графика для этого, хотя я был бы рад помочь просмотреть PR, если кто-то начал его.

Однако я не собираюсь быть тем, кто это реализует, мне нужна помощь сообщества. Смотрите последнее обновление ниже: https://github.com/pallets/werkzeug/issues/1322#issuecomment -600926145

Мне интересно работать над этим.

У меня есть работающая, но хакерская поддержка ASGI: werkzeug , flask . Я хотел бы узнать больше о ваших планах, прежде чем идти дальше.

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

  • Я поддерживаю синтаксический анализатор форм, просто запуская его синхронный код в потоке (который я блокирую, пока асинхронно читаю данные). Я не думаю, что это будет делать. Мой предпочтительный подход состоял бы в том, чтобы исключить IO .
  • Контекстно-локальная поддержка довольно хрупкая. Кажется, есть правильный способ сделать это , но он включает в себя вмешательство в глобальное состояние, и особенно с ASGI, мы не можем предполагать, что мы владеем миром.
  • При работе под ASGI нет очевидного способа анализировать данные формы по запросу для синхронной функции. Возможно, стоит подумать о нетерпеливом анализе данных формы, если мы не можем сказать, что функция просмотра является асинхронной. Каким бы ни было значение по умолчанию, явно может быть декоратор, который его переопределит.

Пожалуйста, поделись своими мыслями.

Я поддерживаю синтаксический анализатор форм, просто запуская его синхронный код в потоке (который я блокирую, пока асинхронно читаю данные). Я не думаю, что это будет делать. Мой предпочтительный подход состоял бы в том, чтобы исключить ввод-вывод.

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

Контекстно-локальная поддержка довольно хрупкая.

Локальные переменные контекста (для asyncio) существуют в stdlib 3.7. https://docs.python.org/3.7/library/contextvars.html
Я бы предпочел использовать их с дополнительной библиотекой совместимости для поддержки более ранних версий Python. (Или просто предположим, что ASGI на Flask в конечном итоге станет вещью 3.7+)

Использует ли werkzeug локальные потоки? (Я знаю, что Flask делает)

При работе под ASGI нет очевидного способа анализировать данные формы по запросу для синхронной функции.

Я бы предложил не предлагать синхронный интерфейс для анализа данных формы. (Или для доступа к телу запроса каким-либо образом), а вместо этого просто предложите ему асинхронные API. См. API Starlette для некоторых примеров здесь... https://github.com/encode/starlette#body

Поскольку Python 3.7 отсутствует, я не возражаю против использования асинхронных функций, требующих Python 3.7, если это не влияет на другой код. Но если есть бэкпорт, который так же хорош, как нативное решение 3.7 — даже лучше!

Что касается синтаксического анализа данных асинхронной формы... Я думаю, что что-то вроде await request.parse() в асинхронной функции будет достаточно, а затем вызовет исключение при попытке доступа к непроанализированным данным формы?

Конечно, или просто сделайте values и друзей асинхронными: values = await request.values или values = await request.values() , в зависимости от того, что выглядит лучше.

разве для этого не потребуются довольно уродливые вещи, такие как (await request.form)['foo'] , для выполнения асинхронного вызова при получении элемента dict напрямую без назначения между ними?

да :(

Хотя я не уверен, что этого особенно можно избежать. я не думаю

form = await request.form
form['foo']

действительно более или менее уродлив, чем

await request.parse()
request.form['foo']

хотя это явно зависит от вкуса.

Я думаю, мы могли бы также изобрести «асинхронный словарь», чьи _members_ все асинхронизированы, но, не видя такого, я полагаю, что это в конечном итоге будет довольно запутанным.

Конечно, или просто сделайте сами значения и друзей асинхронными: values ​​= await request.values ​​или values ​​= await request.values(), в зависимости от того, что выглядит лучше.

Я бы предложил использовать вызовы функций для операций ввода-вывода, а не свойства.

разве для этого не потребуются довольно уродливые вещи, такие как (await request.form)['foo'] для выполнения асинхронного вызова при получении элемента dict напрямую без назначения между ними?

Пожимайте плечами — не делайте этого.

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

form = await request.form()
form['foo']

Хотя я полностью за добавление поддержки асинхронности, мы все еще не можем сломать версии Python 2 и синхронизировать. Один из подходов, о котором я слышал от @njsmith , заключается в том, чтобы написать все как асинхронное, а затем использовать инструмент, похожий на 2to3, для создания версии синхронизации. Судя по всему, это пробуют в urllib3, но я недостаточно об этом знаю.

Интересно, можем ли мы добавить магии к объектам за request.form и т. д., чтобы их вызов выполнял асинхронные действия, в то время как обычные методы, подобные диктовке, будут синхронизироваться (и не будут работать в асинхронном режиме). Или мы можем просто отказаться от любого доступа к request.form и т. д. в асинхронном режиме и использовать отдельное имя для асинхронных версий, например, request.parse_form() .

Или... request_class = AsyncRequest , если кто-то хочет async; на самом деле это может быть значением по умолчанию в классе AsyncFlask .

Или... request_class = AsyncRequest, если кто-то хочет async; на самом деле это может быть значением по умолчанию в классе AsyncFlask.

Такой подход имел бы для меня смысл, да.

Что касается синтаксического анализатора форм, я попытался сделать его sansio-ing в #1330.

На https://github.com/andrew-d/python-multipart также есть реализация парсера потоковой формы со 100% покрытием. (Я нашел еще один, но не было очевидно, что его можно легко адаптировать к потоку «подача данных, обработка событий».)

python-multipart — это библиотека, которую я сейчас использую для Starlette. Вы можете взглянуть на интеграцию с асинхронным потоком здесь: https://github.com/encode/starlette/blob/master/starlette/formparsers.py#L207 .

Я также думал о том, как лучше всего представить интерфейс, совместимый с синхронизацией и асинхронностью, поскольку я также хочу, чтобы это было для Starlette (хотя я иду в другом направлении к Werkzeug, для меня это примерно «У меня есть существующий асинхронный интерфейс, как я могу теперь также представьте синхронизацию")

Для Starlette я думаю, что я, вероятно, перенесу фактический синтаксический анализ в метод async parse(self) и обычно вызываю его во время отправки запроса, но выставляю обычные простые свойства form , files и т. д. ... для доступа к результатам из пользовательского кода.

Не по теме, но связано с популярным случаем ASGI и werkzeug (я не хочу срывать эту проблему, но не хочу создавать повторяющуюся проблему без добавления сути):

Если вы используете https://github.com/django-extensions/django-extensions с ./manage.py runserver_plus и https://github.com/django/channels (который использует ASGI), вы получаете Opcode -1 (код операции минус 1) в инспекторе, попробуйте пока запустить ./manage.py runserver , пока не будет поддерживаться ASGI.

(Werkzeug используется под капотом на Django с расширениями django, и я потратил несколько часов на выяснение причин, так как я так привык разрабатывать с runserver_plus для отладки)

Я использую runserver_plus, чтобы использовать https в разработке.
Сейчас я пытаюсь добавить поддержку веб-сокетов с помощью каналов Django и столкнулся с проблемой, что каналы используют runserver и не поддерживают runserver_plus. Итак, я могу использовать каналы ИЛИ я могу использовать https, но не оба!

@davidism , скажите, пожалуйста, есть ли изменения в реализации поддержки ASGI для Flask с момента вашего последнего ответа в этой теме и изменились ли ваши планы в связи с этой задачей по прекращению поддержки Python 2?

2 июля 2018 г. @davidism пишет:

Судя по всему, это пробуют в urllib3, но я недостаточно об этом знаю.

Только что видел это некоторое время назад — если кому-то интересно узнать больше, похоже, что python-trio/urllib3 # 1 содержит кучу деталей. Обратите внимание на ссылку на urllib3/urllib3#1323, которая содержит:

Решение: мы поддерживаем одну копию кода — версию с аннотациями async/await — а затем небольшой скрипт поддерживает синхронную копию, снова автоматически удаляя их. Это некрасиво, но, насколько я могу судить, все альтернативы хуже...

(Продолжайте читать там, если интересно.)

Приятно видеть, что это, по-видимому, продолжает хорошо работать, основываясь на постоянном прогрессе, достигнутом на https://github.com/python-trio/urllib3/commits/bleach-spike.

Небольшой удар, чтобы вернуть эту проблему в поле зрения. Поскольку версия 3.5 достигает EOL в этом году, я думаю, самое время подумать о поддержке асинхронности?

За полтора года, прошедшие с тех пор, как это было опубликовано, не было особой активности по его реализации. У меня лично нет никакого опыта или потребности в asyncio, и хотя мне нравится ASGI, я никогда не собирался браться за это самостоятельно.

Тем временем @pgjones , автор Quart, стал активнее участвовать в Werkzeug. Quart теперь по возможности использует Werkzeug за кулисами, и мы продолжаем его развивать. Был некоторый протест со стороны сопровождающего Flask по поводу перехода на ASGI, поэтому Фил также создал поддоны/flask#3412, которые, по крайней мере, позволяли выполнять маршрутизацию к функциям async def , но это уже какое-то время неактивно. На данный момент я бы предпочел пройти ASGI, чем согласиться на это. @edk0 создал #1330, чтобы сделать синтаксический анализ формы sans-io, но он также сидит и, вероятно, должен сначала пройти еще один дизайн и проверку.

Вы можете спросить: «Почему Flask не может делать то же, что и Django?» Я не эксперт по внутренностям Django, но @andrewgodwin объяснил мне некоторое время назад, что Django имеет «более простое» (читай: все еще очень сложное) время из-за того, как он изначально адаптировался к WSGI, в отличие от очень ориентированный на WSGI API, с которого начали Werkzeug и Flask. Кроме того, Django получает гораздо больше внимания и ресурсов на полную ставку, чем Pallets.

Так где же оставить эту проблему? Если вам нужна совместимая с Flask структура, использующая Werkzeug, используйте Quart. Внесите свой вклад в Quart (или Flask), чтобы сделать их более совместимыми с API там, где этого не хватает. Если вы хотите, чтобы Werkzeug и Flask поддерживали ASGI, вам нужно активизироваться. Начните узнавать об АСГИ. Начните определять специфичные для WSGI и блокирующие части API Werkzeug. Начните думать об абстракциях, которые мы можем сделать, чтобы обеспечить реализацию как для WSGI, так и для ASGI. Затем вернитесь к этому обсуждению, чтобы мы могли приступить к разработке и написанию PR.

Спасибо за предложение Quart , я был бы очень рад принять участие в нем.

Я попытался ответить, почему я думаю, что Flask не может сделать то, что сделал Django в этой статье . В конечном счете, я думаю, что поддоны/колба № 3412 — лучшее решение для Flask.

Что касается Werkzeug, я думаю, что ASGI возможен, но с некоторой болью. Ярким примером боли является то, что многие вещи в Werkzeug являются вызываемыми объектами WSGI (например, исключения). С ASGI неясно, как эта функциональность может/должна использоваться, поэтому я бы предпочел ее удалить.

Мой план состоит в том, чтобы продолжать интегрировать Werkzeug в Quart, адаптируя Werkzeug к ASGI (sans-io) по ходу дела (насколько это возможно) — мое единственное препятствие — нехватка времени.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги