В python (3.7+) я пытаюсь запустить подпроцесс в качестве менеджера контекста, асинхронно передавая потенциально большие объемы stdout. Проблема в том, что я не могу заставить тело contextmanager работать асинхронно с обратным вызовом stdout. Я пытался использовать потоки, запуская там асинхронную функцию, но тогда я не мог понять, как вернуть объект Process обратно в менеджер контекста.
Итак, вопрос: как я могу получить объект асинхронного процесса из менеджера контекста в основном потоке во время его работы? То есть я хотел бы получить уже и в настоящее время запущенный процесс из open_subprocess() до того, как он будет завершен в следующем коде.
import asyncio
import contextlib
async def read_stream(proc, stream, callback):
while proc.returncode is None:
data = await stream.readline()
if data:
callback(data.decode().rstrip())
else:
break
async def stream_subprocess(cmd, *args, stdout_callback=print):
proc = await asyncio.create_subprocess_exec(
cmd,
*args,
stdout=asyncio.subprocess.PIPE)
read = read_stream(proc, proc.stdout, stdout_callback)
await asyncio.wait([read])
return proc
@contextlib.contextmanager
def open_subprocess(cmd, *args, stdout_callback=print):
proc_coroutine = stream_subprocess(
cmd,
*args,
stdout_callback=stdout_callback)
# The following blocks until proc has finished
# I would like to yield proc while it is running
proc = asyncio.run(proc_coroutine)
yield proc
proc.terminate()
if __name__ == '__main__':
import time
def stdout_callback(data):
print('STDOUT:', data)
with open_subprocess('ping', '-c', '4', 'localhost',
stdout_callback=stdout_callback) as proc:
# The following code only runs after proc completes
# but I would expect these print statements to
# be interleaved with the output from the subprocess
for i in range(2):
print(f'RUNNING SUBPROCESS {proc.pid}...')
time.sleep(1)
print(f'RETURN CODE: {proc.returncode}')
asyncio.create_subprocess_exec()
блокирует и предотвращает вход в asynccontextmanager. - person Johann   schedule 01.08.2019