| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  | __all__ = 'create_subprocess_exec', 'create_subprocess_shell' | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | import subprocess | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from . import events | 
					
						
							|  |  |  | from . import protocols | 
					
						
							|  |  |  | from . import streams | 
					
						
							|  |  |  | from . import tasks | 
					
						
							| 
									
										
										
										
											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.""" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-29 21:59:55 -07:00
										 |  |  |     def __init__(self, limit, loop): | 
					
						
							|  |  |  |         super().__init__(loop=loop) | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |         self._limit = limit | 
					
						
							|  |  |  |         self.stdin = self.stdout = self.stderr = None | 
					
						
							|  |  |  |         self._transport = None | 
					
						
							| 
									
										
										
										
											2017-03-02 22:21:18 -06:00
										 |  |  |         self._process_exited = False | 
					
						
							|  |  |  |         self._pipe_fds = [] | 
					
						
							| 
									
										
										
										
											2019-05-07 16:53:19 -04:00
										 |  |  |         self._stdin_closed = self._loop.create_future() | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-14 18:33:40 +02:00
										 |  |  |     def __repr__(self): | 
					
						
							|  |  |  |         info = [self.__class__.__name__] | 
					
						
							|  |  |  |         if self.stdin is not None: | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |             info.append(f'stdin={self.stdin!r}') | 
					
						
							| 
									
										
										
										
											2014-07-14 18:33:40 +02:00
										 |  |  |         if self.stdout is not None: | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |             info.append(f'stdout={self.stdout!r}') | 
					
						
							| 
									
										
										
										
											2014-07-14 18:33:40 +02:00
										 |  |  |         if self.stderr is not None: | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |             info.append(f'stderr={self.stderr!r}') | 
					
						
							|  |  |  |         return '<{}>'.format(' '.join(info)) | 
					
						
							| 
									
										
										
										
											2014-07-14 18:33:40 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |     def connection_made(self, transport): | 
					
						
							|  |  |  |         self._transport = transport | 
					
						
							| 
									
										
										
										
											2019-09-29 21:59:55 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-11-25 17:20:33 +01:00
										 |  |  |         stdout_transport = transport.get_pipe_transport(1) | 
					
						
							|  |  |  |         if stdout_transport is not None: | 
					
						
							| 
									
										
										
										
											2019-09-29 21:59:55 -07:00
										 |  |  |             self.stdout = streams.StreamReader(limit=self._limit, | 
					
						
							|  |  |  |                                                loop=self._loop) | 
					
						
							|  |  |  |             self.stdout.set_transport(stdout_transport) | 
					
						
							| 
									
										
										
										
											2017-03-02 22:21:18 -06:00
										 |  |  |             self._pipe_fds.append(1) | 
					
						
							| 
									
										
										
										
											2014-11-25 17:20:33 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         stderr_transport = transport.get_pipe_transport(2) | 
					
						
							|  |  |  |         if stderr_transport is not None: | 
					
						
							| 
									
										
										
										
											2019-09-29 21:59:55 -07:00
										 |  |  |             self.stderr = streams.StreamReader(limit=self._limit, | 
					
						
							|  |  |  |                                                loop=self._loop) | 
					
						
							|  |  |  |             self.stderr.set_transport(stderr_transport) | 
					
						
							| 
									
										
										
										
											2017-03-02 22:21:18 -06:00
										 |  |  |             self._pipe_fds.append(2) | 
					
						
							| 
									
										
										
										
											2014-11-25 17:20:33 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         stdin_transport = transport.get_pipe_transport(0) | 
					
						
							|  |  |  |         if stdin_transport is not None: | 
					
						
							| 
									
										
										
										
											2019-09-29 21:59:55 -07:00
										 |  |  |             self.stdin = streams.StreamWriter(stdin_transport, | 
					
						
							|  |  |  |                                               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: | 
					
						
							| 
									
										
										
										
											2019-09-29 21:59:55 -07:00
										 |  |  |             reader.feed_data(data) | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def pipe_connection_lost(self, fd, exc): | 
					
						
							|  |  |  |         if fd == 0: | 
					
						
							|  |  |  |             pipe = self.stdin | 
					
						
							|  |  |  |             if pipe is not None: | 
					
						
							|  |  |  |                 pipe.close() | 
					
						
							|  |  |  |             self.connection_lost(exc) | 
					
						
							| 
									
										
										
										
											2019-05-07 16:53:19 -04:00
										 |  |  |             if exc is None: | 
					
						
							|  |  |  |                 self._stdin_closed.set_result(None) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 self._stdin_closed.set_exception(exc) | 
					
						
							| 
									
										
										
										
											2023-05-17 16:45:11 -07:00
										 |  |  |                 # Since calling `wait_closed()` is not mandatory, | 
					
						
							|  |  |  |                 # we shouldn't log the traceback if this is not awaited. | 
					
						
							|  |  |  |                 self._stdin_closed._log_traceback = False | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |             return | 
					
						
							|  |  |  |         if fd == 1: | 
					
						
							|  |  |  |             reader = self.stdout | 
					
						
							|  |  |  |         elif fd == 2: | 
					
						
							|  |  |  |             reader = self.stderr | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             reader = None | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |         if reader is not None: | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |             if exc is None: | 
					
						
							| 
									
										
										
										
											2019-09-29 21:59:55 -07:00
										 |  |  |                 reader.feed_eof() | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2019-09-29 21:59:55 -07:00
										 |  |  |                 reader.set_exception(exc) | 
					
						
							| 
									
										
										
										
											2017-03-02 23:25:31 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-02 22:21:18 -06:00
										 |  |  |         if fd in self._pipe_fds: | 
					
						
							|  |  |  |             self._pipe_fds.remove(fd) | 
					
						
							|  |  |  |         self._maybe_close_transport() | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def process_exited(self): | 
					
						
							| 
									
										
										
										
											2017-03-02 22:21:18 -06:00
										 |  |  |         self._process_exited = True | 
					
						
							|  |  |  |         self._maybe_close_transport() | 
					
						
							| 
									
										
										
										
											2017-03-02 23:25:31 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-02 22:21:18 -06:00
										 |  |  |     def _maybe_close_transport(self): | 
					
						
							|  |  |  |         if len(self._pipe_fds) == 0 and self._process_exited: | 
					
						
							|  |  |  |             self._transport.close() | 
					
						
							|  |  |  |             self._transport = None | 
					
						
							| 
									
										
										
										
											2015-01-15 13:16:02 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-07 16:53:19 -04:00
										 |  |  |     def _get_close_waiter(self, stream): | 
					
						
							|  |  |  |         if stream is self.stdin: | 
					
						
							|  |  |  |             return self._stdin_closed | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | class Process: | 
					
						
							| 
									
										
										
										
											2019-09-29 21:59:55 -07:00
										 |  |  |     def __init__(self, transport, protocol, loop): | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |         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): | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |         return f'<{self.__class__.__name__} {self.pid}>' | 
					
						
							| 
									
										
										
										
											2014-07-14 18:33:40 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |     @property | 
					
						
							|  |  |  |     def returncode(self): | 
					
						
							|  |  |  |         return self._transport.get_returncode() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |     async def wait(self): | 
					
						
							|  |  |  |         """Wait until the process exit and return the process return code.""" | 
					
						
							|  |  |  |         return await 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() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |     async def _feed_stdin(self, input): | 
					
						
							| 
									
										
										
										
											2014-07-17 13:12:03 +02:00
										 |  |  |         debug = self._loop.get_debug() | 
					
						
							| 
									
										
										
										
											2014-07-17 12:25:27 +02:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2023-09-22 15:29:42 +02:00
										 |  |  |             if input is not None: | 
					
						
							|  |  |  |                 self.stdin.write(input) | 
					
						
							|  |  |  |                 if debug: | 
					
						
							|  |  |  |                     logger.debug( | 
					
						
							|  |  |  |                         '%r communicate: feed stdin (%s bytes)', self, len(input)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |             await self.stdin.drain() | 
					
						
							| 
									
										
										
										
											2014-07-17 13:12:03 +02:00
										 |  |  |         except (BrokenPipeError, ConnectionResetError) as exc: | 
					
						
							| 
									
										
										
										
											2023-09-22 15:29:42 +02:00
										 |  |  |             # communicate() ignores BrokenPipeError and ConnectionResetError. | 
					
						
							|  |  |  |             # write() and drain() can raise these exceptions. | 
					
						
							| 
									
										
										
										
											2014-07-17 13:12:03 +02:00
										 |  |  |             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() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |     async def _noop(self): | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |         return None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |     async def _read_stream(self, fd): | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |         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) | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |         output = await 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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |     async def communicate(self, input=None): | 
					
						
							| 
									
										
										
										
											2023-04-27 20:30:26 -04:00
										 |  |  |         if self.stdin 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() | 
					
						
							| 
									
										
										
										
											2020-11-28 10:21:17 +02:00
										 |  |  |         stdin, stdout, stderr = await tasks.gather(stdin, stdout, stderr) | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |         await self.wait() | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |         return (stdout, stderr) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  | async def create_subprocess_shell(cmd, stdin=None, stdout=None, stderr=None, | 
					
						
							| 
									
										
										
										
											2020-11-28 10:21:17 +02:00
										 |  |  |                                   limit=streams._DEFAULT_LIMIT, **kwds): | 
					
						
							|  |  |  |     loop = events.get_running_loop() | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |     protocol_factory = lambda: SubprocessStreamProtocol(limit=limit, | 
					
						
							| 
									
										
										
										
											2019-09-29 21:59:55 -07:00
										 |  |  |                                                         loop=loop) | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |     transport, protocol = await loop.subprocess_shell( | 
					
						
							|  |  |  |         protocol_factory, | 
					
						
							|  |  |  |         cmd, stdin=stdin, stdout=stdout, | 
					
						
							|  |  |  |         stderr=stderr, **kwds) | 
					
						
							| 
									
										
										
										
											2019-09-29 21:59:55 -07:00
										 |  |  |     return Process(transport, protocol, loop) | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | async def create_subprocess_exec(program, *args, stdin=None, stdout=None, | 
					
						
							| 
									
										
										
										
											2020-11-28 10:21:17 +02:00
										 |  |  |                                  stderr=None, limit=streams._DEFAULT_LIMIT, | 
					
						
							|  |  |  |                                  **kwds): | 
					
						
							|  |  |  |     loop = events.get_running_loop() | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |     protocol_factory = lambda: SubprocessStreamProtocol(limit=limit, | 
					
						
							| 
									
										
										
										
											2019-09-29 21:59:55 -07:00
										 |  |  |                                                         loop=loop) | 
					
						
							| 
									
										
										
										
											2017-12-09 00:23:48 +02:00
										 |  |  |     transport, protocol = await loop.subprocess_exec( | 
					
						
							|  |  |  |         protocol_factory, | 
					
						
							|  |  |  |         program, *args, | 
					
						
							|  |  |  |         stdin=stdin, stdout=stdout, | 
					
						
							|  |  |  |         stderr=stderr, **kwds) | 
					
						
							| 
									
										
										
										
											2019-09-29 21:59:55 -07:00
										 |  |  |     return Process(transport, protocol, loop) |