mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	 71080fc351
			
		
	
	
		71080fc351
		
	
	
	
	
		
			
			Move compatibility helpers for the different Python versions to a new asyncio.compat module.
		
			
				
	
	
		
			296 lines
		
	
	
	
		
			9.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			296 lines
		
	
	
	
		
			9.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """Abstract Transport class."""
 | |
| 
 | |
| import sys
 | |
| 
 | |
| from asyncio import compat
 | |
| 
 | |
| __all__ = ['BaseTransport', 'ReadTransport', 'WriteTransport',
 | |
|            'Transport', 'DatagramTransport', 'SubprocessTransport',
 | |
|            ]
 | |
| 
 | |
| 
 | |
| class BaseTransport:
 | |
|     """Base class for transports."""
 | |
| 
 | |
|     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):
 | |
|         """Close the transport.
 | |
| 
 | |
|         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):
 | |
|     """Interface for read-only transports."""
 | |
| 
 | |
|     def pause_reading(self):
 | |
|         """Pause the receiving end.
 | |
| 
 | |
|         No data will be passed to the protocol's data_received()
 | |
|         method until resume_reading() is called.
 | |
|         """
 | |
|         raise NotImplementedError
 | |
| 
 | |
|     def resume_reading(self):
 | |
|         """Resume the receiving end.
 | |
| 
 | |
|         Data received will once again be passed to the protocol's
 | |
|         data_received() method.
 | |
|         """
 | |
|         raise NotImplementedError
 | |
| 
 | |
| 
 | |
| class WriteTransport(BaseTransport):
 | |
|     """Interface for write-only transports."""
 | |
| 
 | |
|     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
 | |
| 
 | |
|     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.
 | |
| 
 | |
|         The default implementation concatenates the arguments and
 | |
|         calls write() on the result.
 | |
|         """
 | |
|         data = compat.flatten_list_bytes(list_of_data)
 | |
|         self.write(data)
 | |
| 
 | |
|     def write_eof(self):
 | |
|         """Close the write end after flushing buffered data.
 | |
| 
 | |
|         (This is like typing ^D into a UNIX program reading from stdin.)
 | |
| 
 | |
|         Data may still be received.
 | |
|         """
 | |
|         raise NotImplementedError
 | |
| 
 | |
|     def can_write_eof(self):
 | |
|         """Return True if this transport supports write_eof(), False if not."""
 | |
|         raise NotImplementedError
 | |
| 
 | |
|     def abort(self):
 | |
|         """Close the transport immediately.
 | |
| 
 | |
|         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):
 | |
|     """Interface representing a bidirectional transport.
 | |
| 
 | |
|     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):
 | |
|     """Interface for datagram (UDP) transports."""
 | |
| 
 | |
|     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):
 | |
|         """Close the transport immediately.
 | |
| 
 | |
|         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
 | |
| 
 | |
| 
 | |
| 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.
 | |
|     """
 | |
| 
 | |
|     def __init__(self, extra=None, loop=None):
 | |
|         super().__init__(extra)
 | |
|         assert loop is not None
 | |
|         self._loop = loop
 | |
|         self._protocol_paused = False
 | |
|         self._set_write_buffer_limits()
 | |
| 
 | |
|     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()
 | |
|             except Exception as exc:
 | |
|                 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
 | |
|             self.get_write_buffer_size() <= self._low_water):
 | |
|             self._protocol_paused = False
 | |
|             try:
 | |
|                 self._protocol.resume_writing()
 | |
|             except Exception as exc:
 | |
|                 self._loop.call_exception_handler({
 | |
|                     'message': 'protocol.resume_writing() failed',
 | |
|                     'exception': exc,
 | |
|                     'transport': self,
 | |
|                     'protocol': self._protocol,
 | |
|                 })
 | |
| 
 | |
|     def get_write_buffer_limits(self):
 | |
|         return (self._low_water, self._high_water)
 | |
| 
 | |
|     def _set_write_buffer_limits(self, high=None, low=None):
 | |
|         if high is None:
 | |
|             if low is None:
 | |
|                 high = 64*1024
 | |
|             else:
 | |
|                 high = 4*low
 | |
|         if low is None:
 | |
|             low = high // 4
 | |
|         if not high >= low >= 0:
 | |
|             raise ValueError('high (%r) must be >= low (%r) must be >= 0' %
 | |
|                              (high, low))
 | |
|         self._high_water = high
 | |
|         self._low_water = low
 | |
| 
 | |
|     def set_write_buffer_limits(self, high=None, low=None):
 | |
|         self._set_write_buffer_limits(high=high, low=low)
 | |
|         self._maybe_pause_protocol()
 | |
| 
 | |
|     def get_write_buffer_size(self):
 | |
|         raise NotImplementedError
 |