| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | """Abstract Transport class.""" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  | __all__ = ( | 
					
						
							|  |  |  |     'BaseTransport', 'ReadTransport', 'WriteTransport', | 
					
						
							|  |  |  |     'Transport', 'DatagramTransport', 'SubprocessTransport', | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class BaseTransport: | 
					
						
							| 
									
										
										
										
											2013-11-30 15:35:42 -08:00
										 |  |  |     """Base class for transports.""" | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 15:18:46 +03:00
										 |  |  |     __slots__ = ('_extra',) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     def __init__(self, extra=None): | 
					
						
							|  |  |  |         if extra is None: | 
					
						
							|  |  |  |             extra = {} | 
					
						
							|  |  |  |         self._extra = extra | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_extra_info(self, name, default=None): | 
					
						
							|  |  |  |         """Get optional transport information.""" | 
					
						
							|  |  |  |         return self._extra.get(name, default) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-16 12:43:21 -05:00
										 |  |  |     def is_closing(self): | 
					
						
							|  |  |  |         """Return True if the transport is closing or closed.""" | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     def close(self): | 
					
						
							| 
									
										
										
										
											2013-11-23 12:30:00 +01:00
										 |  |  |         """Close the transport.
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         Buffered data will be flushed asynchronously.  No more data | 
					
						
							|  |  |  |         will be received.  After all buffered data is flushed, the | 
					
						
							| 
									
										
										
										
											2020-08-21 05:19:40 -07:00
										 |  |  |         protocol's connection_lost() method will (eventually) be | 
					
						
							|  |  |  |         called with None as its argument. | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-11 21:11:02 -04:00
										 |  |  |     def set_protocol(self, protocol): | 
					
						
							|  |  |  |         """Set a new protocol.""" | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_protocol(self): | 
					
						
							|  |  |  |         """Return the current protocol.""" | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | class ReadTransport(BaseTransport): | 
					
						
							| 
									
										
										
										
											2013-11-30 15:35:42 -08:00
										 |  |  |     """Interface for read-only transports.""" | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 15:18:46 +03:00
										 |  |  |     __slots__ = () | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-18 17:03:23 -05:00
										 |  |  |     def is_reading(self): | 
					
						
							|  |  |  |         """Return True if the transport is receiving.""" | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-18 07:58:20 -07:00
										 |  |  |     def pause_reading(self): | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         """Pause the receiving end.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         No data will be passed to the protocol's data_received() | 
					
						
							| 
									
										
										
										
											2013-10-18 07:58:20 -07:00
										 |  |  |         method until resume_reading() is called. | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-18 07:58:20 -07:00
										 |  |  |     def resume_reading(self): | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         """Resume the receiving end.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Data received will once again be passed to the protocol's | 
					
						
							|  |  |  |         data_received() method. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class WriteTransport(BaseTransport): | 
					
						
							| 
									
										
										
										
											2013-11-30 15:35:42 -08:00
										 |  |  |     """Interface for write-only transports.""" | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 15:18:46 +03:00
										 |  |  |     __slots__ = () | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-18 15:17:11 -07:00
										 |  |  |     def set_write_buffer_limits(self, high=None, low=None): | 
					
						
							|  |  |  |         """Set the high- and low-water limits for write flow control.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         These two values control when to call the protocol's | 
					
						
							|  |  |  |         pause_writing() and resume_writing() methods.  If specified, | 
					
						
							|  |  |  |         the low-water limit must be less than or equal to the | 
					
						
							|  |  |  |         high-water limit.  Neither value can be negative. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         The defaults are implementation-specific.  If only the | 
					
						
							| 
									
										
										
										
											2015-11-02 14:10:23 +02:00
										 |  |  |         high-water limit is given, the low-water limit defaults to an | 
					
						
							| 
									
										
										
										
											2013-10-18 15:17:11 -07:00
										 |  |  |         implementation-specific value less than or equal to the | 
					
						
							|  |  |  |         high-water limit.  Setting high to zero forces low to zero as | 
					
						
							|  |  |  |         well, and causes pause_writing() to be called whenever the | 
					
						
							|  |  |  |         buffer becomes non-empty.  Setting low to zero causes | 
					
						
							|  |  |  |         resume_writing() to be called only once the buffer is empty. | 
					
						
							|  |  |  |         Use of zero for either limit is generally sub-optimal as it | 
					
						
							|  |  |  |         reduces opportunities for doing I/O and computation | 
					
						
							|  |  |  |         concurrently. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_write_buffer_size(self): | 
					
						
							|  |  |  |         """Return the current size of the write buffer.""" | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     def write(self, data): | 
					
						
							|  |  |  |         """Write some data bytes to the transport.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         This does not block; it buffers the data and arranges for it | 
					
						
							|  |  |  |         to be sent out asynchronously. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def writelines(self, list_of_data): | 
					
						
							|  |  |  |         """Write a list (or any iterable) of data bytes to the transport.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-02 18:36:30 -08:00
										 |  |  |         The default implementation concatenates the arguments and | 
					
						
							|  |  |  |         calls write() on the result. | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2017-04-25 10:57:18 +09:00
										 |  |  |         data = b''.join(list_of_data) | 
					
						
							| 
									
										
										
										
											2015-07-25 02:23:21 +02:00
										 |  |  |         self.write(data) | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def write_eof(self): | 
					
						
							| 
									
										
										
										
											2013-11-23 12:30:00 +01:00
										 |  |  |         """Close the write end after flushing buffered data.
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         (This is like typing ^D into a UNIX program reading from stdin.) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Data may still be received. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def can_write_eof(self): | 
					
						
							| 
									
										
										
										
											2013-11-23 12:30:00 +01:00
										 |  |  |         """Return True if this transport supports write_eof(), False if not.""" | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def abort(self): | 
					
						
							| 
									
										
										
										
											2013-11-23 11:51:09 -08:00
										 |  |  |         """Close the transport immediately.
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         Buffered data will be lost.  No more data will be received. | 
					
						
							|  |  |  |         The protocol's connection_lost() method will (eventually) be | 
					
						
							|  |  |  |         called with None as its argument. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Transport(ReadTransport, WriteTransport): | 
					
						
							| 
									
										
										
										
											2013-11-30 15:35:42 -08:00
										 |  |  |     """Interface representing a bidirectional transport.
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     There may be several implementations, but typically, the user does | 
					
						
							|  |  |  |     not implement new transports; rather, the platform provides some | 
					
						
							|  |  |  |     useful transports that are implemented using the platform's best | 
					
						
							|  |  |  |     practices. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     The user never instantiates a transport directly; they call a | 
					
						
							|  |  |  |     utility function, passing it a protocol factory and other | 
					
						
							|  |  |  |     information necessary to create the transport and protocol.  (E.g. | 
					
						
							|  |  |  |     EventLoop.create_connection() or EventLoop.create_server().) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     The utility function will asynchronously create a transport and a | 
					
						
							|  |  |  |     protocol and hook them up by calling the protocol's | 
					
						
							|  |  |  |     connection_made() method, passing it the transport. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     The implementation here raises NotImplemented for every method | 
					
						
							|  |  |  |     except writelines(), which calls write() in a loop. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 15:18:46 +03:00
										 |  |  |     __slots__ = () | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | class DatagramTransport(BaseTransport): | 
					
						
							| 
									
										
										
										
											2013-11-30 15:35:42 -08:00
										 |  |  |     """Interface for datagram (UDP) transports.""" | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 15:18:46 +03:00
										 |  |  |     __slots__ = () | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     def sendto(self, data, addr=None): | 
					
						
							|  |  |  |         """Send data to the transport.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         This does not block; it buffers the data and arranges for it | 
					
						
							|  |  |  |         to be sent out asynchronously. | 
					
						
							|  |  |  |         addr is target socket address. | 
					
						
							|  |  |  |         If addr is None use target address pointed on transport creation. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def abort(self): | 
					
						
							| 
									
										
										
										
											2013-11-23 12:30:00 +01:00
										 |  |  |         """Close the transport immediately.
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         Buffered data will be lost.  No more data will be received. | 
					
						
							|  |  |  |         The protocol's connection_lost() method will (eventually) be | 
					
						
							|  |  |  |         called with None as its argument. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SubprocessTransport(BaseTransport): | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 15:18:46 +03:00
										 |  |  |     __slots__ = () | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  |     def get_pid(self): | 
					
						
							|  |  |  |         """Get subprocess id.""" | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_returncode(self): | 
					
						
							|  |  |  |         """Get subprocess returncode.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         See also | 
					
						
							|  |  |  |         http://docs.python.org/3/library/subprocess#subprocess.Popen.returncode | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_pipe_transport(self, fd): | 
					
						
							|  |  |  |         """Get transport for pipe with number fd.""" | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def send_signal(self, signal): | 
					
						
							|  |  |  |         """Send signal to subprocess.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         See also: | 
					
						
							|  |  |  |         docs.python.org/3/library/subprocess#subprocess.Popen.send_signal | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def terminate(self): | 
					
						
							|  |  |  |         """Stop the subprocess.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Alias for close() method. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         On Posix OSs the method sends SIGTERM to the subprocess. | 
					
						
							|  |  |  |         On Windows the Win32 API function TerminateProcess() | 
					
						
							|  |  |  |          is called to stop the subprocess. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         See also: | 
					
						
							|  |  |  |         http://docs.python.org/3/library/subprocess#subprocess.Popen.terminate | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def kill(self): | 
					
						
							|  |  |  |         """Kill the subprocess.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         On Posix OSs the function sends SIGKILL to the subprocess. | 
					
						
							|  |  |  |         On Windows kill() is an alias for terminate(). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         See also: | 
					
						
							|  |  |  |         http://docs.python.org/3/library/subprocess#subprocess.Popen.kill | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							| 
									
										
										
										
											2014-02-18 18:41:13 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _FlowControlMixin(Transport): | 
					
						
							|  |  |  |     """All the logic for (write) flow control in a mix-in base class.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     The subclass must implement get_write_buffer_size().  It must call | 
					
						
							|  |  |  |     _maybe_pause_protocol() whenever the write buffer size increases, | 
					
						
							|  |  |  |     and _maybe_resume_protocol() whenever it decreases.  It may also | 
					
						
							|  |  |  |     override set_write_buffer_limits() (e.g. to specify different | 
					
						
							|  |  |  |     defaults). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     The subclass constructor must call super().__init__(extra).  This | 
					
						
							|  |  |  |     will call set_write_buffer_limits(). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     The user may call set_write_buffer_limits() and | 
					
						
							|  |  |  |     get_write_buffer_size(), and their protocol's pause_writing() and | 
					
						
							|  |  |  |     resume_writing() may be called. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 15:18:46 +03:00
										 |  |  |     __slots__ = ('_loop', '_protocol_paused', '_high_water', '_low_water') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-11-05 15:27:41 +01:00
										 |  |  |     def __init__(self, extra=None, loop=None): | 
					
						
							| 
									
										
										
										
											2014-02-18 18:41:13 -05:00
										 |  |  |         super().__init__(extra) | 
					
						
							| 
									
										
										
										
											2014-11-05 15:27:41 +01:00
										 |  |  |         assert loop is not None | 
					
						
							|  |  |  |         self._loop = loop | 
					
						
							| 
									
										
										
										
											2014-02-18 18:41:13 -05:00
										 |  |  |         self._protocol_paused = False | 
					
						
							| 
									
										
										
										
											2014-02-19 11:10:52 -05:00
										 |  |  |         self._set_write_buffer_limits() | 
					
						
							| 
									
										
										
										
											2014-02-18 18:41:13 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _maybe_pause_protocol(self): | 
					
						
							|  |  |  |         size = self.get_write_buffer_size() | 
					
						
							|  |  |  |         if size <= self._high_water: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         if not self._protocol_paused: | 
					
						
							|  |  |  |             self._protocol_paused = True | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 self._protocol.pause_writing() | 
					
						
							| 
									
										
										
										
											2019-05-27 14:45:12 +02:00
										 |  |  |             except (SystemExit, KeyboardInterrupt): | 
					
						
							|  |  |  |                 raise | 
					
						
							|  |  |  |             except BaseException as exc: | 
					
						
							| 
									
										
										
										
											2014-02-18 18:41:13 -05:00
										 |  |  |                 self._loop.call_exception_handler({ | 
					
						
							|  |  |  |                     'message': 'protocol.pause_writing() failed', | 
					
						
							|  |  |  |                     'exception': exc, | 
					
						
							|  |  |  |                     'transport': self, | 
					
						
							|  |  |  |                     'protocol': self._protocol, | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _maybe_resume_protocol(self): | 
					
						
							|  |  |  |         if (self._protocol_paused and | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |                 self.get_write_buffer_size() <= self._low_water): | 
					
						
							| 
									
										
										
										
											2014-02-18 18:41:13 -05:00
										 |  |  |             self._protocol_paused = False | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 self._protocol.resume_writing() | 
					
						
							| 
									
										
										
										
											2019-05-27 14:45:12 +02:00
										 |  |  |             except (SystemExit, KeyboardInterrupt): | 
					
						
							|  |  |  |                 raise | 
					
						
							|  |  |  |             except BaseException as exc: | 
					
						
							| 
									
										
										
										
											2014-02-18 18:41:13 -05:00
										 |  |  |                 self._loop.call_exception_handler({ | 
					
						
							|  |  |  |                     'message': 'protocol.resume_writing() failed', | 
					
						
							|  |  |  |                     'exception': exc, | 
					
						
							|  |  |  |                     'transport': self, | 
					
						
							|  |  |  |                     'protocol': self._protocol, | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-08-26 00:22:28 +02:00
										 |  |  |     def get_write_buffer_limits(self): | 
					
						
							|  |  |  |         return (self._low_water, self._high_water) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-19 11:10:52 -05:00
										 |  |  |     def _set_write_buffer_limits(self, high=None, low=None): | 
					
						
							| 
									
										
										
										
											2014-02-18 18:41:13 -05:00
										 |  |  |         if high is None: | 
					
						
							|  |  |  |             if low is None: | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |                 high = 64 * 1024 | 
					
						
							| 
									
										
										
										
											2014-02-18 18:41:13 -05:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |                 high = 4 * low | 
					
						
							| 
									
										
										
										
											2014-02-18 18:41:13 -05:00
										 |  |  |         if low is None: | 
					
						
							|  |  |  |             low = high // 4 | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-18 18:41:13 -05:00
										 |  |  |         if not high >= low >= 0: | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |             raise ValueError( | 
					
						
							|  |  |  |                 f'high ({high!r}) must be >= low ({low!r}) must be >= 0') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-18 18:41:13 -05:00
										 |  |  |         self._high_water = high | 
					
						
							|  |  |  |         self._low_water = low | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-19 11:10:52 -05:00
										 |  |  |     def set_write_buffer_limits(self, high=None, low=None): | 
					
						
							|  |  |  |         self._set_write_buffer_limits(high=high, low=low) | 
					
						
							|  |  |  |         self._maybe_pause_protocol() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-18 18:41:13 -05:00
										 |  |  |     def get_write_buffer_size(self): | 
					
						
							|  |  |  |         raise NotImplementedError |