blob: 81698b09850d106ff0cb8d3a46e3c6d3b9ec502a [file] [log] [blame]
Guido van Rossum0016e1d2013-10-30 14:56:49 -07001import collections
2import subprocess
3
4from . import protocols
Guido van Rossum0016e1d2013-10-30 14:56:49 -07005from . import transports
Victor Stinnerf951d282014-06-29 00:46:45 +02006from .coroutines import coroutine
Victor Stinneracdb7822014-07-14 18:33:40 +02007from .log import logger
Guido van Rossum0016e1d2013-10-30 14:56:49 -07008
9
Guido van Rossum0016e1d2013-10-30 14:56:49 -070010class BaseSubprocessTransport(transports.SubprocessTransport):
11
12 def __init__(self, loop, protocol, args, shell,
13 stdin, stdout, stderr, bufsize,
14 extra=None, **kwargs):
15 super().__init__(extra)
16 self._protocol = protocol
17 self._loop = loop
Victor Stinneracdb7822014-07-14 18:33:40 +020018 self._pid = None
Guido van Rossum0016e1d2013-10-30 14:56:49 -070019
20 self._pipes = {}
21 if stdin == subprocess.PIPE:
Victor Stinner915bcb02014-02-01 22:49:59 +010022 self._pipes[0] = None
Guido van Rossum0016e1d2013-10-30 14:56:49 -070023 if stdout == subprocess.PIPE:
Victor Stinner915bcb02014-02-01 22:49:59 +010024 self._pipes[1] = None
Guido van Rossum0016e1d2013-10-30 14:56:49 -070025 if stderr == subprocess.PIPE:
Victor Stinner915bcb02014-02-01 22:49:59 +010026 self._pipes[2] = None
Guido van Rossum0016e1d2013-10-30 14:56:49 -070027 self._pending_calls = collections.deque()
28 self._finished = False
29 self._returncode = None
30 self._start(args=args, shell=shell, stdin=stdin, stdout=stdout,
31 stderr=stderr, bufsize=bufsize, **kwargs)
Victor Stinneracdb7822014-07-14 18:33:40 +020032 self._pid = self._proc.pid
Guido van Rossum0016e1d2013-10-30 14:56:49 -070033 self._extra['subprocess'] = self._proc
Victor Stinneracdb7822014-07-14 18:33:40 +020034 if self._loop.get_debug():
35 if isinstance(args, (bytes, str)):
36 program = args
37 else:
38 program = args[0]
39 logger.debug('process %r created: pid %s',
40 program, self._pid)
41
42 def __repr__(self):
43 info = [self.__class__.__name__, 'pid=%s' % self._pid]
44 if self._returncode is not None:
45 info.append('returncode=%s' % self._returncode)
46
47 stdin = self._pipes.get(0)
48 if stdin is not None:
49 info.append('stdin=%s' % stdin.pipe)
50
51 stdout = self._pipes.get(1)
52 stderr = self._pipes.get(2)
53 if stdout is not None and stderr is stdout:
54 info.append('stdout=stderr=%s' % stdout.pipe)
55 else:
56 if stdout is not None:
57 info.append('stdout=%s' % stdout.pipe)
58 if stderr is not None:
59 info.append('stderr=%s' % stderr.pipe)
60
61 return '<%s>' % ' '.join(info)
Guido van Rossum0016e1d2013-10-30 14:56:49 -070062
63 def _start(self, args, shell, stdin, stdout, stderr, bufsize, **kwargs):
64 raise NotImplementedError
65
66 def _make_write_subprocess_pipe_proto(self, fd):
67 raise NotImplementedError
68
69 def _make_read_subprocess_pipe_proto(self, fd):
70 raise NotImplementedError
71
72 def close(self):
73 for proto in self._pipes.values():
74 proto.pipe.close()
75 if self._returncode is None:
76 self.terminate()
77
78 def get_pid(self):
Victor Stinneracdb7822014-07-14 18:33:40 +020079 return self._pid
Guido van Rossum0016e1d2013-10-30 14:56:49 -070080
81 def get_returncode(self):
82 return self._returncode
83
84 def get_pipe_transport(self, fd):
85 if fd in self._pipes:
86 return self._pipes[fd].pipe
87 else:
88 return None
89
90 def send_signal(self, signal):
91 self._proc.send_signal(signal)
92
93 def terminate(self):
94 self._proc.terminate()
95
96 def kill(self):
97 self._proc.kill()
98
Victor Stinnerf951d282014-06-29 00:46:45 +020099 @coroutine
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700100 def _post_init(self):
101 proc = self._proc
102 loop = self._loop
103 if proc.stdin is not None:
Victor Stinneraaabc4f2014-01-29 14:22:56 -0800104 _, pipe = yield from loop.connect_write_pipe(
Victor Stinner915bcb02014-02-01 22:49:59 +0100105 lambda: WriteSubprocessPipeProto(self, 0),
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700106 proc.stdin)
Victor Stinner915bcb02014-02-01 22:49:59 +0100107 self._pipes[0] = pipe
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700108 if proc.stdout is not None:
Victor Stinneraaabc4f2014-01-29 14:22:56 -0800109 _, pipe = yield from loop.connect_read_pipe(
Victor Stinner915bcb02014-02-01 22:49:59 +0100110 lambda: ReadSubprocessPipeProto(self, 1),
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700111 proc.stdout)
Victor Stinner915bcb02014-02-01 22:49:59 +0100112 self._pipes[1] = pipe
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700113 if proc.stderr is not None:
Victor Stinneraaabc4f2014-01-29 14:22:56 -0800114 _, pipe = yield from loop.connect_read_pipe(
Victor Stinner915bcb02014-02-01 22:49:59 +0100115 lambda: ReadSubprocessPipeProto(self, 2),
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700116 proc.stderr)
Victor Stinner915bcb02014-02-01 22:49:59 +0100117 self._pipes[2] = pipe
Victor Stinneraaabc4f2014-01-29 14:22:56 -0800118
119 assert self._pending_calls is not None
120
121 self._loop.call_soon(self._protocol.connection_made, self)
122 for callback, data in self._pending_calls:
123 self._loop.call_soon(callback, *data)
124 self._pending_calls = None
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700125
126 def _call(self, cb, *data):
127 if self._pending_calls is not None:
128 self._pending_calls.append((cb, data))
129 else:
130 self._loop.call_soon(cb, *data)
131
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700132 def _pipe_connection_lost(self, fd, exc):
133 self._call(self._protocol.pipe_connection_lost, fd, exc)
134 self._try_finish()
135
136 def _pipe_data_received(self, fd, data):
137 self._call(self._protocol.pipe_data_received, fd, data)
138
139 def _process_exited(self, returncode):
140 assert returncode is not None, returncode
141 assert self._returncode is None, self._returncode
Victor Stinneracdb7822014-07-14 18:33:40 +0200142 if self._loop.get_debug():
143 logger.info('%r exited with return code %r',
144 self, returncode)
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700145 self._returncode = returncode
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700146 self._call(self._protocol.process_exited)
147 self._try_finish()
148
149 def _try_finish(self):
150 assert not self._finished
151 if self._returncode is None:
152 return
153 if all(p is not None and p.disconnected
154 for p in self._pipes.values()):
155 self._finished = True
Victor Stinner1b9763d2014-12-18 23:47:27 +0100156 self._call(self._call_connection_lost, None)
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700157
158 def _call_connection_lost(self, exc):
159 try:
160 self._protocol.connection_lost(exc)
161 finally:
162 self._proc = None
163 self._protocol = None
164 self._loop = None
165
166
167class WriteSubprocessPipeProto(protocols.BaseProtocol):
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700168
169 def __init__(self, proc, fd):
170 self.proc = proc
171 self.fd = fd
Victor Stinneraaabc4f2014-01-29 14:22:56 -0800172 self.pipe = None
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700173 self.disconnected = False
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700174
175 def connection_made(self, transport):
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700176 self.pipe = transport
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700177
Victor Stinneracdb7822014-07-14 18:33:40 +0200178 def __repr__(self):
179 return ('<%s fd=%s pipe=%r>'
180 % (self.__class__.__name__, self.fd, self.pipe))
181
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700182 def connection_lost(self, exc):
183 self.disconnected = True
184 self.proc._pipe_connection_lost(self.fd, exc)
185
Guido van Rossum1e9a4462014-01-29 14:28:15 -0800186 def pause_writing(self):
187 self.proc._protocol.pause_writing()
188
189 def resume_writing(self):
190 self.proc._protocol.resume_writing()
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700191
192
193class ReadSubprocessPipeProto(WriteSubprocessPipeProto,
194 protocols.Protocol):
195
196 def data_received(self, data):
197 self.proc._pipe_data_received(self.fd, data)