| 
									
										
										
										
											2013-10-30 14:56:49 -07:00
										 |  |  | import collections | 
					
						
							|  |  |  | import subprocess | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from . import protocols | 
					
						
							|  |  |  | from . import transports | 
					
						
							| 
									
										
										
										
											2014-06-29 00:46:45 +02:00
										 |  |  | from .coroutines import coroutine | 
					
						
							| 
									
										
										
										
											2014-07-14 18:33:40 +02:00
										 |  |  | from .log import logger | 
					
						
							| 
									
										
										
										
											2013-10-30 14:56:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class BaseSubprocessTransport(transports.SubprocessTransport): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, loop, protocol, args, shell, | 
					
						
							|  |  |  |                  stdin, stdout, stderr, bufsize, | 
					
						
							|  |  |  |                  extra=None, **kwargs): | 
					
						
							|  |  |  |         super().__init__(extra) | 
					
						
							|  |  |  |         self._protocol = protocol | 
					
						
							|  |  |  |         self._loop = loop | 
					
						
							| 
									
										
										
										
											2014-07-14 18:33:40 +02:00
										 |  |  |         self._pid = None | 
					
						
							| 
									
										
										
										
											2013-10-30 14:56:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         self._pipes = {} | 
					
						
							|  |  |  |         if stdin == subprocess.PIPE: | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |             self._pipes[0] = None | 
					
						
							| 
									
										
										
										
											2013-10-30 14:56:49 -07:00
										 |  |  |         if stdout == subprocess.PIPE: | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |             self._pipes[1] = None | 
					
						
							| 
									
										
										
										
											2013-10-30 14:56:49 -07:00
										 |  |  |         if stderr == subprocess.PIPE: | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |             self._pipes[2] = None | 
					
						
							| 
									
										
										
										
											2013-10-30 14:56:49 -07:00
										 |  |  |         self._pending_calls = collections.deque() | 
					
						
							|  |  |  |         self._finished = False | 
					
						
							|  |  |  |         self._returncode = None | 
					
						
							|  |  |  |         self._start(args=args, shell=shell, stdin=stdin, stdout=stdout, | 
					
						
							|  |  |  |                     stderr=stderr, bufsize=bufsize, **kwargs) | 
					
						
							| 
									
										
										
										
											2014-07-14 18:33:40 +02:00
										 |  |  |         self._pid = self._proc.pid | 
					
						
							| 
									
										
										
										
											2013-10-30 14:56:49 -07:00
										 |  |  |         self._extra['subprocess'] = self._proc | 
					
						
							| 
									
										
										
										
											2014-07-14 18:33:40 +02:00
										 |  |  |         if self._loop.get_debug(): | 
					
						
							|  |  |  |             if isinstance(args, (bytes, str)): | 
					
						
							|  |  |  |                 program = args | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 program = args[0] | 
					
						
							|  |  |  |             logger.debug('process %r created: pid %s', | 
					
						
							|  |  |  |                          program, self._pid) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self): | 
					
						
							|  |  |  |         info = [self.__class__.__name__, 'pid=%s' % self._pid] | 
					
						
							|  |  |  |         if self._returncode is not None: | 
					
						
							|  |  |  |             info.append('returncode=%s' % self._returncode) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         stdin = self._pipes.get(0) | 
					
						
							|  |  |  |         if stdin is not None: | 
					
						
							|  |  |  |             info.append('stdin=%s' % stdin.pipe) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         stdout = self._pipes.get(1) | 
					
						
							|  |  |  |         stderr = self._pipes.get(2) | 
					
						
							|  |  |  |         if stdout is not None and stderr is stdout: | 
					
						
							|  |  |  |             info.append('stdout=stderr=%s' % stdout.pipe) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             if stdout is not None: | 
					
						
							|  |  |  |                 info.append('stdout=%s' % stdout.pipe) | 
					
						
							|  |  |  |             if stderr is not None: | 
					
						
							|  |  |  |                 info.append('stderr=%s' % stderr.pipe) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return '<%s>' % ' '.join(info) | 
					
						
							| 
									
										
										
										
											2013-10-30 14:56:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _start(self, args, shell, stdin, stdout, stderr, bufsize, **kwargs): | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _make_write_subprocess_pipe_proto(self, fd): | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _make_read_subprocess_pipe_proto(self, fd): | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def close(self): | 
					
						
							|  |  |  |         for proto in self._pipes.values(): | 
					
						
							|  |  |  |             proto.pipe.close() | 
					
						
							|  |  |  |         if self._returncode is None: | 
					
						
							|  |  |  |             self.terminate() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_pid(self): | 
					
						
							| 
									
										
										
										
											2014-07-14 18:33:40 +02:00
										 |  |  |         return self._pid | 
					
						
							| 
									
										
										
										
											2013-10-30 14:56:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def get_returncode(self): | 
					
						
							|  |  |  |         return self._returncode | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_pipe_transport(self, fd): | 
					
						
							|  |  |  |         if fd in self._pipes: | 
					
						
							|  |  |  |             return self._pipes[fd].pipe | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def send_signal(self, signal): | 
					
						
							|  |  |  |         self._proc.send_signal(signal) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def terminate(self): | 
					
						
							|  |  |  |         self._proc.terminate() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def kill(self): | 
					
						
							|  |  |  |         self._proc.kill() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-29 00:46:45 +02:00
										 |  |  |     @coroutine | 
					
						
							| 
									
										
										
										
											2013-10-30 14:56:49 -07:00
										 |  |  |     def _post_init(self): | 
					
						
							|  |  |  |         proc = self._proc | 
					
						
							|  |  |  |         loop = self._loop | 
					
						
							|  |  |  |         if proc.stdin is not None: | 
					
						
							| 
									
										
										
										
											2014-01-29 14:22:56 -08:00
										 |  |  |             _, pipe = yield from loop.connect_write_pipe( | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |                 lambda: WriteSubprocessPipeProto(self, 0), | 
					
						
							| 
									
										
										
										
											2013-10-30 14:56:49 -07:00
										 |  |  |                 proc.stdin) | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |             self._pipes[0] = pipe | 
					
						
							| 
									
										
										
										
											2013-10-30 14:56:49 -07:00
										 |  |  |         if proc.stdout is not None: | 
					
						
							| 
									
										
										
										
											2014-01-29 14:22:56 -08:00
										 |  |  |             _, pipe = yield from loop.connect_read_pipe( | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |                 lambda: ReadSubprocessPipeProto(self, 1), | 
					
						
							| 
									
										
										
										
											2013-10-30 14:56:49 -07:00
										 |  |  |                 proc.stdout) | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |             self._pipes[1] = pipe | 
					
						
							| 
									
										
										
										
											2013-10-30 14:56:49 -07:00
										 |  |  |         if proc.stderr is not None: | 
					
						
							| 
									
										
										
										
											2014-01-29 14:22:56 -08:00
										 |  |  |             _, pipe = yield from loop.connect_read_pipe( | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |                 lambda: ReadSubprocessPipeProto(self, 2), | 
					
						
							| 
									
										
										
										
											2013-10-30 14:56:49 -07:00
										 |  |  |                 proc.stderr) | 
					
						
							| 
									
										
										
										
											2014-02-01 22:49:59 +01:00
										 |  |  |             self._pipes[2] = pipe | 
					
						
							| 
									
										
										
										
											2014-01-29 14:22:56 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         assert self._pending_calls is not None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self._loop.call_soon(self._protocol.connection_made, self) | 
					
						
							|  |  |  |         for callback, data in self._pending_calls: | 
					
						
							|  |  |  |             self._loop.call_soon(callback, *data) | 
					
						
							|  |  |  |         self._pending_calls = None | 
					
						
							| 
									
										
										
										
											2013-10-30 14:56:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _call(self, cb, *data): | 
					
						
							|  |  |  |         if self._pending_calls is not None: | 
					
						
							|  |  |  |             self._pending_calls.append((cb, data)) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self._loop.call_soon(cb, *data) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _pipe_connection_lost(self, fd, exc): | 
					
						
							|  |  |  |         self._call(self._protocol.pipe_connection_lost, fd, exc) | 
					
						
							|  |  |  |         self._try_finish() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _pipe_data_received(self, fd, data): | 
					
						
							|  |  |  |         self._call(self._protocol.pipe_data_received, fd, data) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _process_exited(self, returncode): | 
					
						
							|  |  |  |         assert returncode is not None, returncode | 
					
						
							|  |  |  |         assert self._returncode is None, self._returncode | 
					
						
							| 
									
										
										
										
											2014-07-14 18:33:40 +02:00
										 |  |  |         if self._loop.get_debug(): | 
					
						
							|  |  |  |             logger.info('%r exited with return code %r', | 
					
						
							|  |  |  |                         self, returncode) | 
					
						
							| 
									
										
										
										
											2013-10-30 14:56:49 -07:00
										 |  |  |         self._returncode = returncode | 
					
						
							|  |  |  |         self._call(self._protocol.process_exited) | 
					
						
							|  |  |  |         self._try_finish() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _try_finish(self): | 
					
						
							|  |  |  |         assert not self._finished | 
					
						
							|  |  |  |         if self._returncode is None: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         if all(p is not None and p.disconnected | 
					
						
							|  |  |  |                for p in self._pipes.values()): | 
					
						
							|  |  |  |             self._finished = True | 
					
						
							|  |  |  |             self._loop.call_soon(self._call_connection_lost, None) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _call_connection_lost(self, exc): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             self._protocol.connection_lost(exc) | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             self._proc = None | 
					
						
							|  |  |  |             self._protocol = None | 
					
						
							|  |  |  |             self._loop = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class WriteSubprocessPipeProto(protocols.BaseProtocol): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, proc, fd): | 
					
						
							|  |  |  |         self.proc = proc | 
					
						
							|  |  |  |         self.fd = fd | 
					
						
							| 
									
										
										
										
											2014-01-29 14:22:56 -08:00
										 |  |  |         self.pipe = None | 
					
						
							| 
									
										
										
										
											2013-10-30 14:56:49 -07:00
										 |  |  |         self.disconnected = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def connection_made(self, transport): | 
					
						
							|  |  |  |         self.pipe = transport | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-14 18:33:40 +02:00
										 |  |  |     def __repr__(self): | 
					
						
							|  |  |  |         return ('<%s fd=%s pipe=%r>' | 
					
						
							|  |  |  |                 % (self.__class__.__name__, self.fd, self.pipe)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-30 14:56:49 -07:00
										 |  |  |     def connection_lost(self, exc): | 
					
						
							|  |  |  |         self.disconnected = True | 
					
						
							|  |  |  |         self.proc._pipe_connection_lost(self.fd, exc) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-29 14:28:15 -08:00
										 |  |  |     def pause_writing(self): | 
					
						
							|  |  |  |         self.proc._protocol.pause_writing() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def resume_writing(self): | 
					
						
							|  |  |  |         self.proc._protocol.resume_writing() | 
					
						
							| 
									
										
										
										
											2013-10-30 14:56:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ReadSubprocessPipeProto(WriteSubprocessPipeProto, | 
					
						
							|  |  |  |                               protocols.Protocol): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def data_received(self, data): | 
					
						
							|  |  |  |         self.proc._pipe_data_received(self.fd, data) |