• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""Abstract Protocol base classes."""
2
3__all__ = (
4    'BaseProtocol', 'Protocol', 'DatagramProtocol',
5    'SubprocessProtocol', 'BufferedProtocol',
6)
7
8
9class BaseProtocol:
10    """Common base class for protocol interfaces.
11
12    Usually user implements protocols that derived from BaseProtocol
13    like Protocol or ProcessProtocol.
14
15    The only case when BaseProtocol should be implemented directly is
16    write-only transport like write pipe
17    """
18
19    def connection_made(self, transport):
20        """Called when a connection is made.
21
22        The argument is the transport representing the pipe connection.
23        To receive data, wait for data_received() calls.
24        When the connection is closed, connection_lost() is called.
25        """
26
27    def connection_lost(self, exc):
28        """Called when the connection is lost or closed.
29
30        The argument is an exception object or None (the latter
31        meaning a regular EOF is received or the connection was
32        aborted or closed).
33        """
34
35    def pause_writing(self):
36        """Called when the transport's buffer goes over the high-water mark.
37
38        Pause and resume calls are paired -- pause_writing() is called
39        once when the buffer goes strictly over the high-water mark
40        (even if subsequent writes increases the buffer size even
41        more), and eventually resume_writing() is called once when the
42        buffer size reaches the low-water mark.
43
44        Note that if the buffer size equals the high-water mark,
45        pause_writing() is not called -- it must go strictly over.
46        Conversely, resume_writing() is called when the buffer size is
47        equal or lower than the low-water mark.  These end conditions
48        are important to ensure that things go as expected when either
49        mark is zero.
50
51        NOTE: This is the only Protocol callback that is not called
52        through EventLoop.call_soon() -- if it were, it would have no
53        effect when it's most needed (when the app keeps writing
54        without yielding until pause_writing() is called).
55        """
56
57    def resume_writing(self):
58        """Called when the transport's buffer drains below the low-water mark.
59
60        See pause_writing() for details.
61        """
62
63
64class Protocol(BaseProtocol):
65    """Interface for stream protocol.
66
67    The user should implement this interface.  They can inherit from
68    this class but don't need to.  The implementations here do
69    nothing (they don't raise exceptions).
70
71    When the user wants to requests a transport, they pass a protocol
72    factory to a utility function (e.g., EventLoop.create_connection()).
73
74    When the connection is made successfully, connection_made() is
75    called with a suitable transport object.  Then data_received()
76    will be called 0 or more times with data (bytes) received from the
77    transport; finally, connection_lost() will be called exactly once
78    with either an exception object or None as an argument.
79
80    State machine of calls:
81
82      start -> CM [-> DR*] [-> ER?] -> CL -> end
83
84    * CM: connection_made()
85    * DR: data_received()
86    * ER: eof_received()
87    * CL: connection_lost()
88    """
89
90    def data_received(self, data):
91        """Called when some data is received.
92
93        The argument is a bytes object.
94        """
95
96    def eof_received(self):
97        """Called when the other end calls write_eof() or equivalent.
98
99        If this returns a false value (including None), the transport
100        will close itself.  If it returns a true value, closing the
101        transport is up to the protocol.
102        """
103
104
105class BufferedProtocol(BaseProtocol):
106    """Interface for stream protocol with manual buffer control.
107
108    Important: this has been added to asyncio in Python 3.7
109    *on a provisional basis*!  Consider it as an experimental API that
110    might be changed or removed in Python 3.8.
111
112    Event methods, such as `create_server` and `create_connection`,
113    accept factories that return protocols that implement this interface.
114
115    The idea of BufferedProtocol is that it allows to manually allocate
116    and control the receive buffer.  Event loops can then use the buffer
117    provided by the protocol to avoid unnecessary data copies.  This
118    can result in noticeable performance improvement for protocols that
119    receive big amounts of data.  Sophisticated protocols can allocate
120    the buffer only once at creation time.
121
122    State machine of calls:
123
124      start -> CM [-> GB [-> BU?]]* [-> ER?] -> CL -> end
125
126    * CM: connection_made()
127    * GB: get_buffer()
128    * BU: buffer_updated()
129    * ER: eof_received()
130    * CL: connection_lost()
131    """
132
133    def get_buffer(self, sizehint):
134        """Called to allocate a new receive buffer.
135
136        *sizehint* is a recommended minimal size for the returned
137        buffer.  When set to -1, the buffer size can be arbitrary.
138
139        Must return an object that implements the
140        :ref:`buffer protocol <bufferobjects>`.
141        It is an error to return a zero-sized buffer.
142        """
143
144    def buffer_updated(self, nbytes):
145        """Called when the buffer was updated with the received data.
146
147        *nbytes* is the total number of bytes that were written to
148        the buffer.
149        """
150
151    def eof_received(self):
152        """Called when the other end calls write_eof() or equivalent.
153
154        If this returns a false value (including None), the transport
155        will close itself.  If it returns a true value, closing the
156        transport is up to the protocol.
157        """
158
159
160class DatagramProtocol(BaseProtocol):
161    """Interface for datagram protocol."""
162
163    def datagram_received(self, data, addr):
164        """Called when some datagram is received."""
165
166    def error_received(self, exc):
167        """Called when a send or receive operation raises an OSError.
168
169        (Other than BlockingIOError or InterruptedError.)
170        """
171
172
173class SubprocessProtocol(BaseProtocol):
174    """Interface for protocol for subprocess calls."""
175
176    def pipe_data_received(self, fd, data):
177        """Called when the subprocess writes data into stdout/stderr pipe.
178
179        fd is int file descriptor.
180        data is bytes object.
181        """
182
183    def pipe_connection_lost(self, fd, exc):
184        """Called when a file descriptor associated with the child process is
185        closed.
186
187        fd is the int file descriptor that was closed.
188        """
189
190    def process_exited(self):
191        """Called when subprocess has exited."""
192
193
194def _feed_data_to_buffered_proto(proto, data):
195    data_len = len(data)
196    while data_len:
197        buf = proto.get_buffer(data_len)
198        buf_len = len(buf)
199        if not buf_len:
200            raise RuntimeError('get_buffer() returned an empty buffer')
201
202        if buf_len >= data_len:
203            buf[:data_len] = data
204            proto.buffer_updated(data_len)
205            return
206        else:
207            buf[:buf_len] = data[:buf_len]
208            proto.buffer_updated(buf_len)
209            data = data[buf_len:]
210            data_len = len(data)
211