def _testPipeLoggerToPipe(self, test_string, loop=None): """ Test PipeLogger writing to a pipe connected to a PipeReader. This verifies that PipeLogger does not deadlock when writing to a pipe that's drained by a PipeReader running in the same process (requires non-blocking write). """ producer = PopenProcess(proc=subprocess.Popen( ["bash", "-c", self._echo_cmd % test_string], stdout=subprocess.PIPE, stderr=subprocess.STDOUT), scheduler=loop) pr, pw = os.pipe() consumer = producer.pipe_reader = PipeLogger( background=True, input_fd=producer.proc.stdout, log_file_path=os.fdopen(pw, 'wb', 0)) reader = _reader(pr, loop=loop) yield producer.async_start() content = yield reader yield producer.async_wait() yield consumer.async_wait() self.assertEqual(producer.returncode, os.EX_OK) self.assertEqual(consumer.returncode, os.EX_OK) coroutine_return(content.decode('ascii', 'replace'))
async def _testPipeLoggerToPipe(self, test_string, loop): """ Test PipeLogger writing to a pipe connected to a PipeReader. This verifies that PipeLogger does not deadlock when writing to a pipe that's drained by a PipeReader running in the same process (requires non-blocking write). """ input_fd, writer_pipe = os.pipe() _set_nonblocking(writer_pipe) writer_pipe = os.fdopen(writer_pipe, 'wb', 0) writer = asyncio.ensure_future( _writer(writer_pipe, test_string.encode('ascii'))) writer.add_done_callback(lambda writer: writer_pipe.close()) pr, pw = os.pipe() consumer = PipeLogger(background=True, input_fd=input_fd, log_file_path=os.fdopen(pw, 'wb', 0), scheduler=loop) consumer.start() # Before starting the reader, wait here for a moment, in order # to exercise PipeLogger's handling of EAGAIN during write. await asyncio.wait([writer], timeout=0.01) reader = _reader(pr) await writer content = await reader await consumer.async_wait() self.assertEqual(consumer.returncode, os.EX_OK) return content.decode('ascii', 'replace')
def communicate(self, input=None): """ Read data from stdout and stderr, until end-of-file is reached. Wait for process to terminate. @param input: stdin content to write @type input: bytes @return: tuple (stdout_data, stderr_data) @rtype: asyncio.Future (or compatible) """ futures = [] for input_file in (self._proc.stdout, self._proc.stderr): if input_file is None: future = self._loop.create_future() future.set_result(None) else: future = _reader(input_file, loop=self._loop) futures.append(future) writer = None if input is not None: if self._proc.stdin is None: raise TypeError( 'communicate: expected file or int, got {}'.format( type(self._proc.stdin))) stdin = self._proc.stdin stdin = os.fdopen(stdin, 'wb', 0) if isinstance(stdin, int) else stdin _set_nonblocking(stdin.fileno()) writer = asyncio.ensure_future(_writer(stdin, input, loop=self._loop), loop=self._loop) writer.add_done_callback(lambda writer: stdin.close()) try: yield asyncio.wait(futures + [self.wait()], loop=self._loop) finally: if writer is not None: if writer.done(): # Consume expected exceptions. try: writer.result() except EnvironmentError: # This is normal if the other end of the pipe was closed. pass else: writer.cancel() coroutine_return(tuple(future.result() for future in futures))