Skip to content

Fix sys.excepthook #5830

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions Lib/test/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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):
Expand Down
2 changes: 0 additions & 2 deletions Lib/test/test_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 8 additions & 0 deletions compiler/codegen/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 17 additions & 2 deletions vm/src/stdlib/sys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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__")]
Expand Down
Loading