news 2026/9/10 1:43:57

IDLE Shell 忙碌时运行 “Run... Customized“:gh-82183 修复解析与实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
IDLE Shell 忙碌时运行 “Run... Customized“:gh-82183 修复解析与实战指南

IDLE Shell 忙碌时运行 "Run... Customized":gh-82183 修复解析与实战指南

【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython

本指南围绕 CPython 仓库中 IDLE 模块的一项行为修复展开:当 Python Shell 正在执行代码时,用户在编辑器中使用 "Run... Customized"(并取消勾选 "Restart shell")触发运行,IDLE 不再强行重启 Shell,而是明确报告 Shell 正处于执行状态。读完本文,你将掌握该修复的完整背景、底层调用链、触发路径、源码与测试验证方法,以及在实际使用 IDLE 时的注意事项与规避策略。

该修复记录位于 Misc/NEWS.d/next/IDLE/2026-07-01-19-00-00.gh-issue-82183.rNsTrt.rst,本文所有源码与测试证据均来自当前仓库。

一、修复背景:一个被 "Run Module" 掩盖的边界场景

IDLE 编辑器窗口的 Run 菜单包含两条核心命令(定义见 Lib/idlelib/mainmenu.py):

  • Run Module<<run-module>>):语法检查后,在 Shell 中执行当前模块,默认会重启 Shell 子进程;
  • Run... Customized<<run-custom>>):与 Run Module 相同,但弹出对话框允许自定义命令行参数(扩展sys.argv)并选择是否重启 Shell

关键差异在“是否重启”:普通的 Run Module 无条件重启子进程(源码中run_args if customize else ([], True)的默认值即为True),而 Run... Customized 允许用户取消勾选 "Restart shell",把模块直接运行在当前 Shell 上下文中,保留已有命名空间与状态,这与命令行执行python -i file的交互效果类似(参见 Lib/idlelib/help.html 中 "Run… Customized" 的官方说明)。

修复前的隐患正是出在这个“不重启”的选项上:如果此时 Shell 正处于忙碌状态(正在执行一段耗时代码),旧的执行路径仍然会尝试继续运行模块,导致与正在进行的执行冲突,用户体验是 Shell 行为异常而非得到明确提示。

二、修复内容:从“照常重启”到“明确报错”

本次变更的核心逻辑位于 Lib/idlelib/runscript.py 的run_module_event()方法中:

if customize: title = f"Customize {self.editwin.short_title()} Run" run_args = CustomRun(self.shell.text, title, cli_args=self.cli_args).result if not run_args: # User cancelled. return 'break' self.cli_args, restart = run_args if customize else ([], True) interp = self.shell.interp if self.shell.executing and not restart: # Cannot run without restarting the busy shell (gh-82183). interp.display_executing_dialog() return 'break' if pyshell.use_subprocess and restart: interp.restart_subprocess( with_cwd=False, filename=filename)

新增的守卫条件if self.shell.executing and not restart:准确描述了修复语义:

  1. self.shell.executing为 True:Shell 正忙于执行代码;
  2. restart为 False:用户通过 Run... Customized 明确取消了 "Restart shell" 勾选。

两者同时成立时,代码不再继续执行interp.restart_subprocess(...)与后续的interp.runcommand(...)/interp.runcode(...),而是调用display_executing_dialog()弹出错误对话框并提前返回'break'

值得注意的是,run_custom_eventrun_module_event的薄封装:

def run_custom_event(self, event): return self.run_module_event(event, customize=True)

因此两条命令共享同一执行函数,修复对两者都生效;普通 Run Module 因为restart恒为True,不受新守卫影响,行为保持不变。

三、底层原理:executing 状态与 display_executing_dialog

3.1executing标志的维护

executing是 Shell 窗口(PyShell,继承自OutputWindow)的一个实例属性,定义于 Lib/idlelib/pyshell.py:

reading = False executing = False canceled = False

它由两个配套方法控制,定义在同文件 pyshell.py:

  • beginexecuting():调用resetoutput()清空输出区后置executing = True
  • endexecuting():置executing = False、清空canceled并重新显示提示符showprompt()

ModifiedInterpreter.runcode()(pyshell.py)在真正执行用户代码前调用self.tkconsole.beginexecuting(),执行结束(无论成功还是异常)在finally中恢复。因此只要 Shell 内正在跑耗时任务,executing就持续为True

3.2 错误对话框的实现

新增守卫调用的display_executing_dialog()定义于 pyshell.py:

def display_executing_dialog(self): messagebox.showerror( "Already executing", "The Python Shell window is already executing a command; " "please wait until it is finished.", parent=self.tkconsole.text)

它弹出一个标题为 "Already executing" 的模态错误框,提示用户“Shell 正在执行命令,请等待其完成”。该对话框本身并非新方法——它此前已被ModifiedInterpreter.runcommand()用于 Shell 内部冲突场景(pyshell.py),本次修复是将其复用到 Run... Customized 的调用路径上,实现了错误提示的一致性。

3.3 单线程架构下的必然冲突

IDLE 的 GUI 主循环与代码执行共享同一线程(tkinter 单线程模型)。当 Shell 处于executing状态时,再尝试向同一解释器提交新代码,必然与正在执行的代码在sys.argv、工作目录、sys.modules__main__命名空间等全局状态上产生竞争。因此“不重启而强行运行”在架构上就是不安全的,这也解释了为何修复选择“报告错误”而非“排队执行”。

