【问题标题】:How to obtain an event loop from Quart如何从 Quart 获取事件循环
【发布时间】:2018-02-25 02:39:36
【问题描述】:

您好,我对 Python 还很陌生,我正在尝试将我在 Flask 上的现有应用程序转换为应该构建在 asyncio 之上的 Quart (https://gitlab.com/pgjones/quart),因此我可以使用 Goblin OGM 与 JanusGraph 进行交互或 TinkerPop。根据我在 Goblin 上找到的示例,我需要获取一个事件循环来异步运行命令。

    >>> import asyncio
    >>> from goblin import Goblin

    >>> loop = asyncio.get_event_loop()
    >>> app = loop.run_until_complete(
    ...     Goblin.open(loop))
    >>> app.register(Person, Knows)

但是我找不到从 Quart 获取事件循环的方法,即使它是建立在 asyncio 之上的。

有谁知道我怎么能得到它?任何帮助将不胜感激。

【问题讨论】:

  • 您好,请问我的回答是否对您的问题有所帮助?
  • 嗨,是的,它确实回答了我的主要问题,即如何获取事件循环。因为我在某处读到 Quart 是建立在 asyncio 之上的,所以我会从 Quart 实现并从 asyncio 继承的方法中获取 lopp。但是我还没有完成这个建议的实现,因为在我的例子中,我有一个包含多个模块的应用程序,我试图找出首先在哪里获得 tne 循环并传递给其他模块。
  • 你不需要传递它;您可以在任何需要的地方拨打asyncio.get_event_loop()。在旧版本的 asyncio 中需要显式传递循环的代码,其中 get_event_loop() 不能保证在从协程调用时为您提供正确的事件循环。

标签: python python-asyncio quart goblin


【解决方案1】:

TL;DR 要获取事件循环,请调用asyncio.get_event_loop()

在基于 asyncio 的应用程序中,事件循环通常不属于 Quart 或任何其他协议/应用程序级别的组件,它由 asyncio 或可能的加速器(如 uvloop)提供。事件循环是通过调用asyncio.get_event_loop()获得的,有时用asyncio.set_event_loop()设置。

这是 quart 的 app.run() 用来运行应用程序的,这意味着它可以与 asyncio 为主线程创建的默认事件循环一起使用。在您的情况下,您可以在注册 Goblin 后简单地调用 quart 的 run()

loop = asyncio.get_event_loop()
goblin_app = loop.run_until_complete(Goblin.open(loop))
goblin_app.register(Person, Knows)
quart_app = Quart(...)
# ... @app.route, etc

# now they both run in the same event loop
quart_app.run()


以上应该从实际意义上回答这个问题。但是如果不止一个组件坚持拥有自己的 run() 方法来旋转事件循环,那么这种方法就行不通了——因为 app.run() 不会返回,你只能在线程中调用一个这样的函数。

不过,如果您仔细观察,quart 也并非如此。虽然 Quart 示例确实使用app.run() 为应用程序提供服务,但如果您查看app.run() 的实现,您会看到它调用了便利函数run_app(),它简单地创建了一个服务器并启动了主循环永远:

def run_app(...):
    loop = asyncio.get_event_loop()
    # ...
    create_server = loop.create_server(
        lambda: Server(app, loop, ...), host, port, ...)
    server = loop.run_until_complete(create_server)
    # ...
    loop.run_forever()

如果您需要控制事件循环的实际运行方式,您可以随时自行完成:

# obtain the event loop from asyncio
loop = asyncio.get_event_loop()

# hook Goblin to the loop
goblin_app = loop.run_until_complete(Goblin.open(loop))
goblin_app.register(Person, Knows)

# hook Quart to the loop
quart_server = loop.run_until_complete(loop.create_server(
        lambda: quart.serving.Server(quart_app, loop), host, port))

# actually run the loop (and the program)
try:
    loop.run_forever()
except KeyboardInterrupt:  # pragma: no cover
    pass
finally:
    quart_server.close()
    loop.run_until_complete(quart_server.wait_closed())
    loop.run_until_complete(loop.shutdown_asyncgens())
    loop.close()

【讨论】:

  • 关于您的回答的一个后续问题。如果这很明显,我真的很抱歉,但我才刚刚开始使用 Python。在您描述的第一个场景中,我假设我添加代码以在我的应用程序的 init.py 文件中初始化 Quart 和 Goblin 对象,并且在 Quart 应用程序上调用运行。然后在一个单独的控制器模块上,我想需要使用 Goblin 对象/应用程序将事务发送到数据库。我怎样才能模块化这个初始化,以便 Goblin 对象在所有模块中都可用,并且 Quart 应用程序也可以用于路由?
  • @Cracoras 我没有亲自使用过 Quart 和 Goblin,所以我可能误解了你的问题。答案的第二部分试图表明您不需要在 Quart 应用程序上调用 run,您可以分别准备 goblin 和 quart,然后启动事件循环两者兼得。
  • 这种手动服务器创建对我不起作用。我没有看到任何在 127.0.0.1 上运行的服务器,它没有在监听。
  • 默认情况下,Quart.run 使用asyncio.run(),这会创建一个新的异步事件循环。矛盾“这是 quart 的 app.run() 用来运行应用程序的,这意味着它可以与 asyncio 为主线程创建的默认事件循环一起使用。”
  • @SmartManoj 在撰写此答案时,asyncio.run() 尚不可用。 (它于 2018 年 6 月在 Python 3.7.0 中引入。)如果 Quart.run 现在使用 asyncio.run(),这在技术上是不兼容的更改,会使响应的第一部分无效。第二部分应该仍然可以正常工作,并且可能应该更新为使用 asyncio.run() 本身。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-06-02
  • 2019-02-26
  • 2020-05-28
  • 1970-01-01
  • 2021-05-06
  • 1970-01-01
相关资源
最近更新 更多