IDLE Shell 忙碌时运行 Run... Customizedgh-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.pyRun Modulerun-module语法检查后在 Shell 中执行当前模块默认会重启 Shell 子进程Run... Customizedrun-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 fCustomize {self.editwin.short_title()} Run run_args CustomRun(self.shell.text, title, cli_argsself.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_cwdFalse, filenamefilename)新增的守卫条件if self.shell.executing and not restart:准确描述了修复语义self.shell.executing为 TrueShell 正忙于执行代码restart为 False用户通过 Run... Customized 明确取消了 Restart shell 勾选。两者同时成立时代码不再继续执行interp.restart_subprocess(...)与后续的interp.runcommand(...)/interp.runcode(...)而是调用display_executing_dialog()弹出错误对话框并提前返回break。值得注意的是run_custom_event是run_module_event的薄封装def run_custom_event(self, event): return self.run_module_event(event, customizeTrue)因此两条命令共享同一执行函数修复对两者都生效普通 Run Module 因为restart恒为True不受新守卫影响行为保持不变。三、底层原理executing 状态与 display_executing_dialog3.1executing标志的维护executing是 Shell 窗口PyShell继承自OutputWindow的一个实例属性定义于 Lib/idlelib/pyshell.pyreading False executing False canceled False它由两个配套方法控制定义在同文件 pyshell.pybeginexecuting()调用resetoutput()清空输出区后置executing Trueendexecuting()置executing False、清空canceled并重新显示提示符showprompt()。ModifiedInterpreter.runcode()pyshell.py在真正执行用户代码前调用self.tkconsole.beginexecuting()执行结束无论成功还是异常在finally中恢复。因此只要 Shell 内正在跑耗时任务executing就持续为True。3.2 错误对话框的实现新增守卫调用的display_executing_dialog()定义于 pyshell.pydef display_executing_dialog(self): messagebox.showerror( Already executing, The Python Shell window is already executing a command; please wait until it is finished., parentself.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 为例菜单绑定mainmenu.py 定义(Run... _Customized, run-custom)按键绑定事件run-custom在 Lib/idlelib/editor.py 绑定到scriptbinding.run_custom_event默认快捷键ShiftF5定义于 Lib/idlelib/config-keys.defWindows 下为run-custom Shift-Key-F5config.py 注册该默认绑定弹窗收集参数run_custom_event→run_module_event(customizeTrue)→ 弹出CustomRun对话框Lib/idlelib/query.py。该对话框提供两处输入Command Line Arguments用shlex.split按 POSIX 规则解析为sys.argv扩展列表和 Restart shell 复选框BooleanVar默认勾选True语法预检checksyntax()与tabnanny()分别做语法检查和缩进一致性检查详见 runscript.py 与 runscript.py守卫判断self.shell.executing and not restart成立 →interp.display_executing_dialog()弹框并return break整个运行流程中止正常路径若 Shell 空闲则按restart决定是否interp.restart_subprocess(with_cwdFalse, filenamefilename)pyshell.py 会关闭旧子进程、spawn_subprocess()新建、rpcclt.accept()接受连接并清空 Shell 输出随后设置__file__、sys.argv、os.chdir()到模块目录、prepend_syspath(filename)加入搜索路径最后interp.runcode(code)真正执行。五、测试验证为修复写下的回归测试该修复配套了专门的回归测试位于 Lib/idlelib/idle_test/test_runscript.pydef test_run_module_event_shell_busy_no_restart(self): # gh-82183: running without restarting the busy shell aborts. ew EditorWindow(rootself.root) sb runscript.ScriptBinding(ew) sb.getfilename mock.Mock(return_valuetest.py) sb.checksyntax mock.Mock(return_valuecode) sb.tabnanny mock.Mock(return_valueTrue) 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, customizeTrue) 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_subprocess与runcode均未被调用。这三个断言保证了“报错而非运行”的修复方向既不重启 Shell也不向繁忙的解释器提交任何代码。该测试要求gui环境requires(gui)见测试类 setUpClass因此通常在带图形界面的桌面环境中运行例如./python -m unittest -v idlelib.idle_test.test_runscript六、实际操作如何复现与规避6.1 复现步骤在 IDLE 编辑器窗口打开一个脚本例如包含time.sleep(30)的模块用Run ModuleF5启动它Shell 随即进入忙碌状态在 Shell 执行完之前回到编辑器选择Run → Run... CustomizedShiftF5在弹出的对话框中取消勾选 Restart shell点击 Run修复后的行为弹出 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),仅供参考