changeset 45045:8403cc54bc83

procutil: make mercurial.utils.procutil.stderr unbuffered For most Mercurial code, it doesn’t make a difference, as the ui object flushes stderr explicitly (after the change, we could get rid of the explicit flush). One example where it makes a observable difference is mercurial.util.timed(). Without the patch, the time is not immediately shown on Python 3. With the patch, it’s shown immediately on all Python versions and platforms.
author Manuel Jacob <me@manueljacob.de>
date Sun, 05 Jul 2020 13:09:22 +0200
parents 359884685eab
children dd3050227a84
files mercurial/utils/procutil.py tests/test-stdio.py
diffstat 2 files changed, 24 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/utils/procutil.py	Sun Jul 05 13:05:06 2020 +0200
+++ b/mercurial/utils/procutil.py	Sun Jul 05 13:09:22 2020 +0200
@@ -99,6 +99,18 @@
     else:
         stdout = os.fdopen(stdout.fileno(), 'wb', 1)
 
+# stderr should be unbuffered
+if pycompat.ispy3:
+    # On Python 3, buffered streams may expose an underlying raw stream. This is
+    # definitively the case for the streams initialized by the interpreter. If
+    # the attribute isn't present, the stream is already unbuffered or doesn't
+    # expose an underlying raw stream, in which case we use the stream as-is.
+    stderr = getattr(stderr, 'raw', stderr)
+elif pycompat.iswindows:
+    # On Windows, stderr is buffered at least when connected to a pipe.
+    stderr = os.fdopen(stderr.fileno(), 'wb', 0)
+# On other platforms, stderr is always unbuffered.
+
 
 findexe = platform.findexe
 _gethgcmd = platform.gethgcmd
--- a/tests/test-stdio.py	Sun Jul 05 13:05:06 2020 +0200
+++ b/tests/test-stdio.py	Sun Jul 05 13:09:22 2020 +0200
@@ -100,6 +100,18 @@
             test_stdout_ptys_unbuffered
         )
 
+    def test_stderr_pipes(self):
+        self._test('stderr', _pipes, UNBUFFERED)
+
+    def test_stderr_ptys(self):
+        self._test('stderr', _ptys, UNBUFFERED)
+
+    def test_stderr_pipes_unbuffered(self):
+        self._test('stderr', _pipes, UNBUFFERED, python_args=['-u'])
+
+    def test_stderr_ptys_unbuffered(self):
+        self._test('stderr', _ptys, UNBUFFERED, python_args=['-u'])
+
 
 if __name__ == '__main__':
     import silenttestrunner