2013-10-17 17:40:50 -03:00
|
|
|
"""Abstract Transport class."""
|
|
|
|
|
2013-12-02 22:36:30 -04:00
|
|
|
import sys
|
|
|
|
|
|
|
|
PY34 = sys.version_info >= (3, 4)
|
|
|
|
|
2013-12-20 18:16:21 -04:00
|
|
|
__all__ = ['BaseTransport', 'ReadTransport', 'WriteTransport',
|
|
|
|
'Transport', 'DatagramTransport', 'SubprocessTransport',
|
|
|
|
]
|
2013-10-17 17:40:50 -03:00
|
|
|
|
|
|
|
|
|
|
|
class BaseTransport:
|
2013-11-30 19:35:42 -04:00
|
|
|
"""Base class for transports."""
|
2013-10-17 17:40:50 -03: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 07:30:00 -04:00
|
|
|
"""Close the transport.
|
2013-10-17 17:40:50 -03: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 19:35:42 -04:00
|
|
|
"""Interface for read-only transports."""
|
2013-10-17 17:40:50 -03:00
|
|
|
|
2013-10-18 11:58:20 -03:00
|
|
|
def pause_reading(self):
|
2013-10-17 17:40:50 -03:00
|
|
|
"""Pause the receiving end.
|
|
|
|
|
|
|
|
No data will be passed to the protocol's data_received()
|
2013-10-18 11:58:20 -03:00
|
|
|
method until resume_reading() is called.
|
2013-10-17 17:40:50 -03:00
|
|
|
"""
|
|
|
|
raise NotImplementedError
|
|
|
|
|
2013-10-18 11:58:20 -03:00
|
|
|
def resume_reading(self):
|
2013-10-17 17:40:50 -03: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 19:35:42 -04:00
|
|
|
"""Interface for write-only transports."""
|
2013-10-17 17:40:50 -03:00
|
|
|
|
2013-10-18 19:17:11 -03: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 17:40:50 -03: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 22:36:30 -04:00
|
|
|
The default implementation concatenates the arguments and
|
|
|
|
calls write() on the result.
|
2013-10-17 17:40:50 -03:00
|
|
|
"""
|
2013-12-02 22:36:30 -04:00
|
|
|
if not PY34:
|
|
|
|
# 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 17:40:50 -03:00
|
|
|
|
|
|
|
def write_eof(self):
|
2013-11-23 07:30:00 -04:00
|
|
|
"""Close the write end after flushing buffered data.
|
2013-10-17 17:40:50 -03: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 07:30:00 -04:00
|
|
|
"""Return True if this transport supports write_eof(), False if not."""
|
2013-10-17 17:40:50 -03:00
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
def abort(self):
|
2013-11-23 15:51:09 -04:00
|
|
|
"""Close the transport immediately.
|
2013-10-17 17:40:50 -03: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 19:35:42 -04:00
|
|
|
"""Interface representing a bidirectional transport.
|
2013-10-17 17:40:50 -03: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 19:35:42 -04:00
|
|
|
"""Interface for datagram (UDP) transports."""
|
2013-10-17 17:40:50 -03: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 07:30:00 -04:00
|
|
|
"""Close the transport immediately.
|
2013-10-17 17:40:50 -03: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
|