| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  | __all__ = ['create_subprocess_exec', 'create_subprocess_shell'] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import subprocess | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from . import events | 
					
						
							|  |  |  | from . import protocols | 
					
						
							|  |  |  | from . import streams | 
					
						
							|  |  |  | from . import tasks | 
					
						
							| 
									
										
										
										
											2014-06-29 00:46:45 +02:00
										 |  |  | from .coroutines import coroutine | 
					
						
							| 
									
										
										
										
											2014-07-14 18:33:40 +02:00
										 |  |  | from .log import logger | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PIPE = subprocess.PIPE | 
					
						
							|  |  |  | STDOUT = subprocess.STDOUT | 
					
						
							|  |  |  | DEVNULL = subprocess.DEVNULL | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SubprocessStreamProtocol(streams.FlowControlMixin, | 
					
						
							|  |  |  |                                protocols.SubprocessProtocol): | 
					
						
							|  |  |  |     """Like StreamReaderProtocol, but for a subprocess.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, limit, loop): | 
					
						
							|  |  |  |         super().__init__(loop=loop) | 
					
						
							|  |  |  |         self._limit = limit | 
					
						
							|  |  |  |         self.stdin = self.stdout = self.stderr = None | 
					
						
							|  |  |  |         self._transport = None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-14 18:33:40 +02:00
										 |  |  |     def __repr__(self): | 
					
						
							|  |  |  |         info = [self.__class__.__name__] | 
					
						
							|  |  |  |         if self.stdin is not None: | 
					
						
							|  |  |  |             info.append('stdin=%r' % self.stdin) | 
					
						
							|  |  |  |         if self.stdout is not None: | 
					
						
							|  |  |  |             info.append('stdout=%r' % self.stdout) | 
					
						
							|  |  |  |         if self.stderr is not None: | 
					
						
							|  |  |  |             info.append('stderr=%r' % self.stderr) | 
					
						
							|  |  |  |         return '<%s>' % ' '.join(info) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |     def connection_made(self, transport): | 
					
						
							|  |  |  |         self._transport = transport | 
					
						
							| 
									
										
										
										
											2014-11-25 17:20:33 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         stdout_transport = transport.get_pipe_transport(1) | 
					
						
							|  |  |  |         if stdout_transport is not None: | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |             self.stdout = streams.StreamReader(limit=self._limit, | 
					
						
							|  |  |  |                                                loop=self._loop) | 
					
						
							| 
									
										
										
										
											2014-11-25 17:20:33 +01:00
										 |  |  |             self.stdout.set_transport(stdout_transport) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         stderr_transport = transport.get_pipe_transport(2) | 
					
						
							|  |  |  |         if stderr_transport is not None: | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |             self.stderr = streams.StreamReader(limit=self._limit, | 
					
						
							|  |  |  |                                                loop=self._loop) | 
					
						
							| 
									
										
										
										
											2014-11-25 17:20:33 +01:00
										 |  |  |             self.stderr.set_transport(stderr_transport) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         stdin_transport = transport.get_pipe_transport(0) | 
					
						
							|  |  |  |         if stdin_transport is not None: | 
					
						
							|  |  |  |             self.stdin = streams.StreamWriter(stdin_transport, | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |                                               protocol=self, | 
					
						
							|  |  |  |                                               reader=None, | 
					
						
							|  |  |  |                                               loop=self._loop) | 
					
						
							| 
									
										
										
										
											2015-01-14 02:10:33 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |     def pipe_data_received(self, fd, data): | 
					
						
							|  |  |  |         if fd == 1: | 
					
						
							|  |  |  |             reader = self.stdout | 
					
						
							|  |  |  |         elif fd == 2: | 
					
						
							|  |  |  |             reader = self.stderr | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             reader = None | 
					
						
							|  |  |  |         if reader is not None: | 
					
						
							|  |  |  |             reader.feed_data(data) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def pipe_connection_lost(self, fd, exc): | 
					
						
							|  |  |  |         if fd == 0: | 
					
						
							|  |  |  |             pipe = self.stdin | 
					
						
							|  |  |  |             if pipe is not None: | 
					
						
							|  |  |  |                 pipe.close() | 
					
						
							|  |  |  |             self.connection_lost(exc) | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         if fd == 1: | 
					
						
							|  |  |  |             reader = self.stdout | 
					
						
							|  |  |  |         elif fd == 2: | 
					
						
							|  |  |  |             reader = self.stderr | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             reader = None | 
					
						
							|  |  |  |         if reader != None: | 
					
						
							|  |  |  |             if exc is None: | 
					
						
							|  |  |  |                 reader.feed_eof() | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 reader.set_exception(exc) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def process_exited(self): | 
					
						
							| 
									
										
										
										
											2015-01-15 13:16:02 +01:00
										 |  |  |         self._transport.close() | 
					
						
							|  |  |  |         self._transport = None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | class Process: | 
					
						
							|  |  |  |     def __init__(self, transport, protocol, loop): | 
					
						
							|  |  |  |         self._transport = transport | 
					
						
							|  |  |  |         self._protocol = protocol | 
					
						
							|  |  |  |         self._loop = loop | 
					
						
							|  |  |  |         self.stdin = protocol.stdin | 
					
						
							|  |  |  |         self.stdout = protocol.stdout | 
					
						
							|  |  |  |         self.stderr = protocol.stderr | 
					
						
							|  |  |  |         self.pid = transport.get_pid() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-14 18:33:40 +02:00
										 |  |  |     def __repr__(self): | 
					
						
							|  |  |  |         return '<%s %s>' % (self.__class__.__name__, self.pid) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |     @property | 
					
						
							|  |  |  |     def returncode(self): | 
					
						
							|  |  |  |         return self._transport.get_returncode() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-29 00:46:45 +02:00
										 |  |  |     @coroutine | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |     def wait(self): | 
					
						
							| 
									
										
										
										
											2015-01-30 00:05:19 +01:00
										 |  |  |         """Wait until the process exit and return the process return code.
 | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-30 00:05:19 +01:00
										 |  |  |         This method is a coroutine."""
 | 
					
						
							| 
									
										
										
										
											2015-01-30 00:16:14 +01:00
										 |  |  |         return (yield from self._transport._wait()) | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def send_signal(self, signal): | 
					
						
							|  |  |  |         self._transport.send_signal(signal) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def terminate(self): | 
					
						
							|  |  |  |         self._transport.terminate() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def kill(self): | 
					
						
							|  |  |  |         self._transport.kill() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-29 00:46:45 +02:00
										 |  |  |     @coroutine | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |     def _feed_stdin(self, input): | 
					
						
							| 
									
										
										
										
											2014-07-17 13:12:03 +02:00
										 |  |  |         debug = self._loop.get_debug() | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |         self.stdin.write(input) | 
					
						
							| 
									
										
										
										
											2014-07-17 13:12:03 +02:00
										 |  |  |         if debug: | 
					
						
							| 
									
										
										
										
											2014-07-14 18:33:40 +02:00
										 |  |  |             logger.debug('%r communicate: feed stdin (%s bytes)', | 
					
						
							|  |  |  |                         self, len(input)) | 
					
						
							| 
									
										
										
										
											2014-07-17 12:25:27 +02:00
										 |  |  |         try: | 
					
						
							|  |  |  |             yield from self.stdin.drain() | 
					
						
							| 
									
										
										
										
											2014-07-17 13:12:03 +02:00
										 |  |  |         except (BrokenPipeError, ConnectionResetError) as exc: | 
					
						
							|  |  |  |             # communicate() ignores BrokenPipeError and ConnectionResetError | 
					
						
							|  |  |  |             if debug: | 
					
						
							|  |  |  |                 logger.debug('%r communicate: stdin got %r', self, exc) | 
					
						
							| 
									
										
										
										
											2014-07-14 18:33:40 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-17 13:12:03 +02:00
										 |  |  |         if debug: | 
					
						
							| 
									
										
										
										
											2014-07-14 18:33:40 +02:00
										 |  |  |             logger.debug('%r communicate: close stdin', self) | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |         self.stdin.close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-29 00:46:45 +02:00
										 |  |  |     @coroutine | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |     def _noop(self): | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-29 00:46:45 +02:00
										 |  |  |     @coroutine | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |     def _read_stream(self, fd): | 
					
						
							|  |  |  |         transport = self._transport.get_pipe_transport(fd) | 
					
						
							|  |  |  |         if fd == 2: | 
					
						
							|  |  |  |             stream = self.stderr | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             assert fd == 1 | 
					
						
							|  |  |  |             stream = self.stdout | 
					
						
							| 
									
										
										
										
											2014-07-14 18:33:40 +02:00
										 |  |  |         if self._loop.get_debug(): | 
					
						
							|  |  |  |             name = 'stdout' if fd == 1 else 'stderr' | 
					
						
							|  |  |  |             logger.debug('%r communicate: read %s', self, name) | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |         output = yield from stream.read() | 
					
						
							| 
									
										
										
										
											2014-07-14 18:33:40 +02:00
										 |  |  |         if self._loop.get_debug(): | 
					
						
							|  |  |  |             name = 'stdout' if fd == 1 else 'stderr' | 
					
						
							|  |  |  |             logger.debug('%r communicate: close %s', self, name) | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |         transport.close() | 
					
						
							|  |  |  |         return output | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-29 00:46:45 +02:00
										 |  |  |     @coroutine | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |     def communicate(self, input=None): | 
					
						
							| 
									
										
										
										
											2016-05-13 15:35:28 -04:00
										 |  |  |         if input is not None: | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |             stdin = self._feed_stdin(input) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             stdin = self._noop() | 
					
						
							|  |  |  |         if self.stdout is not None: | 
					
						
							|  |  |  |             stdout = self._read_stream(1) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             stdout = self._noop() | 
					
						
							|  |  |  |         if self.stderr is not None: | 
					
						
							|  |  |  |             stderr = self._read_stream(2) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             stderr = self._noop() | 
					
						
							|  |  |  |         stdin, stdout, stderr = yield from tasks.gather(stdin, stdout, stderr, | 
					
						
							| 
									
										
										
										
											2014-02-20 10:12:59 +01:00
										 |  |  |                                                         loop=self._loop) | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |         yield from self.wait() | 
					
						
							|  |  |  |         return (stdout, stderr) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-29 00:46:45 +02:00
										 |  |  | @coroutine | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  | def create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, | 
					
						
							|  |  |  |                             loop=None, limit=streams._DEFAULT_LIMIT, **kwds): | 
					
						
							|  |  |  |     if loop is None: | 
					
						
							|  |  |  |         loop = events.get_event_loop() | 
					
						
							|  |  |  |     protocol_factory = lambda: SubprocessStreamProtocol(limit=limit, | 
					
						
							|  |  |  |                                                         loop=loop) | 
					
						
							|  |  |  |     transport, protocol = yield from loop.subprocess_shell( | 
					
						
							|  |  |  |                                             protocol_factory, | 
					
						
							|  |  |  |                                             cmd, stdin=stdin, stdout=stdout, | 
					
						
							|  |  |  |                                             stderr=stderr, **kwds) | 
					
						
							|  |  |  |     return Process(transport, protocol, loop) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-29 00:46:45 +02:00
										 |  |  | @coroutine | 
					
						
							| 
									
										
										
										
											2014-02-18 22:56:15 -05:00
										 |  |  | def create_subprocess_exec(program, *args, stdin=None, stdout=None, | 
					
						
							|  |  |  |                            stderr=None, loop=None, | 
					
						
							|  |  |  |                            limit=streams._DEFAULT_LIMIT, **kwds): | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |     if loop is None: | 
					
						
							|  |  |  |         loop = events.get_event_loop() | 
					
						
							|  |  |  |     protocol_factory = lambda: SubprocessStreamProtocol(limit=limit, | 
					
						
							|  |  |  |                                                         loop=loop) | 
					
						
							|  |  |  |     transport, protocol = yield from loop.subprocess_exec( | 
					
						
							|  |  |  |                                             protocol_factory, | 
					
						
							| 
									
										
										
										
											2014-02-11 11:44:56 +01:00
										 |  |  |                                             program, *args, | 
					
						
							|  |  |  |                                             stdin=stdin, stdout=stdout, | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |                                             stderr=stderr, **kwds) | 
					
						
							|  |  |  |     return Process(transport, protocol, loop) |