| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | """Abstract Transport class.""" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-02 18:36:30 -08:00
										 |  |  | import sys | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-02 18:41:34 +01:00
										 |  |  | _PY34 = sys.version_info >= (3, 4) | 
					
						
							| 
									
										
										
										
											2013-12-02 18:36:30 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-20 14:16:21 -08: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
										 |  |  | 
 | 
					
						
							|  |  |  |     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) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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 | 
					
						
							|  |  |  |         protocol's connection_lost() method will (eventually) called | 
					
						
							|  |  |  |         with None as its argument. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ReadTransport(BaseTransport): | 
					
						
							| 
									
										
										
										
											2013-11-30 15:35:42 -08:00
										 |  |  |     """Interface for read-only transports.""" | 
					
						
							| 
									
										
										
										
											2013-10-17 13:40:50 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  |         high-water limit is given, the low-water limit defaults to a | 
					
						
							|  |  |  |         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
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2014-01-02 18:41:34 +01:00
										 |  |  |         if not _PY34: | 
					
						
							| 
									
										
										
										
											2013-12-02 18:36:30 -08:00
										 |  |  |             # In Python 3.3, bytes.join() doesn't handle memoryview. | 
					
						
							|  |  |  |             list_of_data = ( | 
					
						
							|  |  |  |                 bytes(data) if isinstance(data, memoryview) else data | 
					
						
							|  |  |  |                 for data in list_of_data) | 
					
						
							|  |  |  |         self.write(b''.join(list_of_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. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class DatagramTransport(BaseTransport): | 
					
						
							| 
									
										
										
										
											2013-11-30 15:35:42 -08:00
										 |  |  |     """Interface for datagram (UDP) transports.""" | 
					
						
							| 
									
										
										
										
											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): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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 |