四、触发路径全链路:从菜单到守卫

一次完整的触发链路如下(以 Run... Customized 为例):

  1. 菜单绑定:mainmenu.py 定义('Run... _Customized', '<<run-custom>>')
  2. 按键绑定:事件<<run-custom>>在 Lib/idlelib/editor.py 绑定到scriptbinding.run_custom_event;默认快捷键Shift+F5,定义于 Lib/idlelib/config-keys.def(Windows 下为run-custom= <Shift-Key-F5>,config.py 注册该默认绑定);
  3. 弹窗收集参数run_custom_eventrun_module_event(customize=True)→ 弹出CustomRun对话框(Lib/idlelib/query.py)。该对话框提供两处输入:Command Line Arguments(用shlex.split按 POSIX 规则解析为sys.argv扩展列表)和 "Restart shell" 复选框(BooleanVar,默认勾选True);
  4. 语法预检checksyntax()tabnanny()分别做语法检查和缩进一致性检查(详见 runscript.py 与 runscript.py);
  5. 守卫判断self.shell.executing and not restart成立 →interp.display_executing_dialog()弹框并return 'break',整个运行流程中止;
  6. 正常路径:若 Shell 空闲,则按restart决定是否interp.restart_subprocess(with_cwd=False, filename=filename)(pyshell.py 会关闭旧子进程、spawn_subprocess()新建、rpcclt.accept()接受连接并清空 Shell 输出),随后设置__file__sys.argvos.chdir()到模块目录、prepend_syspath(filename)加入搜索路径,最后interp.runcode(code)真正执行。

五、测试验证:为修复写下的回归测试

该修复配套了专门的回归测试,位于 Lib/idlelib/idle_test/test_runscript.py:

def test_run_module_event_shell_busy_no_restart(self): # gh-82183: running without restarting the busy shell aborts. ew = EditorWindow(root=self.root) sb = runscript.ScriptBinding(ew) sb.getfilename = mock.Mock(return_value='test.py') sb.checksyntax = mock.Mock(return_value='code') sb.tabnanny = mock.Mock(return_value=True) sb.shell = shell = mock.Mock() shell.executing = True interp = shell.interp with mock.patch.object(runscript, 'CustomRun') as customrun: # Restart shell unchecked. customrun.return_value.result = (['arg'], False) result = sb.run_module_event(None, customize=True) self.assertEqual(result, 'break') interp.display_executing_dialog.assert_called_once() interp.restart_subprocess.assert_not_called() interp.runcode.assert_not_called() ew._close()

测试精确覆盖了修复语义:

  • 构造繁忙 Shellshell.executing = True
  • 模拟不重启CustomRun对话框返回(['arg'], False),即restart = False
  • 断言行为:返回'break'(事件被消费)、display_executing_dialog恰好被调用一次、且restart_subprocessruncode均未被调用

这三个断言保证了“报错而非运行”的修复方向:既不重启 Shell,也不向繁忙的解释器提交任何代码。该测试要求gui环境(@requires('gui'),见测试类 setUpClass),因此通常在带图形界面的桌面环境中运行,例如:

./python -m unittest -v idlelib.idle_test.test_runscript

六、实际操作:如何复现与规避

6.1 复现步骤

  1. 在 IDLE 编辑器窗口打开一个脚本,例如包含time.sleep(30)的模块;
  2. Run ModuleF5)启动它,Shell 随即进入忙碌状态;
  3. 在 Shell 执行完之前,回到编辑器,选择Run → Run... CustomizedShift+F5);
  4. 在弹出的对话框中取消勾选 "Restart shell",点击 Run;
  5. 修复后的行为:弹出 "Already executing" 错误框,提示 Shell 正在执行命令,模块不会被运行,Shell 原有执行不受干扰。

6.2 规避与正确做法

  • 若确实希望立即运行新模块,保持"Restart shell" 勾选——restart = True时守卫条件不成立,会走restart_subprocess强制重启子进程后运行;
  • 若希望保留当前 Shell 上下文(不重启),请等待当前代码执行完毕,Shell 重新出现>>>提示符(endexecuting()已调用)后再运行;
  • Shell 窗口菜单栏的Shell → View Last Restart可以快速跳转到最近一次重启标记处,方便确认 Shell 状态(见 help.html)。

七、总结

gh-82183 修复的是一处真实存在的状态竞争:Run... Customized 的“不重启”选项遇上忙碌的 Shell。修复在 Lib/idlelib/runscript.py 增加了一行守卫,将“强行运行”转变为“明确报错”,复用已有的display_executing_dialog()提示,并通过 test_runscript.py 的回归测试锁定行为。这条变更体现了 IDLE 对 Shell 单线程执行模型的一致遵循:无论从哪条路径触发运行,都不允许在 Shell 忙碌时绕过状态检查向解释器注入新代码。

对于使用者,牢记一句话即可:Shell 忙碌且不打算重启时,Run... Customized 会礼貌地拒绝你——先等它忙完,或者干脆勾上 "Restart shell"。

【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 1:42:43

CANN/GE扩展算子列表

扩展算子列表 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前…

作者头像 李华
网站建设 2026/9/10 1:42:13

CANN/ge - ATC概述

ATC概述 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前端的…

作者头像 李华
网站建设 2026/9/10 1:42:11

8款编程助手实测:免费版够用吗?团队选型付费价值深度拆解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华