procutil: make mercurial.utils.procutil.stderr unbuffered
authorManuel Jacob <me@manueljacob.de>
Sun, 05 Jul 2020 13:09:22 +0200
changeset 45045 8403cc54bc83
parent 45044 359884685eab
child 45046 dd3050227a84
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.
mercurial/utils/procutil.py
tests/test-stdio.py
--- 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