@@ -5,19 +5,20 @@ use crate::obj::objstr::{PyString, PyStringRef};
55use crate :: obj:: objtraceback:: PyTracebackRef ;
66use crate :: obj:: objtuple:: { PyTuple , PyTupleRef } ;
77use crate :: obj:: objtype:: { self , PyClass , PyClassRef } ;
8- use crate :: py_serde ;
8+ use crate :: py_io :: { self , Write } ;
99use crate :: pyobject:: {
1010 PyClassImpl , PyContext , PyIterable , PyObjectRef , PyRef , PyResult , PyValue , TryFromObject ,
1111 TypeProtocol ,
1212} ;
1313use crate :: slots:: PyTpFlags ;
1414use crate :: types:: create_type;
1515use crate :: VirtualMachine ;
16+ use crate :: { py_serde, sysmodule} ;
1617
1718use itertools:: Itertools ;
1819use std:: fmt;
1920use std:: fs:: File ;
20- use std:: io:: { self , BufRead , BufReader , Write } ;
21+ use std:: io:: { self , BufRead , BufReader } ;
2122
2223use crossbeam_utils:: atomic:: AtomicCell ;
2324
@@ -146,18 +147,35 @@ impl PyBaseException {
146147 }
147148}
148149
149- /// Print exception chain
150- pub fn print_exception ( vm : & VirtualMachine , exc : & PyBaseExceptionRef ) {
151- let stderr = io:: stderr ( ) ;
152- let mut stderr = stderr. lock ( ) ;
153- let _ = write_exception ( & mut stderr, vm, exc) ;
150+ /// Print exception chain by calling sys.excepthook
151+ pub fn print_exception ( vm : & VirtualMachine , exc : PyBaseExceptionRef ) {
152+ let write_fallback = |exc, errstr| {
153+ if let Ok ( stderr) = sysmodule:: get_stderr ( vm) {
154+ let mut stderr = py_io:: PyWriter ( stderr, vm) ;
155+ // if this fails stderr might be closed -- ignore it
156+ let _ = writeln ! ( stderr, "{}" , errstr) ;
157+ let _ = write_exception ( & mut stderr, vm, exc) ;
158+ } else {
159+ eprintln ! ( "{}\n lost sys.stderr" , errstr) ;
160+ let _ = write_exception ( & mut io:: stderr ( ) , vm, exc) ;
161+ }
162+ } ;
163+ if let Ok ( excepthook) = vm. get_attribute ( vm. sys_module . clone ( ) , "excepthook" ) {
164+ let ( exc_type, exc_val, exc_tb) = split ( exc, vm) ;
165+ if let Err ( eh_exc) = vm. invoke ( & excepthook, vec ! [ exc_type, exc_val, exc_tb] ) {
166+ write_fallback ( & eh_exc, "Error in sys.excepthook:" ) ;
167+ write_fallback ( & eh_exc, "Original exception was:" ) ;
168+ }
169+ } else {
170+ write_fallback ( & exc, "missing sys.excepthook" ) ;
171+ }
154172}
155173
156174pub fn write_exception < W : Write > (
157175 output : & mut W ,
158176 vm : & VirtualMachine ,
159177 exc : & PyBaseExceptionRef ,
160- ) -> io :: Result < ( ) > {
178+ ) -> Result < ( ) , W :: Error > {
161179 if let Some ( cause) = exc. cause ( ) {
162180 write_exception ( output, vm, & cause) ?;
163181 writeln ! (
@@ -175,7 +193,11 @@ pub fn write_exception<W: Write>(
175193 write_exception_inner ( output, vm, exc)
176194}
177195
178- fn print_source_line < W : Write > ( output : & mut W , filename : & str , lineno : usize ) -> io:: Result < ( ) > {
196+ fn print_source_line < W : Write > (
197+ output : & mut W ,
198+ filename : & str ,
199+ lineno : usize ,
200+ ) -> Result < ( ) , W :: Error > {
179201 // TODO: use io.open() method instead, when available, according to https://github.com/python/cpython/blob/master/Python/traceback.c#L393
180202 // TODO: support different encodings
181203 let file = match File :: open ( filename) {
@@ -198,7 +220,10 @@ fn print_source_line<W: Write>(output: &mut W, filename: &str, lineno: usize) ->
198220}
199221
200222/// Print exception occurrence location from traceback element
201- fn write_traceback_entry < W : Write > ( output : & mut W , tb_entry : & PyTracebackRef ) -> io:: Result < ( ) > {
223+ fn write_traceback_entry < W : Write > (
224+ output : & mut W ,
225+ tb_entry : & PyTracebackRef ,
226+ ) -> Result < ( ) , W :: Error > {
202227 let filename = tb_entry. frame . code . source_path . to_owned ( ) ;
203228 writeln ! (
204229 output,
@@ -215,7 +240,7 @@ pub fn write_exception_inner<W: Write>(
215240 output : & mut W ,
216241 vm : & VirtualMachine ,
217242 exc : & PyBaseExceptionRef ,
218- ) -> io :: Result < ( ) > {
243+ ) -> Result < ( ) , W :: Error > {
219244 if let Some ( tb) = exc. traceback . read ( ) . clone ( ) {
220245 writeln ! ( output, "Traceback (most recent call last):" ) ?;
221246 for tb in tb. iter ( ) {
@@ -341,6 +366,14 @@ impl ExceptionCtor {
341366 }
342367}
343368
369+ pub fn split (
370+ exc : PyBaseExceptionRef ,
371+ vm : & VirtualMachine ,
372+ ) -> ( PyObjectRef , PyObjectRef , PyObjectRef ) {
373+ let tb = exc. traceback ( ) . map_or ( vm. get_none ( ) , |tb| tb. into_object ( ) ) ;
374+ ( exc. class ( ) . into_object ( ) , exc. into_object ( ) , tb)
375+ }
376+
344377/// Similar to PyErr_NormalizeException in CPython
345378pub fn normalize (
346379 exc_type : PyObjectRef ,
0 commit comments