blob: 233bbb53cb6a3f92833232fdbcd4c0a1002722b6 [file] [log] [blame]
Guido van Rossum27b7c7e2013-10-17 13:40:50 -07001"""Abstract Transport class."""
2
Yury Selivanov6370f342017-12-10 18:36:12 -05003__all__ = (
4 'BaseTransport', 'ReadTransport', 'WriteTransport',
5 'Transport', 'DatagramTransport', 'SubprocessTransport',
6)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -07007
8
9class BaseTransport:
Guido van Rossum9204af42013-11-30 15:35:42 -080010 """Base class for transports."""
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070011
12 def __init__(self, extra=None):
13 if extra is None:
14 extra = {}
15 self._extra = extra
16
17 def get_extra_info(self, name, default=None):
18 """Get optional transport information."""
19 return self._extra.get(name, default)
20
Yury Selivanov5bb1afb2015-11-16 12:43:21 -050021 def is_closing(self):
22 """Return True if the transport is closing or closed."""
23 raise NotImplementedError
24
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070025 def close(self):
Antoine Pitroudec43382013-11-23 12:30:00 +010026 """Close the transport.
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070027
28 Buffered data will be flushed asynchronously. No more data
29 will be received. After all buffered data is flushed, the
30 protocol's connection_lost() method will (eventually) called
31 with None as its argument.
32 """
33 raise NotImplementedError
34
Yury Selivanova05a6ef2016-09-11 21:11:02 -040035 def set_protocol(self, protocol):
36 """Set a new protocol."""
37 raise NotImplementedError
38
39 def get_protocol(self):
40 """Return the current protocol."""
41 raise NotImplementedError
42
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070043
44class ReadTransport(BaseTransport):
Guido van Rossum9204af42013-11-30 15:35:42 -080045 """Interface for read-only transports."""
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070046
Yury Selivanovd757aaf2017-12-18 17:03:23 -050047 def is_reading(self):
48 """Return True if the transport is receiving."""
49 raise NotImplementedError
50
Guido van Rossum57497ad2013-10-18 07:58:20 -070051 def pause_reading(self):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070052 """Pause the receiving end.
53
54 No data will be passed to the protocol's data_received()
Guido van Rossum57497ad2013-10-18 07:58:20 -070055 method until resume_reading() is called.
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070056 """
57 raise NotImplementedError
58
Guido van Rossum57497ad2013-10-18 07:58:20 -070059 def resume_reading(self):
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070060 """Resume the receiving end.
61
62 Data received will once again be passed to the protocol's
63 data_received() method.
64 """
65 raise NotImplementedError
66
67
68class WriteTransport(BaseTransport):
Guido van Rossum9204af42013-11-30 15:35:42 -080069 """Interface for write-only transports."""
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070070
Guido van Rossum355491d2013-10-18 15:17:11 -070071 def set_write_buffer_limits(self, high=None, low=None):
72 """Set the high- and low-water limits for write flow control.
73
74 These two values control when to call the protocol's
75 pause_writing() and resume_writing() methods. If specified,
76 the low-water limit must be less than or equal to the
77 high-water limit. Neither value can be negative.
78
79 The defaults are implementation-specific. If only the
Serhiy Storchakad65c9492015-11-02 14:10:23 +020080 high-water limit is given, the low-water limit defaults to an
Guido van Rossum355491d2013-10-18 15:17:11 -070081 implementation-specific value less than or equal to the
82 high-water limit. Setting high to zero forces low to zero as
83 well, and causes pause_writing() to be called whenever the
84 buffer becomes non-empty. Setting low to zero causes
85 resume_writing() to be called only once the buffer is empty.
86 Use of zero for either limit is generally sub-optimal as it
87 reduces opportunities for doing I/O and computation
88 concurrently.
89 """
90 raise NotImplementedError
91
92 def get_write_buffer_size(self):
93 """Return the current size of the write buffer."""
94 raise NotImplementedError
95
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070096 def write(self, data):
97 """Write some data bytes to the transport.
98
99 This does not block; it buffers the data and arranges for it
100 to be sent out asynchronously.
101 """
102 raise NotImplementedError
103
104 def writelines(self, list_of_data):
105 """Write a list (or any iterable) of data bytes to the transport.
106
Guido van Rossumf10345e2013-12-02 18:36:30 -0800107 The default implementation concatenates the arguments and
108 calls write() on the result.
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700109 """
INADA Naoki3e2ad8e2017-04-25 10:57:18 +0900110 data = b''.join(list_of_data)
Victor Stinner71080fc2015-07-25 02:23:21 +0200111 self.write(data)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700112
113 def write_eof(self):
Antoine Pitroudec43382013-11-23 12:30:00 +0100114 """Close the write end after flushing buffered data.
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700115
116 (This is like typing ^D into a UNIX program reading from stdin.)
117
118 Data may still be received.
119 """
120 raise NotImplementedError
121
122 def can_write_eof(self):
Antoine Pitroudec43382013-11-23 12:30:00 +0100123 """Return True if this transport supports write_eof(), False if not."""
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700124 raise NotImplementedError
125
126 def abort(self):
Guido van Rossum488b0da2013-11-23 11:51:09 -0800127 """Close the transport immediately.
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700128
129 Buffered data will be lost. No more data will be received.
130 The protocol's connection_lost() method will (eventually) be
131 called with None as its argument.
132 """
133 raise NotImplementedError
134
135
136class Transport(ReadTransport, WriteTransport):
Guido van Rossum9204af42013-11-30 15:35:42 -0800137 """Interface representing a bidirectional transport.
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700138
139 There may be several implementations, but typically, the user does
140 not implement new transports; rather, the platform provides some
141 useful transports that are implemented using the platform's best
142 practices.
143
144 The user never instantiates a transport directly; they call a
145 utility function, passing it a protocol factory and other
146 information necessary to create the transport and protocol. (E.g.
147 EventLoop.create_connection() or EventLoop.create_server().)
148
149 The utility function will asynchronously create a transport and a
150 protocol and hook them up by calling the protocol's
151 connection_made() method, passing it the transport.
152
153 The implementation here raises NotImplemented for every method
154 except writelines(), which calls write() in a loop.
155 """
156
157
158class DatagramTransport(BaseTransport):
Guido van Rossum9204af42013-11-30 15:35:42 -0800159 """Interface for datagram (UDP) transports."""
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700160
161 def sendto(self, data, addr=None):
162 """Send data to the transport.
163
164 This does not block; it buffers the data and arranges for it
165 to be sent out asynchronously.
166 addr is target socket address.
167 If addr is None use target address pointed on transport creation.
168 """
169 raise NotImplementedError
170
171 def abort(self):
Antoine Pitroudec43382013-11-23 12:30:00 +0100172 """Close the transport immediately.
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700173
174 Buffered data will be lost. No more data will be received.
175 The protocol's connection_lost() method will (eventually) be
176 called with None as its argument.
177 """
178 raise NotImplementedError
179
180
181class SubprocessTransport(BaseTransport):
182
183 def get_pid(self):
184 """Get subprocess id."""
185 raise NotImplementedError
186
187 def get_returncode(self):
188 """Get subprocess returncode.
189
190 See also
191 http://docs.python.org/3/library/subprocess#subprocess.Popen.returncode
192 """
193 raise NotImplementedError
194
195 def get_pipe_transport(self, fd):
196 """Get transport for pipe with number fd."""
197 raise NotImplementedError
198
199 def send_signal(self, signal):
200 """Send signal to subprocess.
201
202 See also:
203 docs.python.org/3/library/subprocess#subprocess.Popen.send_signal
204 """
205 raise NotImplementedError
206
207 def terminate(self):
208 """Stop the subprocess.
209
210 Alias for close() method.
211
212 On Posix OSs the method sends SIGTERM to the subprocess.
213 On Windows the Win32 API function TerminateProcess()
214 is called to stop the subprocess.
215
216 See also:
217 http://docs.python.org/3/library/subprocess#subprocess.Popen.terminate
218 """
219 raise NotImplementedError
220
221 def kill(self):
222 """Kill the subprocess.
223
224 On Posix OSs the function sends SIGKILL to the subprocess.
225 On Windows kill() is an alias for terminate().
226
227 See also:
228 http://docs.python.org/3/library/subprocess#subprocess.Popen.kill
229 """
230 raise NotImplementedError
Yury Selivanov3cb99142014-02-18 18:41:13 -0500231
232
233class _FlowControlMixin(Transport):
234 """All the logic for (write) flow control in a mix-in base class.
235
236 The subclass must implement get_write_buffer_size(). It must call
237 _maybe_pause_protocol() whenever the write buffer size increases,
238 and _maybe_resume_protocol() whenever it decreases. It may also
239 override set_write_buffer_limits() (e.g. to specify different
240 defaults).
241
242 The subclass constructor must call super().__init__(extra). This
243 will call set_write_buffer_limits().
244
245 The user may call set_write_buffer_limits() and
246 get_write_buffer_size(), and their protocol's pause_writing() and
247 resume_writing() may be called.
248 """
249
Victor Stinner004adb92014-11-05 15:27:41 +0100250 def __init__(self, extra=None, loop=None):
Yury Selivanov3cb99142014-02-18 18:41:13 -0500251 super().__init__(extra)
Victor Stinner004adb92014-11-05 15:27:41 +0100252 assert loop is not None
253 self._loop = loop
Yury Selivanov3cb99142014-02-18 18:41:13 -0500254 self._protocol_paused = False
Yury Selivanov15899202014-02-19 11:10:52 -0500255 self._set_write_buffer_limits()
Yury Selivanov3cb99142014-02-18 18:41:13 -0500256
257 def _maybe_pause_protocol(self):
258 size = self.get_write_buffer_size()
259 if size <= self._high_water:
260 return
261 if not self._protocol_paused:
262 self._protocol_paused = True
263 try:
264 self._protocol.pause_writing()
265 except Exception as exc:
266 self._loop.call_exception_handler({
267 'message': 'protocol.pause_writing() failed',
268 'exception': exc,
269 'transport': self,
270 'protocol': self._protocol,
271 })
272
273 def _maybe_resume_protocol(self):
274 if (self._protocol_paused and
Yury Selivanov6370f342017-12-10 18:36:12 -0500275 self.get_write_buffer_size() <= self._low_water):
Yury Selivanov3cb99142014-02-18 18:41:13 -0500276 self._protocol_paused = False
277 try:
278 self._protocol.resume_writing()
279 except Exception as exc:
280 self._loop.call_exception_handler({
281 'message': 'protocol.resume_writing() failed',
282 'exception': exc,
283 'transport': self,
284 'protocol': self._protocol,
285 })
286
Victor Stinner52bb9492014-08-26 00:22:28 +0200287 def get_write_buffer_limits(self):
288 return (self._low_water, self._high_water)
289
Yury Selivanov15899202014-02-19 11:10:52 -0500290 def _set_write_buffer_limits(self, high=None, low=None):
Yury Selivanov3cb99142014-02-18 18:41:13 -0500291 if high is None:
292 if low is None:
Yury Selivanov6370f342017-12-10 18:36:12 -0500293 high = 64 * 1024
Yury Selivanov3cb99142014-02-18 18:41:13 -0500294 else:
Yury Selivanov6370f342017-12-10 18:36:12 -0500295 high = 4 * low
Yury Selivanov3cb99142014-02-18 18:41:13 -0500296 if low is None:
297 low = high // 4
Yury Selivanov6370f342017-12-10 18:36:12 -0500298
Yury Selivanov3cb99142014-02-18 18:41:13 -0500299 if not high >= low >= 0:
Yury Selivanov6370f342017-12-10 18:36:12 -0500300 raise ValueError(
301 f'high ({high!r}) must be >= low ({low!r}) must be >= 0')
302
Yury Selivanov3cb99142014-02-18 18:41:13 -0500303 self._high_water = high
304 self._low_water = low
305
Yury Selivanov15899202014-02-19 11:10:52 -0500306 def set_write_buffer_limits(self, high=None, low=None):
307 self._set_write_buffer_limits(high=high, low=low)
308 self._maybe_pause_protocol()
309
Yury Selivanov3cb99142014-02-18 18:41:13 -0500310 def get_write_buffer_size(self):
311 raise NotImplementedError