From 27390dc54ab59c6f14467f508a18f2fa17d7ceef Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" Date: Mon, 16 Feb 2026 22:06:27 +0900 Subject: [PATCH] Use _print_exception_bltin in excepthook, register source in linecache - excepthook: call traceback._print_exception_bltin instead of traceback.print_exception to match PyErr_Display behavior - run_string: register compiled code in linecache._interactive_cache so traceback can display source lines and caret indicators - Remove test_sys_tracebacklimit expectedFailure --- .cspell.dict/cpython.txt | 1 + Lib/test/test_sys.py | 1 - Lib/test/test_warnings/__init__.py | 1 - crates/vm/src/stdlib/sys.rs | 10 ++++++---- crates/vm/src/vm/python_run.rs | 16 +++++++++++++++- 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/.cspell.dict/cpython.txt b/.cspell.dict/cpython.txt index c0d007a90c3..f428c42e5f6 100644 --- a/.cspell.dict/cpython.txt +++ b/.cspell.dict/cpython.txt @@ -11,6 +11,7 @@ badsyntax baseinfo basetype binop +bltin boolop BUFMAX BUILDSTDLIB diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index b72d09865d8..9ebd6dd9cc1 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1232,7 +1232,6 @@ def test_getandroidapilevel(self): self.assertIsInstance(level, int) self.assertGreater(level, 0) - @unittest.expectedFailure # TODO: RUSTPYTHON @force_not_colorized @support.requires_subprocess() def test_sys_tracebacklimit(self): diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py index d466128e8be..53ac0363a3c 100644 --- a/Lib/test/test_warnings/__init__.py +++ b/Lib/test/test_warnings/__init__.py @@ -1478,7 +1478,6 @@ def test_envvar_and_command_line(self): self.assertEqual(stdout, b"['ignore::DeprecationWarning', 'ignore::UnicodeWarning']") - @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: b"['error::DeprecationWarning']" != b"['default::DeprecationWarning', 'error::DeprecationWarning']" @force_not_colorized def test_conflicting_envvar_and_command_line(self): rc, stdout, stderr = assert_python_failure("-Werror::DeprecationWarning", "-c", diff --git a/crates/vm/src/stdlib/sys.rs b/crates/vm/src/stdlib/sys.rs index 0a6f26d642c..22b720a1cd2 100644 --- a/crates/vm/src/stdlib/sys.rs +++ b/crates/vm/src/stdlib/sys.rs @@ -825,11 +825,13 @@ mod sys { let stderr = super::get_stderr(vm)?; match vm.normalize_exception(exc_type.clone(), exc_val.clone(), exc_tb) { Ok(exc) => { - // Try Python traceback module first for richer output - // (enables features like keyword typo suggestions in SyntaxError) + // PyErr_Display: try traceback._print_exception_bltin first if let Ok(tb_mod) = vm.import("traceback", 0) - && let Ok(print_exc) = tb_mod.get_attr("print_exception", vm) - && print_exc.call((exc.as_object().to_owned(),), vm).is_ok() + && let Ok(print_exc_builtin) = + tb_mod.get_attr("_print_exception_bltin", vm) + && print_exc_builtin + .call((exc.as_object().to_owned(),), vm) + .is_ok() { return Ok(()); } diff --git a/crates/vm/src/vm/python_run.rs b/crates/vm/src/vm/python_run.rs index e651b34cc50..70d845b03f5 100644 --- a/crates/vm/src/vm/python_run.rs +++ b/crates/vm/src/vm/python_run.rs @@ -1,7 +1,8 @@ //! Python code execution functions. use crate::{ - PyResult, VirtualMachine, + AsObject, PyRef, PyResult, VirtualMachine, + builtins::PyCode, compiler::{self}, scope::Scope, }; @@ -22,9 +23,22 @@ impl VirtualMachine { let code_obj = self .compile(source, compiler::Mode::Exec, source_path) .map_err(|err| self.new_syntax_error(&err, Some(source)))?; + // linecache._register_code(code, source, filename) + let _ = self.register_code_in_linecache(&code_obj, source); self.run_code_obj(code_obj, scope) } + /// Register a code object's source in linecache._interactive_cache + /// so that traceback can display source lines and caret indicators. + fn register_code_in_linecache(&self, code: &PyRef, source: &str) -> PyResult<()> { + let linecache = self.import("linecache", 0)?; + let register = linecache.get_attr("_register_code", self)?; + let source_str = self.ctx.new_str(source); + let filename = self.ctx.new_str(code.source_path().as_str()); + register.call((code.as_object().to_owned(), source_str, filename), self)?; + Ok(()) + } + #[deprecated(note = "use run_string instead")] pub fn run_code_string(&self, scope: Scope, source: &str, source_path: String) -> PyResult { self.run_string(scope, source, source_path)