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