https://github.com/python/cpython/commit/89c67a98aeebee80d4cddcca86d9e52ceea89874
commit: 89c67a98aeebee80d4cddcca86d9e52ceea89874
branch: main
author: Serhiy Storchaka <[email protected]>
committer: terryjreedy <[email protected]>
date: 2026-09-19T23:06:11-04:00
summary:
gh-75512: Keep the prompt with the input in progress when a thread prints in
the IDLE Shell (#157706)
Output written while a prompt is shown was inserted after the newline
which marks the prompt, so the statement being entered lost the prompt.
If the output ends with a newline, the prompt is moved to the next line.
files:
A Misc/NEWS.d/next/IDLE/2026-09-17-22-00-00.gh-issue-75512.asyncout.rst
M Lib/idlelib/idle_test/test_pyshell.py
M Lib/idlelib/pyshell.py
diff --git a/Lib/idlelib/idle_test/test_pyshell.py
b/Lib/idlelib/idle_test/test_pyshell.py
index dec81bdbbccd67..0e292e7be87ee4 100644
--- a/Lib/idlelib/idle_test/test_pyshell.py
+++ b/Lib/idlelib/idle_test/test_pyshell.py
@@ -69,6 +69,46 @@ def test_init(self):
## self.assertIsInstance(ps, pyshell.PyShell)
+class PyShellTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ requires('gui')
+ cls.root = Tk()
+ cls.root.withdraw()
+ cls.shell = pyshell.PyShell(pyshell.PyShellFileList(cls.root))
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.shell.close()
+ del cls.shell
+ cls.root.destroy()
+ del cls.root
+
+ def setUp(self):
+ text = self.shell.text
+ self.shell.per.bottom.delete('1.0', 'end')
+ text.mark_set('iomark', '1.0') # As after begin().
+ text.mark_gravity('iomark', 'left')
+ self.shell.undo.reset_undo()
+
+ def test_output_at_prompt(self):
+ # gh-75512: output from a thread while a prompt is shown goes
+ # before the prompt, which stays with the input in progress.
+ shell = self.shell
+ text = shell.text
+ shell.write('x\n', 'stdout')
+ shell.resetoutput()
+ text.tag_add('console', 'iomark-1c') # As showprompt() does.
+ text.insert('end-1c', 'a = (')
+ shell.write('hel', 'stdout')
+ shell.write('lo\n', 'stdout')
+ self.assertEqual(text.get('iomark-6c', 'end-1c'), 'hello\na = (')
+ self.assertIn('console', text.tag_names('iomark-1c'))
+ self.assertNotIn('console', text.tag_names('iomark-7c'))
+ self.assertEqual(shell.shell_sidebar.line_prompts, {3: '>>>'})
+
+
class PyShellRemoveLastNewlineAndSurroundingWhitespaceTest(unittest.TestCase):
regexp = pyshell.PyShell._last_newline_re
diff --git a/Lib/idlelib/pyshell.py b/Lib/idlelib/pyshell.py
index 997b91e53327d7..a7374377b1a34b 100755
--- a/Lib/idlelib/pyshell.py
+++ b/Lib/idlelib/pyshell.py
@@ -1432,13 +1432,24 @@ def resetoutput(self):
self.ctip.remove_calltip_window()
def write(self, s, tags=()):
+ text = self.text
+ # Move the prompt (the "console" tag on the preceding newline)
+ # after output which comes while it is shown (gh-75512).
+ at_prompt = (s and tags in ("stdout", "stderr")
+ and not self.executing and not self.reading)
+ if at_prompt:
+ text.tag_remove("console", "iomark-1c")
+ text.tag_remove("stdin", "iomark-1c")
try:
- self.text.mark_gravity("iomark", "right")
+ text.mark_gravity("iomark", "right")
count = OutputWindow.write(self, s, tags, "iomark")
- self.text.mark_gravity("iomark", "left")
+ text.mark_gravity("iomark", "left")
except:
raise ###pass # ### 11Aug07 KBK if we are expecting exceptions
# let's find out what they are and be specific.
+ if at_prompt and s.endswith('\n'):
+ text.tag_add("console", "iomark-1c")
+ self.shell_sidebar.update_sidebar()
if self.canceled:
self.canceled = False
if not use_subprocess:
diff --git
a/Misc/NEWS.d/next/IDLE/2026-09-17-22-00-00.gh-issue-75512.asyncout.rst
b/Misc/NEWS.d/next/IDLE/2026-09-17-22-00-00.gh-issue-75512.asyncout.rst
new file mode 100644
index 00000000000000..166cb6971f9b0e
--- /dev/null
+++ b/Misc/NEWS.d/next/IDLE/2026-09-17-22-00-00.gh-issue-75512.asyncout.rst
@@ -0,0 +1,2 @@
+Output from a thread printed in the IDLE Shell while a statement is being
+entered no longer takes over the prompt of the statement.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]