mirror of
https://github.com/python/cpython.git
synced 2025-07-12 13:55:34 +00:00
Issue #21425: Fix flushing of standard streams in the interactive interpreter.
This commit is contained in:
commit
1c33280c95
4 changed files with 56 additions and 3 deletions
|
@ -78,7 +78,7 @@ def assert_python_failure(*args, **env_vars):
|
||||||
"""
|
"""
|
||||||
return _assert_python(False, *args, **env_vars)
|
return _assert_python(False, *args, **env_vars)
|
||||||
|
|
||||||
def spawn_python(*args, **kw):
|
def spawn_python(*args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kw):
|
||||||
"""Run a Python subprocess with the given arguments.
|
"""Run a Python subprocess with the given arguments.
|
||||||
|
|
||||||
kw is extra keyword args to pass to subprocess.Popen. Returns a Popen
|
kw is extra keyword args to pass to subprocess.Popen. Returns a Popen
|
||||||
|
@ -87,7 +87,7 @@ def spawn_python(*args, **kw):
|
||||||
cmd_line = [sys.executable, '-E']
|
cmd_line = [sys.executable, '-E']
|
||||||
cmd_line.extend(args)
|
cmd_line.extend(args)
|
||||||
return subprocess.Popen(cmd_line, stdin=subprocess.PIPE,
|
return subprocess.Popen(cmd_line, stdin=subprocess.PIPE,
|
||||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
stdout=stdout, stderr=stderr,
|
||||||
**kw)
|
**kw)
|
||||||
|
|
||||||
def kill_python(p):
|
def kill_python(p):
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
# tests command line execution of scripts
|
# tests command line execution of scripts
|
||||||
|
|
||||||
|
import contextlib
|
||||||
import importlib
|
import importlib
|
||||||
import importlib.machinery
|
import importlib.machinery
|
||||||
import zipimport
|
import zipimport
|
||||||
|
@ -8,6 +9,7 @@ import sys
|
||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
import py_compile
|
import py_compile
|
||||||
|
import subprocess
|
||||||
|
|
||||||
import textwrap
|
import textwrap
|
||||||
from test import support
|
from test import support
|
||||||
|
@ -173,6 +175,53 @@ class CmdLineTest(unittest.TestCase):
|
||||||
expected = repr(importlib.machinery.BuiltinImporter).encode("utf-8")
|
expected = repr(importlib.machinery.BuiltinImporter).encode("utf-8")
|
||||||
self.assertIn(expected, out)
|
self.assertIn(expected, out)
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def interactive_python(self, separate_stderr=False):
|
||||||
|
if separate_stderr:
|
||||||
|
p = spawn_python('-i', bufsize=1, stderr=subprocess.PIPE)
|
||||||
|
stderr = p.stderr
|
||||||
|
else:
|
||||||
|
p = spawn_python('-i', bufsize=1, stderr=subprocess.STDOUT)
|
||||||
|
stderr = p.stdout
|
||||||
|
try:
|
||||||
|
# Drain stderr until prompt
|
||||||
|
while True:
|
||||||
|
data = stderr.read(4)
|
||||||
|
if data == b">>> ":
|
||||||
|
break
|
||||||
|
stderr.readline()
|
||||||
|
yield p
|
||||||
|
finally:
|
||||||
|
kill_python(p)
|
||||||
|
stderr.close()
|
||||||
|
|
||||||
|
def check_repl_stdout_flush(self, separate_stderr=False):
|
||||||
|
with self.interactive_python(separate_stderr) as p:
|
||||||
|
p.stdin.write(b"print('foo')\n")
|
||||||
|
p.stdin.flush()
|
||||||
|
self.assertEqual(b'foo', p.stdout.readline().strip())
|
||||||
|
|
||||||
|
def check_repl_stderr_flush(self, separate_stderr=False):
|
||||||
|
with self.interactive_python(separate_stderr) as p:
|
||||||
|
p.stdin.write(b"1/0\n")
|
||||||
|
p.stdin.flush()
|
||||||
|
stderr = p.stderr if separate_stderr else p.stdout
|
||||||
|
self.assertIn(b'Traceback ', stderr.readline())
|
||||||
|
self.assertIn(b'File "<stdin>"', stderr.readline())
|
||||||
|
self.assertIn(b'ZeroDivisionError', stderr.readline())
|
||||||
|
|
||||||
|
def test_repl_stdout_flush(self):
|
||||||
|
self.check_repl_stdout_flush()
|
||||||
|
|
||||||
|
def test_repl_stdout_flush_separate_stderr(self):
|
||||||
|
self.check_repl_stdout_flush(True)
|
||||||
|
|
||||||
|
def test_repl_stderr_flush(self):
|
||||||
|
self.check_repl_stderr_flush()
|
||||||
|
|
||||||
|
def test_repl_stderr_flush_separate_stderr(self):
|
||||||
|
self.check_repl_stderr_flush(True)
|
||||||
|
|
||||||
def test_basic_script(self):
|
def test_basic_script(self):
|
||||||
with temp_dir() as script_dir:
|
with temp_dir() as script_dir:
|
||||||
script_name = _make_test_script(script_dir, 'script')
|
script_name = _make_test_script(script_dir, 'script')
|
||||||
|
|
|
@ -10,6 +10,9 @@ Release date: TBA
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #21425: Fix flushing of standard streams in the interactive
|
||||||
|
interpreter.
|
||||||
|
|
||||||
- Issue #21435: In rare cases, when running finalizers on objects in cyclic
|
- Issue #21435: In rare cases, when running finalizers on objects in cyclic
|
||||||
trash a bad pointer dereference could occur due to a subtle flaw in
|
trash a bad pointer dereference could occur due to a subtle flaw in
|
||||||
internal iteration logic.
|
internal iteration logic.
|
||||||
|
|
|
@ -1453,12 +1453,13 @@ PyRun_InteractiveOneObject(FILE *fp, PyObject *filename, PyCompilerFlags *flags)
|
||||||
d = PyModule_GetDict(m);
|
d = PyModule_GetDict(m);
|
||||||
v = run_mod(mod, filename, d, d, flags, arena);
|
v = run_mod(mod, filename, d, d, flags, arena);
|
||||||
PyArena_Free(arena);
|
PyArena_Free(arena);
|
||||||
flush_io();
|
|
||||||
if (v == NULL) {
|
if (v == NULL) {
|
||||||
PyErr_Print();
|
PyErr_Print();
|
||||||
|
flush_io();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
Py_DECREF(v);
|
Py_DECREF(v);
|
||||||
|
flush_io();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue