A non-blocking read on a subprocess.PIPE in Python

后端 未结 29 2666
醉酒成梦
醉酒成梦 2020-11-21 04:49

I\'m using the subprocess module to start a subprocess and connect to its output stream (standard output). I want to be able to execute non-blocking reads on its standard ou

29条回答
  •  别那么骄傲
    2020-11-21 05:16

    Python 3.4 introduces new provisional API for asynchronous IO -- asyncio module.

    The approach is similar to twisted-based answer by @Bryan Ward -- define a protocol and its methods are called as soon as data is ready:

    #!/usr/bin/env python3
    import asyncio
    import os
    
    class SubprocessProtocol(asyncio.SubprocessProtocol):
        def pipe_data_received(self, fd, data):
            if fd == 1: # got stdout data (bytes)
                print(data)
    
        def connection_lost(self, exc):
            loop.stop() # end loop.run_forever()
    
    if os.name == 'nt':
        loop = asyncio.ProactorEventLoop() # for subprocess' pipes on Windows
        asyncio.set_event_loop(loop)
    else:
        loop = asyncio.get_event_loop()
    try:
        loop.run_until_complete(loop.subprocess_exec(SubprocessProtocol, 
            "myprogram.exe", "arg1", "arg2"))
        loop.run_forever()
    finally:
        loop.close()
    

    See "Subprocess" in the docs.

    There is a high-level interface asyncio.create_subprocess_exec() that returns Process objects that allows to read a line asynchroniosly using StreamReader.readline() coroutine (with async/await Python 3.5+ syntax):

    #!/usr/bin/env python3.5
    import asyncio
    import locale
    import sys
    from asyncio.subprocess import PIPE
    from contextlib import closing
    
    async def readline_and_kill(*args):
        # start child process
        process = await asyncio.create_subprocess_exec(*args, stdout=PIPE)
    
        # read line (sequence of bytes ending with b'\n') asynchronously
        async for line in process.stdout:
            print("got line:", line.decode(locale.getpreferredencoding(False)))
            break
        process.kill()
        return await process.wait() # wait for the child process to exit
    
    
    if sys.platform == "win32":
        loop = asyncio.ProactorEventLoop()
        asyncio.set_event_loop(loop)
    else:
        loop = asyncio.get_event_loop()
    
    with closing(loop):
        sys.exit(loop.run_until_complete(readline_and_kill(
            "myprogram.exe", "arg1", "arg2")))
    

    readline_and_kill() performs the following tasks:

    • start subprocess, redirect its stdout to a pipe
    • read a line from subprocess' stdout asynchronously
    • kill subprocess
    • wait for it to exit

    Each step could be limited by timeout seconds if necessary.

提交回复
热议问题