@@ -604,6 +604,29 @@ impl PyErr {
604604 }
605605 }
606606
607+ /// Return the context (either an exception instance, or None, set by an implicit exception
608+ /// during handling of another exception) associated with the exception, as accessible from
609+ /// Python through `__context__`.
610+ pub fn context ( & self , py : Python < ' _ > ) -> Option < PyErr > {
611+ unsafe {
612+ ffi:: PyException_GetContext ( self . value ( py) . as_ptr ( ) )
613+ . assume_owned_or_opt ( py)
614+ . map ( Self :: from_value)
615+ }
616+ }
617+
618+ /// Set the context associated with the exception, pass `None` to clear it.
619+ pub fn set_context ( & self , py : Python < ' _ > , context : Option < PyErr > ) {
620+ let value = self . value ( py) ;
621+ let context = context. map ( |err| err. into_value ( py) ) ;
622+ unsafe {
623+ ffi:: PyException_SetContext (
624+ value. as_ptr ( ) ,
625+ context. map_or ( std:: ptr:: null_mut ( ) , Py :: into_ptr) ,
626+ ) ;
627+ }
628+ }
629+
607630 /// Equivalent to calling `add_note` on the exception in Python.
608631 #[ cfg( Py_3_11 ) ]
609632 pub fn add_note < N : for < ' py > IntoPyObject < ' py , Target = PyString > > (
@@ -1027,4 +1050,19 @@ mod tests {
10271050 ) ;
10281051 } ) ;
10291052 }
1053+
1054+ #[ test]
1055+ fn test_set_context ( ) {
1056+ Python :: attach ( |py| {
1057+ let err = PyErr :: new :: < PyValueError , _ > ( "original error" ) ;
1058+ assert ! ( err. context( py) . is_none( ) ) ;
1059+
1060+ let context = PyErr :: new :: < PyTypeError , _ > ( "context error" ) ;
1061+ err. set_context ( py, Some ( context) ) ;
1062+ assert ! ( err. context( py) . unwrap( ) . is_instance_of:: <PyTypeError >( py) ) ;
1063+
1064+ err. set_context ( py, None ) ;
1065+ assert ! ( err. context( py) . is_none( ) ) ;
1066+ } )
1067+ }
10301068}
0 commit comments