From 5b9077134cd0535f21905d5f5195847526cac99c Mon Sep 17 00:00:00 2001 From: Jendrik Seipp Date: Wed, 1 Jan 2020 23:21:43 +0100 Subject: [PATCH] bpo-13601: always use line-buffering for sys.stderr (GH-17646) --- Doc/library/sys.rst | 12 +++++++++--- Lib/test/test_cmd_line.py | 16 ++++++++++++++++ Misc/ACKS | 1 + .../2019-12-17-22-32-11.bpo-13601.vNP4LC.rst | 6 ++++++ Python/pylifecycle.c | 2 +- 5 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2019-12-17-22-32-11.bpo-13601.vNP4LC.rst diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index a824fb95e8e..0aae263ff5f 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -1446,9 +1446,15 @@ always available. for the Windows console, this only applies when :envvar:`PYTHONLEGACYWINDOWSSTDIO` is also set. - * When interactive, ``stdout`` and ``stderr`` streams are line-buffered. - Otherwise, they are block-buffered like regular text files. You can - override this value with the :option:`-u` command-line option. + * When interactive, the ``stdout`` stream is line-buffered. Otherwise, + it is block-buffered like regular text files. The ``stderr`` stream + is line-buffered in both cases. You can make both streams unbuffered + by passing the :option:`-u` command-line option or setting the + :envvar:`PYTHONUNBUFFERED` environment variable. + + .. versionchanged:: 3.9 + Non-interactive ``stderr`` is now line-buffered instead of fully + buffered. .. note:: diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py index 47810020dd3..ee96473322d 100644 --- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -6,6 +6,7 @@ import os import subprocess import sys import tempfile +import textwrap import unittest from test import support from test.support.script_helper import ( @@ -219,6 +220,21 @@ class CmdLineTest(unittest.TestCase): ) check_output(text) + def test_non_interactive_output_buffering(self): + code = textwrap.dedent(""" + import sys + out = sys.stdout + print(out.isatty(), out.write_through, out.line_buffering) + err = sys.stderr + print(err.isatty(), err.write_through, err.line_buffering) + """) + args = [sys.executable, '-c', code] + proc = subprocess.run(args, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, text=True, check=True) + self.assertEqual(proc.stdout, + 'False False False\n' + 'False False True\n') + def test_unbuffered_output(self): # Test expected operation of the '-u' switch for stream in ('stdout', 'stderr'): diff --git a/Misc/ACKS b/Misc/ACKS index 5ecbac13e0b..d3e683d4a08 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1510,6 +1510,7 @@ Steven Scott Nick Seidenman Michael Seifert Žiga Seilnacht +Jendrik Seipp Michael Selik Yury Selivanov Fred Sells diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-12-17-22-32-11.bpo-13601.vNP4LC.rst b/Misc/NEWS.d/next/Core and Builtins/2019-12-17-22-32-11.bpo-13601.vNP4LC.rst new file mode 100644 index 00000000000..f2c9495a59a --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-12-17-22-32-11.bpo-13601.vNP4LC.rst @@ -0,0 +1,6 @@ +By default, ``sys.stderr`` is line-buffered now, even if ``stderr`` is +redirected to a file. You can still make ``sys.stderr`` unbuffered by +passing the :option:`-u` command-line option or setting the +:envvar:`PYTHONUNBUFFERED` environment variable. + +(Contributed by Jendrik Seipp in bpo-13601.) diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 4f05dfc3490..94bbbdb560e 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1817,7 +1817,7 @@ create_stdio(const PyConfig *config, PyObject* io, write_through = Py_True; else write_through = Py_False; - if (isatty && buffered_stdio) + if (buffered_stdio && (isatty || fd == fileno(stderr))) line_buffering = Py_True; else line_buffering = Py_False;