From a57328ecfbed828272d8a0357636c76068073722 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Tue, 24 Jun 2025 13:41:14 +0900 Subject: [PATCH] Fix excepthook --- Lib/test/test_exceptions.py | 6 --- Lib/test/test_sys.py | 2 - compiler/codegen/src/compile.rs | 8 ++++ ...pile__tests__nested_double_async_with.snap | 47 ++++++++++--------- vm/src/stdlib/sys.rs | 19 +++++++- 5 files changed, 50 insertions(+), 32 deletions(-) diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index fec040716b..eb377877bb 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -701,8 +701,6 @@ def test_str(self): self.assertTrue(str(Exception('a'))) self.assertTrue(str(Exception('a', 'b'))) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_exception_cleanup_names(self): # Make sure the local variable bound to the exception instance by # an "except" statement is only visible inside the except block. @@ -725,8 +723,6 @@ def test_exception_cleanup_names2(self): with self.assertRaises(UnboundLocalError): e - # TODO: RUSTPYTHON - @unittest.expectedFailure def testExceptionCleanupState(self): # Make sure exception state is cleaned up as soon as the except # block is left. See #2507 @@ -1573,8 +1569,6 @@ def inner(): gc_collect() # For PyPy or other GCs. self.assertEqual(wr(), None) - # TODO: RUSTPYTHON - @unittest.expectedFailure @no_tracing @unittest.skipIf(sys.platform == 'win32', 'TODO: RUSTPYTHON Windows') def test_recursion_error_cleanup(self): diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 12e683c94d..1ce2e9fc0f 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -167,8 +167,6 @@ def test_excepthook_bytes_filename(self): self.assertIn(""" text\n""", err) self.assertTrue(err.endswith("SyntaxError: msg\n")) - # TODO: RUSTPYTHON, print argument error to stderr in sys.excepthook instead of throwing - @unittest.expectedFailure def test_excepthook(self): with test.support.captured_output("stderr") as stderr: sys.excepthook(1, '1', 1) diff --git a/compiler/codegen/src/compile.rs b/compiler/codegen/src/compile.rs index 4d9bbf23ff..d6d4cc16b3 100644 --- a/compiler/codegen/src/compile.rs +++ b/compiler/codegen/src/compile.rs @@ -1317,6 +1317,14 @@ impl Compiler<'_> { self.compile_statements(body)?; emit!(self, Instruction::PopException); + // Delete the exception variable if it was bound + if let Some(alias) = name { + // Set the variable to None before deleting (as CPython does) + self.emit_load_const(ConstantData::None); + self.store_name(alias.as_str())?; + self.compile_name(alias.as_str(), NameUsage::Delete)?; + } + if !finalbody.is_empty() { emit!(self, Instruction::PopBlock); // pop excepthandler block // We enter the finally block, without exception. diff --git a/compiler/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap b/compiler/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap index 91523b2582..36b00c567d 100644 --- a/compiler/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap +++ b/compiler/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap @@ -11,7 +11,7 @@ expression: "compile_exec(\"\\\nfor stop_exc in (StopIteration('spam'), StopAsyn 6 CallFunctionPositional(1) 7 BuildTuple (2) 8 GetIter - >> 9 ForIter (68) + >> 9 ForIter (71) 10 StoreLocal (2, stop_exc) 2 11 LoadNameAny (3, self) @@ -21,7 +21,7 @@ expression: "compile_exec(\"\\\nfor stop_exc in (StopIteration('spam'), StopAsyn 15 CallFunctionPositional(1) 16 LoadConst (("type")) 17 CallMethodKeyword (1) - 18 SetupWith (65) + 18 SetupWith (68) 19 Pop 3 20 SetupExcept (40) @@ -46,12 +46,12 @@ expression: "compile_exec(\"\\\nfor stop_exc in (StopIteration('spam'), StopAsyn 36 YieldFrom 37 WithCleanupFinish 38 PopBlock - 39 Jump (54) + 39 Jump (57) >> 40 Duplicate 6 41 LoadNameAny (7, Exception) 42 TestOperation (ExceptionMatch) - 43 JumpIfFalse (53) + 43 JumpIfFalse (56) 44 StoreLocal (8, ex) 7 45 LoadNameAny (3, self) @@ -61,23 +61,26 @@ expression: "compile_exec(\"\\\nfor stop_exc in (StopIteration('spam'), StopAsyn 49 CallMethodPositional (2) 50 Pop 51 PopException - 52 Jump (63) - >> 53 Raise (Reraise) + 52 LoadConst (None) + 53 StoreLocal (8, ex) + 54 DeleteLocal (8, ex) + 55 Jump (66) + >> 56 Raise (Reraise) - 9 >> 54 LoadNameAny (3, self) - 55 LoadMethod (10, fail) - 56 LoadConst ("") - 57 LoadNameAny (2, stop_exc) - 58 FormatValue (None) - 59 LoadConst (" was suppressed") - 60 BuildString (2) - 61 CallMethodPositional (1) - 62 Pop + 9 >> 57 LoadNameAny (3, self) + 58 LoadMethod (10, fail) + 59 LoadConst ("") + 60 LoadNameAny (2, stop_exc) + 61 FormatValue (None) + 62 LoadConst (" was suppressed") + 63 BuildString (2) + 64 CallMethodPositional (1) + 65 Pop - 2 >> 63 PopBlock - 64 EnterFinally - >> 65 WithCleanupStart - 66 WithCleanupFinish - 67 Jump (9) - >> 68 PopBlock - 69 ReturnConst (None) + 2 >> 66 PopBlock + 67 EnterFinally + >> 68 WithCleanupStart + 69 WithCleanupFinish + 70 Jump (9) + >> 71 PopBlock + 72 ReturnConst (None) diff --git a/vm/src/stdlib/sys.rs b/vm/src/stdlib/sys.rs index 111198f758..a940f2ce17 100644 --- a/vm/src/stdlib/sys.rs +++ b/vm/src/stdlib/sys.rs @@ -363,9 +363,24 @@ mod sys { exc_tb: PyObjectRef, vm: &VirtualMachine, ) -> PyResult<()> { - let exc = vm.normalize_exception(exc_type, exc_val, exc_tb)?; let stderr = super::get_stderr(vm)?; - vm.write_exception(&mut crate::py_io::PyWriter(stderr, vm), &exc) + + // Try to normalize the exception. If it fails, print error to stderr like CPython + match vm.normalize_exception(exc_type.clone(), exc_val.clone(), exc_tb) { + Ok(exc) => vm.write_exception(&mut crate::py_io::PyWriter(stderr, vm), &exc), + Err(_) => { + // CPython prints error message to stderr instead of raising exception + let type_name = exc_val.class().name(); + // TODO: fix error message + let msg = format!( + "TypeError: print_exception(): Exception expected for value, {} found\n", + type_name + ); + use crate::py_io::Write; + write!(&mut crate::py_io::PyWriter(stderr, vm), "{}", msg)?; + Ok(()) + } + } } #[pyfunction(name = "__breakpointhook__")]