blob: f5e7dfec1367c9b5c9e7cf5c7258962b4c4c46de [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():
Victor Stinner29ad0112015-01-15 00:04:21 +010074 if proto is None:
75 continue
Guido van Rossum0016e1d2013-10-30 14:56:49 -070076 proto.pipe.close()
77 if self._returncode is None:
78 self.terminate()
79
80 def get_pid(self):
Victor Stinneracdb7822014-07-14 18:33:40 +020081 return self._pid
Guido van Rossum0016e1d2013-10-30 14:56:49 -070082
83 def get_returncode(self):
84 return self._returncode
85
86 def get_pipe_transport(self, fd):
87 if fd in self._pipes:
88 return self._pipes[fd].pipe
89 else:
90 return None
91
92 def send_signal(self, signal):
93 self._proc.send_signal(signal)
94
95 def terminate(self):
96 self._proc.terminate()
97
98 def kill(self):
99 self._proc.kill()
100
Victor Stinnerf651a602015-01-14 02:10:33 +0100101 def _kill_wait(self):
102 """Close pipes, kill the subprocess and read its return status.
103
104 Function called when an exception is raised during the creation
105 of a subprocess.
106 """
107 if self._loop.get_debug():
108 logger.warning('Exception during subprocess creation, '
109 'kill the subprocess %r',
110 self,
111 exc_info=True)
112
113 proc = self._proc
114 if proc.stdout:
115 proc.stdout.close()
116 if proc.stderr:
117 proc.stderr.close()
118 if proc.stdin:
119 proc.stdin.close()
120 try:
121 proc.kill()
122 except ProcessLookupError:
123 pass
Victor Stinner29ad0112015-01-15 00:04:21 +0100124 self._returncode = proc.wait()
Victor Stinnerf651a602015-01-14 02:10:33 +0100125
Victor Stinnerf951d282014-06-29 00:46:45 +0200126 @coroutine
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700127 def _post_init(self):
Victor Stinnerf651a602015-01-14 02:10:33 +0100128 try:
129 proc = self._proc
130 loop = self._loop
131 if proc.stdin is not None:
132 _, pipe = yield from loop.connect_write_pipe(
133 lambda: WriteSubprocessPipeProto(self, 0),
134 proc.stdin)
135 self._pipes[0] = pipe
136 if proc.stdout is not None:
137 _, pipe = yield from loop.connect_read_pipe(
138 lambda: ReadSubprocessPipeProto(self, 1),
139 proc.stdout)
140 self._pipes[1] = pipe
141 if proc.stderr is not None:
142 _, pipe = yield from loop.connect_read_pipe(
143 lambda: ReadSubprocessPipeProto(self, 2),
144 proc.stderr)
145 self._pipes[2] = pipe
Victor Stinneraaabc4f2014-01-29 14:22:56 -0800146
Victor Stinnerf651a602015-01-14 02:10:33 +0100147 assert self._pending_calls is not None
Victor Stinneraaabc4f2014-01-29 14:22:56 -0800148
Victor Stinnerf651a602015-01-14 02:10:33 +0100149 self._loop.call_soon(self._protocol.connection_made, self)
150 for callback, data in self._pending_calls:
151 self._loop.call_soon(callback, *data)
152 self._pending_calls = None
153 except:
154 self._kill_wait()
155 raise
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700156
157 def _call(self, cb, *data):
158 if self._pending_calls is not None:
159 self._pending_calls.append((cb, data))
160 else:
161 self._loop.call_soon(cb, *data)
162
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700163 def _pipe_connection_lost(self, fd, exc):
164 self._call(self._protocol.pipe_connection_lost, fd, exc)
165 self._try_finish()
166
167 def _pipe_data_received(self, fd, data):
168 self._call(self._protocol.pipe_data_received, fd, data)
169
170 def _process_exited(self, returncode):
171 assert returncode is not None, returncode
172 assert self._returncode is None, self._returncode
Victor Stinneracdb7822014-07-14 18:33:40 +0200173 if self._loop.get_debug():
174 logger.info('%r exited with return code %r',
175 self, returncode)
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700176 self._returncode = returncode
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700177 self._call(self._protocol.process_exited)
178 self._try_finish()
179
180 def _try_finish(self):
181 assert not self._finished
182 if self._returncode is None:
183 return
184 if all(p is not None and p.disconnected
185 for p in self._pipes.values()):
186 self._finished = True
Victor Stinner1b9763d2014-12-18 23:47:27 +0100187 self._call(self._call_connection_lost, None)
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700188
189 def _call_connection_lost(self, exc):
190 try:
191 self._protocol.connection_lost(exc)
192 finally:
193 self._proc = None
194 self._protocol = None
195 self._loop = None
196
197
198class WriteSubprocessPipeProto(protocols.BaseProtocol):
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700199
200 def __init__(self, proc, fd):
201 self.proc = proc
202 self.fd = fd
Victor Stinneraaabc4f2014-01-29 14:22:56 -0800203 self.pipe = None
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700204 self.disconnected = False
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700205
206 def connection_made(self, transport):
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700207 self.pipe = transport
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700208
Victor Stinneracdb7822014-07-14 18:33:40 +0200209 def __repr__(self):
210 return ('<%s fd=%s pipe=%r>'
211 % (self.__class__.__name__, self.fd, self.pipe))
212
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700213 def connection_lost(self, exc):
214 self.disconnected = True
215 self.proc._pipe_connection_lost(self.fd, exc)
Victor Stinner587feb12015-01-09 21:34:27 +0100216 self.proc = None
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700217
Guido van Rossum1e9a4462014-01-29 14:28:15 -0800218 def pause_writing(self):
219 self.proc._protocol.pause_writing()
220
221 def resume_writing(self):
222 self.proc._protocol.resume_writing()
Guido van Rossum0016e1d2013-10-30 14:56:49 -0700223
224
225class ReadSubprocessPipeProto(WriteSubprocessPipeProto,
226 protocols.Protocol):
227
228 def data_received(self, data):
229 self.proc._pipe_data_received(self.fd, data)