Mercurial > hg
view rust/rhg/src/ui.rs @ 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 | 513b3ef277a3 |
children | eb55274d3650 |
line wrap: on
line source
use std::io; use std::io::Write; pub struct Ui {} /// The kind of user interface error pub enum UiError { /// The standard output stream cannot be written to StdoutError(io::Error), /// The standard error stream cannot be written to StderrError(io::Error), } /// The commandline user interface impl Ui { pub fn new() -> Self { Ui {} } /// Write bytes to stdout pub fn write_stdout(&self, bytes: &[u8]) -> Result<(), UiError> { let mut stdout = io::stdout(); self.write_stream(&mut stdout, bytes) .or_else(|e| self.into_stdout_error(e))?; stdout.flush().or_else(|e| self.into_stdout_error(e)) } fn into_stdout_error(&self, error: io::Error) -> Result<(), UiError> { self.write_stderr( &[b"abort: ", error.to_string().as_bytes(), b"\n"].concat(), )?; Err(UiError::StdoutError(error)) } /// Write bytes to stderr pub fn write_stderr(&self, bytes: &[u8]) -> Result<(), UiError> { let mut stderr = io::stderr(); self.write_stream(&mut stderr, bytes) .or_else(|e| Err(UiError::StderrError(e)))?; stderr.flush().or_else(|e| Err(UiError::StderrError(e))) } fn write_stream( &self, stream: &mut impl Write, bytes: &[u8], ) -> Result<(), io::Error> { stream.write_all(bytes) } }