Skip to content

Commit 9845c7e

Browse files
committed
Issue #21425: Fix flushing of standard streams in the interactive interpreter.
1 parent 3d1bc60 commit 9845c7e

File tree

4 files changed

+57
-4
lines changed

4 files changed

+57
-4
lines changed

Lib/test/script_helper.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ def assert_python_failure(*args, **env_vars):
7878
"""
7979
return _assert_python(False, *args, **env_vars)
8080

81-
def spawn_python(*args, **kw):
81+
def spawn_python(*args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kw):
8282
"""Run a Python subprocess with the given arguments.
8383
8484
kw is extra keyword args to pass to subprocess.Popen. Returns a Popen
@@ -87,7 +87,7 @@ def spawn_python(*args, **kw):
8787
cmd_line = [sys.executable, '-E']
8888
cmd_line.extend(args)
8989
return subprocess.Popen(cmd_line, stdin=subprocess.PIPE,
90-
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
90+
stdout=stdout, stderr=stderr,
9191
**kw)
9292

9393
def kill_python(p):

Lib/test/test_cmd_line_script.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# tests command line execution of scripts
22

3+
import contextlib
34
import importlib
45
import importlib.machinery
56
import zipimport
@@ -8,6 +9,7 @@
89
import os
910
import os.path
1011
import py_compile
12+
import subprocess
1113

1214
import textwrap
1315
from test import support
@@ -173,6 +175,53 @@ def test_stdin_loader(self):
173175
expected = repr(importlib.machinery.BuiltinImporter).encode("utf-8")
174176
self.assertIn(expected, out)
175177

178+
@contextlib.contextmanager
179+
def interactive_python(self, separate_stderr=False):
180+
if separate_stderr:
181+
p = spawn_python('-i', bufsize=1, stderr=subprocess.PIPE)
182+
stderr = p.stderr
183+
else:
184+
p = spawn_python('-i', bufsize=1, stderr=subprocess.STDOUT)
185+
stderr = p.stdout
186+
try:
187+
# Drain stderr until prompt
188+
while True:
189+
data = stderr.read(4)
190+
if data == b">>> ":
191+
break
192+
stderr.readline()
193+
yield p
194+
finally:
195+
kill_python(p)
196+
stderr.close()
197+
198+
def check_repl_stdout_flush(self, separate_stderr=False):
199+
with self.interactive_python(separate_stderr) as p:
200+
p.stdin.write(b"print('foo')\n")
201+
p.stdin.flush()
202+
self.assertEqual(b'foo', p.stdout.readline().strip())
203+
204+
def check_repl_stderr_flush(self, separate_stderr=False):
205+
with self.interactive_python(separate_stderr) as p:
206+
p.stdin.write(b"1/0\n")
207+
p.stdin.flush()
208+
stderr = p.stderr if separate_stderr else p.stdout
209+
self.assertIn(b'Traceback ', stderr.readline())
210+
self.assertIn(b'File "<stdin>"', stderr.readline())
211+
self.assertIn(b'ZeroDivisionError', stderr.readline())
212+
213+
def test_repl_stdout_flush(self):
214+
self.check_repl_stdout_flush()
215+
216+
def test_repl_stdout_flush_separate_stderr(self):
217+
self.check_repl_stdout_flush(True)
218+
219+
def test_repl_stderr_flush(self):
220+
self.check_repl_stderr_flush()
221+
222+
def test_repl_stderr_flush_separate_stderr(self):
223+
self.check_repl_stderr_flush(True)
224+
176225
def test_basic_script(self):
177226
with temp_dir() as script_dir:
178227
script_name = _make_test_script(script_dir, 'script')

Misc/NEWS

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@ Python News
33
+++++++++++
44

55
What's New in Python 3.4.1?
6-
==========================
6+
===========================
77

88
Release date: TBA
99

1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #21425: Fix flushing of standard streams in the interactive
14+
interpreter.
15+
1316
- Issue #21435: In rare cases, when running finalizers on objects in cyclic
1417
trash a bad pointer dereference could occur due to a subtle flaw in
1518
internal iteration logic.

Python/pythonrun.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1444,12 +1444,13 @@ PyRun_InteractiveOneObject(FILE *fp, PyObject *filename, PyCompilerFlags *flags)
14441444
d = PyModule_GetDict(m);
14451445
v = run_mod(mod, filename, d, d, flags, arena);
14461446
PyArena_Free(arena);
1447-
flush_io();
14481447
if (v == NULL) {
14491448
PyErr_Print();
1449+
flush_io();
14501450
return -1;
14511451
}
14521452
Py_DECREF(v);
1453+
flush_io();
14531454
return 0;
14541455
}
14551456

0 commit comments

Comments
 (0)