import asyncio import logging import sys lgr = logging.getLogger(__name__) class Protocol(asyncio.SubprocessProtocol): def __init__(self, exit_future): self.exit_future = exit_future self.output = bytearray() def pipe_data_received(self, fd, data): lgr.debug("pipe_data_received(): fd=%d, data=%r", fd, data) self.output.extend(data) def process_exited(self): lgr.debug("process_exited() called") self.exit_future.set_result(True) async def get_stdout(): loop = asyncio.get_running_loop() exit_future = asyncio.Future(loop=loop) transport, protocol = await loop.subprocess_exec( lambda: Protocol(exit_future), "printf", "ok", stdin=None, stderr=None) await exit_future transport.close() return bytes(protocol.output) if __name__ == '__main__': import tempfile logfile = tempfile.NamedTemporaryFile( prefix="asyncio-debug-", suffix=".log", delete=False) logging.basicConfig(format="%(levelname)s: %(message)s", level=logging.DEBUG, filemode="w", filename=logfile.name) ntimes_arg = sys.argv[1] if len(sys.argv) > 1 else "100" ntimes_width = len(ntimes_arg) ntimes = int(ntimes_arg) for i in range(1, ntimes + 1): lgr.debug("Starting iteration %d", i) print(f"Iteration: {i:{ntimes_width}d} of {ntimes}", end="\r") stdout = asyncio.run(get_stdout()) if not stdout: lgr.debug("Failed on iteration %d", i) print(f"\nFailed on iteration {i}\nlogfile: {logfile.name}") sys.exit(1) print("\nEvery iteration captured stdout as expected")