Werkzeug предлагает много полезных методов, было бы намного проще, если бы он поддерживал ASGI, чем если бы мы начинали с нуля.
Да, Werkzeug и Flask в конечном итоге будут поддерживать ASGI. У меня нет графика для этого, хотя я был бы рад помочь просмотреть PR, если кто-то начал его.
Однако я не собираюсь быть тем, кто это реализует, мне нужна помощь сообщества. Смотрите последнее обновление ниже: https://github.com/pallets/werkzeug/issues/1322#issuecomment -600926145
Мне интересно работать над этим.
У меня есть работающая, но хакерская поддержка ASGI: werkzeug , flask . Я хотел бы узнать больше о ваших планах, прежде чем идти дальше.
Вещи, которые уже приходят мне в голову, помимо того факта, что все, что я там сделал, могло бы быть в целом лучше:
Пожалуйста, поделись своими мыслями.
Я поддерживаю синтаксический анализатор форм, просто запуская его синхронный код в потоке (который я блокирую, пока асинхронно читаю данные). Я не думаю, что это будет делать. Мой предпочтительный подход состоял бы в том, чтобы исключить ввод-вывод.
Я предполагаю, что подход с легким касанием будет заключаться в том, чтобы просто повторно реализовать существующий парсер, но с асинхронным вводом-выводом. Было бы чище, если бы два класса синтаксических анализаторов могли совместно использовать общую реализацию без ввода-вывода под капотом, но может быть более практичным просто продублировать и немного изменить существующую реализацию.
Контекстно-локальная поддержка довольно хрупкая.
Локальные переменные контекста (для 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) по ходу дела (насколько это возможно) — мое единственное препятствие — нехватка времени.
Самый полезный комментарий
Да, Werkzeug и Flask в конечном итоге будут поддерживать ASGI. У меня нет графика для этого, хотя я был бы рад помочь просмотреть PR, если кто-то начал его.
Однако я не собираюсь быть тем, кто это реализует, мне нужна помощь сообщества. Смотрите последнее обновление ниже: https://github.com/pallets/werkzeug/issues/1322#issuecomment -600926145