Mercurial > hg
comparison mercurial/wireprotoframing.py @ 37062:fe4c944f95bb
wireproto: use named arguments when passing around frame data
Named arguments is easier to reason about compared to positional
arguments. Especially when you have many positional arguments.
Differential Revision: https://phab.mercurial-scm.org/D2900
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Thu, 15 Mar 2018 16:09:58 -0700 |
parents | 884a0c1604ad |
children | 39304dd63589 |
comparison
equal
deleted
inserted
replaced
37061:884a0c1604ad | 37062:fe4c944f95bb |
---|---|
111 requestid = attr.ib() | 111 requestid = attr.ib() |
112 typeid = attr.ib() | 112 typeid = attr.ib() |
113 flags = attr.ib() | 113 flags = attr.ib() |
114 payload = attr.ib() | 114 payload = attr.ib() |
115 | 115 |
116 def makeframe(requestid, frametype, frameflags, payload): | 116 def makeframe(requestid, typeid, flags, payload): |
117 """Assemble a frame into a byte array.""" | 117 """Assemble a frame into a byte array.""" |
118 # TODO assert size of payload. | 118 # TODO assert size of payload. |
119 frame = bytearray(FRAME_HEADER_SIZE + len(payload)) | 119 frame = bytearray(FRAME_HEADER_SIZE + len(payload)) |
120 | 120 |
121 # 24 bits length | 121 # 24 bits length |
124 # 4 bits flags | 124 # 4 bits flags |
125 | 125 |
126 l = struct.pack(r'<I', len(payload)) | 126 l = struct.pack(r'<I', len(payload)) |
127 frame[0:3] = l[0:3] | 127 frame[0:3] = l[0:3] |
128 struct.pack_into(r'<H', frame, 3, requestid) | 128 struct.pack_into(r'<H', frame, 3, requestid) |
129 frame[5] = (frametype << 4) | frameflags | 129 frame[5] = (typeid << 4) | flags |
130 frame[6:] = payload | 130 frame[6:] = payload |
131 | 131 |
132 return frame | 132 return frame |
133 | 133 |
134 def makeframefromhumanstring(s): | 134 def makeframefromhumanstring(s): |
164 else: | 164 else: |
165 finalflags |= int(flag) | 165 finalflags |= int(flag) |
166 | 166 |
167 payload = util.unescapestr(payload) | 167 payload = util.unescapestr(payload) |
168 | 168 |
169 return makeframe(requestid, frametype, finalflags, payload) | 169 return makeframe(requestid=requestid, typeid=frametype, |
170 flags=finalflags, payload=payload) | |
170 | 171 |
171 def parseheader(data): | 172 def parseheader(data): |
172 """Parse a unified framing protocol frame header from a buffer. | 173 """Parse a unified framing protocol frame header from a buffer. |
173 | 174 |
174 The header is expected to be in the buffer at offset 0 and the | 175 The header is expected to be in the buffer at offset 0 and the |
227 flags |= FLAG_COMMAND_NAME_HAVE_DATA | 228 flags |= FLAG_COMMAND_NAME_HAVE_DATA |
228 | 229 |
229 if not flags: | 230 if not flags: |
230 flags |= FLAG_COMMAND_NAME_EOS | 231 flags |= FLAG_COMMAND_NAME_EOS |
231 | 232 |
232 yield makeframe(requestid, FRAME_TYPE_COMMAND_NAME, flags, cmd) | 233 yield makeframe(requestid=requestid, typeid=FRAME_TYPE_COMMAND_NAME, |
234 flags=flags, payload=cmd) | |
233 | 235 |
234 for i, k in enumerate(sorted(args)): | 236 for i, k in enumerate(sorted(args)): |
235 v = args[k] | 237 v = args[k] |
236 last = i == len(args) - 1 | 238 last = i == len(args) - 1 |
237 | 239 |
243 payload[offset:offset + len(k)] = k | 245 payload[offset:offset + len(k)] = k |
244 offset += len(k) | 246 offset += len(k) |
245 payload[offset:offset + len(v)] = v | 247 payload[offset:offset + len(v)] = v |
246 | 248 |
247 flags = FLAG_COMMAND_ARGUMENT_EOA if last else 0 | 249 flags = FLAG_COMMAND_ARGUMENT_EOA if last else 0 |
248 yield makeframe(requestid, FRAME_TYPE_COMMAND_ARGUMENT, flags, payload) | 250 yield makeframe(requestid=requestid, |
251 typeid=FRAME_TYPE_COMMAND_ARGUMENT, | |
252 flags=flags, | |
253 payload=payload) | |
249 | 254 |
250 if datafh: | 255 if datafh: |
251 while True: | 256 while True: |
252 data = datafh.read(DEFAULT_MAX_FRAME_SIZE) | 257 data = datafh.read(DEFAULT_MAX_FRAME_SIZE) |
253 | 258 |
257 else: | 262 else: |
258 flags = FLAG_COMMAND_DATA_EOS | 263 flags = FLAG_COMMAND_DATA_EOS |
259 assert datafh.read(1) == b'' | 264 assert datafh.read(1) == b'' |
260 done = True | 265 done = True |
261 | 266 |
262 yield makeframe(requestid, FRAME_TYPE_COMMAND_DATA, flags, data) | 267 yield makeframe(requestid=requestid, |
268 typeid=FRAME_TYPE_COMMAND_DATA, | |
269 flags=flags, | |
270 payload=data) | |
263 | 271 |
264 if done: | 272 if done: |
265 break | 273 break |
266 | 274 |
267 def createbytesresponseframesfrombytes(requestid, data, | 275 def createbytesresponseframesfrombytes(requestid, data, |
271 Returns a generator of bytearrays. | 279 Returns a generator of bytearrays. |
272 """ | 280 """ |
273 | 281 |
274 # Simple case of a single frame. | 282 # Simple case of a single frame. |
275 if len(data) <= maxframesize: | 283 if len(data) <= maxframesize: |
276 yield makeframe(requestid, FRAME_TYPE_BYTES_RESPONSE, | 284 yield makeframe(requestid=requestid, |
277 FLAG_BYTES_RESPONSE_EOS, data) | 285 typeid=FRAME_TYPE_BYTES_RESPONSE, |
286 flags=FLAG_BYTES_RESPONSE_EOS, | |
287 payload=data) | |
278 return | 288 return |
279 | 289 |
280 offset = 0 | 290 offset = 0 |
281 while True: | 291 while True: |
282 chunk = data[offset:offset + maxframesize] | 292 chunk = data[offset:offset + maxframesize] |
286 if done: | 296 if done: |
287 flags = FLAG_BYTES_RESPONSE_EOS | 297 flags = FLAG_BYTES_RESPONSE_EOS |
288 else: | 298 else: |
289 flags = FLAG_BYTES_RESPONSE_CONTINUATION | 299 flags = FLAG_BYTES_RESPONSE_CONTINUATION |
290 | 300 |
291 yield makeframe(requestid, FRAME_TYPE_BYTES_RESPONSE, flags, chunk) | 301 yield makeframe(requestid=requestid, |
302 typeid=FRAME_TYPE_BYTES_RESPONSE, | |
303 flags=flags, | |
304 payload=chunk) | |
292 | 305 |
293 if done: | 306 if done: |
294 break | 307 break |
295 | 308 |
296 def createerrorframe(requestid, msg, protocol=False, application=False): | 309 def createerrorframe(requestid, msg, protocol=False, application=False): |
301 if protocol: | 314 if protocol: |
302 flags |= FLAG_ERROR_RESPONSE_PROTOCOL | 315 flags |= FLAG_ERROR_RESPONSE_PROTOCOL |
303 if application: | 316 if application: |
304 flags |= FLAG_ERROR_RESPONSE_APPLICATION | 317 flags |= FLAG_ERROR_RESPONSE_APPLICATION |
305 | 318 |
306 yield makeframe(requestid, FRAME_TYPE_ERROR_RESPONSE, flags, msg) | 319 yield makeframe(requestid=requestid, |
320 typeid=FRAME_TYPE_ERROR_RESPONSE, | |
321 flags=flags, | |
322 payload=msg) | |
307 | 323 |
308 def createtextoutputframe(requestid, atoms): | 324 def createtextoutputframe(requestid, atoms): |
309 """Create a text output frame to render text to people. | 325 """Create a text output frame to render text to people. |
310 | 326 |
311 ``atoms`` is a 3-tuple of (formatting string, args, labels). | 327 ``atoms`` is a 3-tuple of (formatting string, args, labels). |
369 bytesleft -= len(atom) | 385 bytesleft -= len(atom) |
370 | 386 |
371 if bytesleft < 0: | 387 if bytesleft < 0: |
372 raise ValueError('cannot encode data in a single frame') | 388 raise ValueError('cannot encode data in a single frame') |
373 | 389 |
374 yield makeframe(requestid, FRAME_TYPE_TEXT_OUTPUT, 0, b''.join(atomchunks)) | 390 yield makeframe(requestid=requestid, |
391 typeid=FRAME_TYPE_TEXT_OUTPUT, | |
392 flags=0, | |
393 payload=b''.join(atomchunks)) | |
375 | 394 |
376 class serverreactor(object): | 395 class serverreactor(object): |
377 """Holds state of a server handling frame-based protocol requests. | 396 """Holds state of a server handling frame-based protocol requests. |
378 | 397 |
379 This class is the "brain" of the unified frame-based protocol server | 398 This class is the "brain" of the unified frame-based protocol server |