Werkzeug 提供了许多有用的方法,如果它支持 ASGI 会比我们从头开始容易得多。
是的,Werkzeug 和 Flask 最终会支持 ASGI。 我没有这方面的时间表,但如果有人开始了 PR,我很乐意帮助审查 PR。
但是,我不会成为实施它的人,我需要社区的帮助。 请参阅下面的最新更新: https ://github.com/pallets/werkzeug/issues/1322#issuecomment -600926145
我对这方面的工作很感兴趣。
我有一些工作但很老套的 ASGI 支持: werkzeug , flask 。 在继续之前,我想更多地了解您可能有的任何计划。
我已经想到的事情,除了我在那里所做的一切通常会更好:
请让我知道你的想法。
我通过在一个线程中运行它的同步代码来支持表单解析器(当我异步读取数据时我阻塞了它)。 我认为这行不通。 我首选的方法是排除 IO。
我猜轻触方法只是重新实现现有的解析器,但使用异步 IO。 如果两个解析器类可以在底层共享一个通用的 sans-IO 实现会更简洁,但只是复制和稍微修改现有实现可能更实用。
上下文本地支持非常脆弱。
上下文本地变量(用于 asyncio)存在于 3.7 标准库中。 https://docs.python.org/3.7/library/contextvars.html
我猜想使用这些,并带有一个可选的兼容库来支持早期版本的 python。 (或者只是假设 Flask 上的 ASGI 最终会成为 3.7+ 的东西)
werkzeug 是否使用线程局部变量? (我知道 Flask 确实如此)
在 ASGI 下运行时,没有明显的方法可以按需解析表单数据以实现同步功能
我建议不要为解析表单数据提供同步接口。 (或以任何方式访问请求正文),而只是在其上提供异步 API。 有关示例,请参见 Starlette 的 API ... 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(),主要取决于哪个看起来更好。
我建议将函数调用用于执行 I/O 的操作,而不是属性。
那不需要像 (await request.form)['foo'] 这样丑陋的东西来进行异步调用,同时直接获取 dict 元素而不在两者之间分配吗?
耸肩- 不要那样做。
asyncio 必须更明确地说明代码库的哪些部分执行 I/O,因此我倾向于将它们分成单独的行。
form = await request.form()
form['foo']
虽然我完全赞成添加异步支持,但我们仍然无法破坏 Python 2 和同步版本。 我从@njsmith听到的一种方法是将所有内容编写为异步,然后使用类似于 2to3 的工具生成同步版本。 显然它正在 urllib3 中进行尝试,但我对此知之甚少。
我想知道我们是否可以为request.form
等背后的对象添加魔法,因此调用它们将执行异步操作,而通常的类似 dict 的方法将是同步的(并且在异步模式下会失败)。 或者我们可以在异步模式下对request.form
等进行任何访问失败,并为异步版本使用单独的名称,例如request.parse_form()
。
或者... request_class = AsyncRequest
如果有人想要异步; 这实际上可能是AsyncFlask
类中的默认值。
或者... request_class = AsyncRequest 如果有人想要异步; 这实际上可能是 AsyncFlask 类中的默认值。
这种方法对我来说很有意义,是的。
关于表单解析器,我尝试在 #1330 中对其进行 sansio-ing。
在https://github.com/andrew-d/python-multipart上还有一个流式表单解析器实现,覆盖率为 100%。 (我找到了另一个,但它并不明显可以很容易地适应“提要数据,处理事件”流程。)
python-multipart
是我现在用于 Starlette 的库。 您可以在这里查看与异步流的集成: https ://github.com/encode/starlette/blob/master/starlette/formparsers.py#L207
我也一直在考虑呈现同步和异步兼容接口的最佳方法,因为我也希望 Starlette现在也呈现一个同步”)
对于 Starlette,我想我可能会将实际解析推送到async parse(self)
方法中,并且通常在请求调度期间的某个时间调用它,但要公开常规的普通属性form
、 files
等...用于访问用户代码的结果。
题外话,但与一个流行的 ASGI 和 werkzeug 案例有关(我不想破坏这个问题,但不想创建一个没有实质内容的重复问题):
如果您使用./manage.py runserver_plus
和https://github.com/django/channels (使用 ASGI)运行https://github.com/django-extensions/django-extensions则给出Opcode -1
(操作码减 1)在检查器中,现在尝试运行./manage.py runserver
直到支持 ASGI。
(Werkzeug 在 Django 的底层使用了 django-extensions,我花了几个小时弄清楚为什么,因为我已经习惯了使用runserver_plus
进行调试)
我使用 runserver_plus 以便可以在开发中使用 https。
我现在正在尝试使用 Django 通道添加 Websockets 支持,并且遇到了通道使用 runserver 并且不支持 runserver_plus 的问题。 所以,我可以使用频道,也可以使用 https,不能同时使用!
@davidism请告诉我,自从您在本主题中的最后一个回答以来,对 Flask 的 ASGI 支持的实现是否有任何变化,以及您的计划是否与此任务相关以终止对 Python 2 的支持?
2018 年 7 月 2 日, @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,但我从来不会自己承担。
与此同时,Quart 的作者@pgjones更多地参与了 Werkzeug。 Quart 现在尽可能在幕后使用 Werkzeug,我们正在继续开发它。 Flask 维护人员对使用 ASGI 提出了一些反对意见,因此 Phil 还创建了至少允许路由到async def
函数的pallets/flask#3412,但现在已经有一段时间了。 在这一点上,我宁愿去 ASGI 而不是满足于此。 @edk0创建了 #1330 来进行表单解析 sans-io,但它也一直在使用,可能应该先进行更多设计和审查。
你可能会问,“为什么 Flask 不能像 Django 那样做?” 我不是 Django 内部的专家,但@andrewgodwin 不久前向我解释说 Django 有一个“更容易”(阅读:仍然非常复杂)的时间,因为它最初是如何适应 WSGI 的,而不是Werkzeug 和 Flask 开始使用的非常以 WSGI 为中心的 API。 此外,与 Pallets 相比,Django 获得了更多的全职关注和资源。
那么这个问题在哪里呢? 如果您想要一个使用 Werkzeug 的 Flask 兼容框架,请使用 Quart。 为 Quart(或 Flask)做出贡献,以使它们在缺少的地方与 API 更兼容。 如果您希望 Werkzeug 和 Flask 支持 ASGI,您将需要加强。 开始学习 ASGI。 开始识别 Werkzeug API 中特定于 WSGI 的部分和阻塞部分。 开始考虑我们可以进行的抽象,以支持 WSGI 和 ASGI 的实现。 然后将这项研究带回到讨论中,这样我们就可以开始设计和编写 PR。
感谢Quart的建议,我很乐意接受对它的贡献。
我试图回答为什么我认为 Flask 不能做 Django 在本文中所做的事情。 最终,我认为pallets/flask#3412 是Flask 的最佳解决方案。
就 Werkzeug 而言,我认为 ASGI 是可能的,但有一些痛苦。 痛苦的一个显着例子是 Werkzeug 中的许多东西都是 WSGI 可调用对象(例如异常)。 对于 ASGI,尚不清楚如何/应该使用此功能,因此我更愿意将其删除。
我的计划是继续将 Werkzeug 集成到 Quart 中,将 Werkzeug 调整为 ASGI(sans-io)(尽可能多地接受)——我唯一的障碍是没有时间。
最有用的评论
是的,Werkzeug 和 Flask 最终会支持 ASGI。 我没有这方面的时间表,但如果有人开始了 PR,我很乐意帮助审查 PR。
但是,我不会成为实施它的人,我需要社区的帮助。 请参阅下面的最新更新: https ://github.com/pallets/werkzeug/issues/1322#issuecomment -600926145