Mercurial > hg
view contrib/debugcmdserver.py @ 45095:8e04607023e5
procutil: ensure that procutil.std{out,err}.write() writes all bytes
Python 3 offers different kind of streams and it’s not guaranteed for all of
them that calling write() writes all bytes.
When Python is started in unbuffered mode, sys.std{out,err}.buffer are
instances of io.FileIO, whose write() can write less bytes for
platform-specific reasons (e.g. Linux has a 0x7ffff000 bytes maximum and could
write less if interrupted by a signal; when writing to Windows consoles, it’s
limited to 32767 bytes to avoid the "not enough space" error). This can lead to
silent loss of data, both when using sys.std{out,err}.buffer (which may in fact
not be a buffered stream) and when using the text streams sys.std{out,err}
(I’ve created a CPython bug report for that:
https://bugs.python.org/issue41221).
Python may fix the problem at some point. For now, we implement our own wrapper
for procutil.std{out,err} that calls the raw stream’s write() method until all
bytes have been written. We don’t use sys.std{out,err} for larger writes, so I
think it’s not worth the effort to patch them.
author | Manuel Jacob <me@manueljacob.de> |
---|---|
date | Fri, 10 Jul 2020 12:27:58 +0200 |
parents | 2372284d9457 |
children | c102b704edb5 |
line wrap: on
line source
#!/usr/bin/env python # # Dumps output generated by Mercurial's command server in a formatted style to a # given file or stderr if '-' is specified. Output is also written in its raw # format to stdout. # # $ ./hg serve --cmds pipe | ./contrib/debugcmdserver.py - # o, 52 -> 'capabilities: getencoding runcommand\nencoding: UTF-8' from __future__ import absolute_import, print_function import struct import sys if len(sys.argv) != 2: print('usage: debugcmdserver.py FILE') sys.exit(1) outputfmt = '>cI' outputfmtsize = struct.calcsize(outputfmt) if sys.argv[1] == '-': log = sys.stderr else: log = open(sys.argv[1], 'a') def read(size): data = sys.stdin.read(size) if not data: raise EOFError sys.stdout.write(data) sys.stdout.flush() return data try: while True: header = read(outputfmtsize) channel, length = struct.unpack(outputfmt, header) log.write('%s, %-4d' % (channel, length)) if channel in 'IL': log.write(' -> waiting for input\n') else: data = read(length) log.write(' -> %r\n' % data) log.flush() except EOFError: pass finally: if log != sys.stderr: log.close()