【问题标题】:Async version of built-in print (stdout)内置打印的异步版本(stdout)
【发布时间】:2019-11-07 14:46:34
【问题描述】:

我无法理解在 async 函数中使用 print 的一些限制。基本上这是我的代码:

#!/usr/bin/env python
import sys
import asyncio
import aiohttp

async amain(loop):
    session = aiohttp.ClientSession(loop=loop)

    try:
        # using session to fetch a large json file which is stored
        # in obj

        print(obj)  # for debugging purposes

    finally:
        await session.close()


def main():
    loop = asyncio.get_event_loop()

    res = 1

    try:
        res = loop.run_until_complete(amain(loop, args))
    except KeyboardInterrupt:
        # silence traceback when pressing ctrl+c
        pass

    loop.close()

    return res


if __name__ == '__main__':
    sys.exit(main())

如果我执行此操作,则 json 对象会打印在 stdout 上,然后突然因此错误而死

$ dwd-get-sensor-file ; echo $?
Traceback (most recent call last):
  File "/home/yanez/anaconda/py3/envs/mondas/bin/dwd-get-sensor-file", line 11, in <module>
    load_entry_point('mondassatellite', 'console_scripts', 'dwd-get-sensor-file')()
  File "/home/yanez/projects/mondassatellite/mondassatellite/mondassatellite/bin/dwd_get_sensor_file.py", line 75, in main
    res = loop.run_until_complete(amain(loop, args))
  File "/home/yanez/anaconda/py3/envs/mondas/lib/python3.7/asyncio/base_events.py", line 579, in run_until_complete
    return future.result()
  File "/home/yanez/projects/mondassatellite/mondassatellite/mondassatellite/bin/dwd_get_sensor_file.py", line 57, in amain
    print(obj)
BlockingIOError: [Errno 11] write could not complete without blocking
1

有趣的是,当我执行代码时将stdout 重定向到这样的文件

$ dwd-get-sensor-file > output.txt ; echo $?
0

异常没有发生,整个输出正确重定向到output.txt

出于测试目的,我将 json 对象转换为字符串,而不是做 print(obj) 我做 sys.stdout.write(obj_as_str) 然后我得到这个 例外:

BlockingIOError: [Errno 11] write could not complete without blocking
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>

我已经搜索过这个BlockingIOError 异常,但我发现的所有线程都与网络套接字或 CI 构建有关。但是我找到了一个 有趣github comment:

make: write error 几乎可以肯定是来自标准输出的 EAGAIN。几乎每个命令行工具都希望 stdout 处于阻塞模式,并且在非阻塞模式下不会正确重试。

所以当我执行这个时

python -c 'import os,sys,fcntl; flags = fcntl.fcntl(sys.stdout, fcntl.F_GETFL); print(flags&os.O_NONBLOCK);'

我得到 2048,这意味着阻塞(或者反过来?我很困惑)。执行后

python -c 'import os,sys,fcntl; flags = fcntl.fcntl(sys.stdout, fcntl.F_GETFL); fcntl.fcntl(sys.stdout, fcntl.F_SETFL, flags&~os.O_NONBLOCK);'

我不再收到 BlockingIOError 异常,但我不喜欢这个解决方案。

所以,我的问题是:在async 函数中写入stdout 时应该如何处理?如果我知道我正在处理stdout,我应该 将stdout 设置为非阻塞并在我的程序退出时将其还原?有没有具体的策略?

【问题讨论】:

    标签: python-3.x python-asyncio


    【解决方案1】:

    试试aiofiles,使用stdout FD作为文件对象。

    aiofiles 通过引入支持将操作委托给单独线程池的文件的异步版本来帮助解决此问题。

    就直接将aiofiles 与FD 一起使用而言,您可以使用wrap(os.write) 扩展aiofiles.os module

    【讨论】:

    • 我将aiofiles 用于其他用途,但我看不到如何将aiofilesstdinstdout 一起使用。至少文档没有明确说明如何做到这一点。
    • 所以你的意思是我可以这样做:aiofiles.os.write = aiofiles.os.wrap(os.write) 然后使用await aiofiles.os.write(sys.stdout.fileno(), b"Hello world\n")
    • 类似的东西,虽然可能需要os.fdopen()? (不确定,int 可能有效)抱歉,今天很忙;)
    猜你喜欢
    • 2014-06-03
    • 1970-01-01
    • 1970-01-01
    • 2016-04-19
    • 1970-01-01
    • 1970-01-01
    • 2019-10-28
    • 1970-01-01
    • 2021-02-20
    相关资源
    最近更新 更多