@@ -1830,7 +1830,8 @@ _sharednsitem_has_value(_PyXI_namespace_item *item, int64_t *p_interpid)
18301830}
18311831
18321832static  int 
1833- _sharednsitem_set_value (_PyXI_namespace_item  * item , PyObject  * value )
1833+ _sharednsitem_set_value (_PyXI_namespace_item  * item , PyObject  * value ,
1834+                         xidata_fallback_t  fallback )
18341835{
18351836    assert (_sharednsitem_is_initialized (item ));
18361837    assert (item -> xidata  ==  NULL );
@@ -1839,8 +1840,7 @@ _sharednsitem_set_value(_PyXI_namespace_item *item, PyObject *value)
18391840        return  -1 ;
18401841    }
18411842    PyThreadState  * tstate  =  PyThreadState_Get ();
1842-     // XXX Use _PyObject_GetXIDataWithFallback()? 
1843-     if  (_PyObject_GetXIDataNoFallback (tstate , value , item -> xidata ) !=  0 ) {
1843+     if  (_PyObject_GetXIData (tstate , value , fallback , item -> xidata ) <  0 ) {
18441844        PyMem_RawFree (item -> xidata );
18451845        item -> xidata  =  NULL ;
18461846        // The caller may want to propagate PyExc_NotShareableError 
@@ -1872,7 +1872,8 @@ _sharednsitem_clear(_PyXI_namespace_item *item)
18721872}
18731873
18741874static  int 
1875- _sharednsitem_copy_from_ns (struct  _sharednsitem  * item , PyObject  * ns )
1875+ _sharednsitem_copy_from_ns (struct  _sharednsitem  * item , PyObject  * ns ,
1876+                            xidata_fallback_t  fallback )
18761877{
18771878    assert (item -> name  !=  NULL );
18781879    assert (item -> xidata  ==  NULL );
@@ -1884,7 +1885,7 @@ _sharednsitem_copy_from_ns(struct _sharednsitem *item, PyObject *ns)
18841885        // When applied, this item will be set to the default (or fail). 
18851886        return  0 ;
18861887    }
1887-     if  (_sharednsitem_set_value (item , value ) <  0 ) {
1888+     if  (_sharednsitem_set_value (item , value ,  fallback ) <  0 ) {
18881889        return  -1 ;
18891890    }
18901891    return  0 ;
@@ -2138,14 +2139,15 @@ _create_sharedns(PyObject *names)
21382139static  void  _propagate_not_shareable_error (_PyXI_session  * );
21392140
21402141static  int 
2141- _fill_sharedns (_PyXI_namespace  * ns , PyObject  * nsobj , _PyXI_session  * session )
2142+ _fill_sharedns (_PyXI_namespace  * ns , PyObject  * nsobj ,
2143+                xidata_fallback_t  fallback , _PyXI_session  * session )
21422144{
21432145    // All items are expected to be shareable. 
21442146    assert (_sharedns_check_counts (ns ));
21452147    assert (ns -> numnames  ==  ns -> maxitems );
21462148    assert (ns -> numvalues  ==  0 );
21472149    for  (Py_ssize_t  i = 0 ; i  <  ns -> maxitems ; i ++ ) {
2148-         if  (_sharednsitem_copy_from_ns (& ns -> items [i ], nsobj ) <  0 ) {
2150+         if  (_sharednsitem_copy_from_ns (& ns -> items [i ], nsobj ,  fallback ) <  0 ) {
21492151            if  (session  !=  NULL ) {
21502152                _propagate_not_shareable_error (session );
21512153            }
@@ -2254,10 +2256,14 @@ struct xi_session {
22542256    // beginning of the session as a convenience. 
22552257    PyObject  * main_ns ;
22562258
2259+     // This is a dict of objects that will be available (via sharing) 
2260+     // once the session exits.  Do not access this directly; use 
2261+     // _PyXI_Preserve() and _PyXI_GetPreserved() instead; 
2262+     PyObject  * _preserved ;
2263+ 
22572264    struct  xi_session_error  error ;
22582265};
22592266
2260- 
22612267_PyXI_session  * 
22622268_PyXI_NewSession (void )
22632269{
@@ -2350,14 +2356,16 @@ _exit_session(_PyXI_session *session)
23502356    PyThreadState  * tstate  =  session -> init_tstate ;
23512357    assert (tstate  !=  NULL );
23522358    assert (PyThreadState_Get () ==  tstate );
2359+     assert (!_PyErr_Occurred (tstate ));
23532360
23542361    // Release any of the entered interpreters resources. 
23552362    Py_CLEAR (session -> main_ns );
2363+     Py_CLEAR (session -> _preserved );
23562364
23572365    // Ensure this thread no longer owns __main__. 
23582366    if  (session -> running ) {
23592367        _PyInterpreterState_SetNotRunningMain (tstate -> interp );
2360-         assert (!PyErr_Occurred ( ));
2368+         assert (!_PyErr_Occurred ( tstate ));
23612369        session -> running  =  0 ;
23622370    }
23632371
@@ -2415,7 +2423,9 @@ _PyXI_Enter(_PyXI_session *session,
24152423            if  (sharedns  ==  NULL ) {
24162424                return  -1 ;
24172425            }
2418-             if  (_fill_sharedns (sharedns , nsupdates , NULL ) <  0 ) {
2426+             // For now we limit it to shareable objects. 
2427+             xidata_fallback_t  fallback  =  _PyXIDATA_XIDATA_ONLY ;
2428+             if  (_fill_sharedns (sharedns , nsupdates , fallback , NULL ) <  0 ) {
24192429                assert (session -> error .info  ==  NULL );
24202430                _destroy_sharedns (sharedns );
24212431                return  -1 ;
@@ -2485,12 +2495,27 @@ _PyXI_Enter(_PyXI_session *session,
24852495    return  -1 ;
24862496}
24872497
2498+ static  PyObject  *  _capture_preserved (_PyXI_session  * );
2499+ 
24882500int 
24892501_PyXI_Exit (_PyXI_session  * session , _PyXI_session_result  * result )
24902502{
24912503    _capture_current_exception (session );
24922504    assert (!PyErr_Occurred ());
24932505
2506+     if  (result  !=  NULL ) {
2507+         result -> preserved  =  _capture_preserved (session );
2508+         if  (result -> preserved  ==  NULL  &&  PyErr_Occurred ()) {
2509+             if  (session -> error .info  !=  NULL ) {
2510+                 PyErr_FormatUnraisable (
2511+                     "Exception ignored while capturing preserved objects" );
2512+             }
2513+             else  {
2514+                 _capture_current_exception (session );
2515+             }
2516+         }
2517+     }
2518+ 
24942519    struct  xi_session_error  err ;
24952520    (void )_session_pop_error (session , & err );
24962521    _exit_session (session );
@@ -2634,6 +2659,104 @@ _PyXI_GetMainNamespace(_PyXI_session *session)
26342659}
26352660
26362661
2662+ static  PyObject  * 
2663+ _capture_preserved (_PyXI_session  * session )
2664+ {
2665+     assert (_PyThreadState_GET () ==  session -> init_tstate );  // active session 
2666+     if  (session -> init_tstate  ==  session -> prev_tstate ) {
2667+         // didn't switch 
2668+         return  Py_XNewRef (session -> _preserved );
2669+     }
2670+ 
2671+     _PyXI_namespace  * preserved  =  NULL ;
2672+     if  (session -> _preserved  !=  NULL ) {
2673+         Py_ssize_t  len  =  PyDict_Size (session -> _preserved );
2674+         if  (len  <  0 ) {
2675+             return  NULL ;
2676+         }
2677+         if  (len  >  0 ) {
2678+             preserved  =  _create_sharedns (session -> _preserved );
2679+             if  (preserved  ==  NULL ) {
2680+                 return  NULL ;
2681+             }
2682+             if  (_fill_sharedns (preserved , session -> _preserved ,
2683+                                _PyXIDATA_FULL_FALLBACK , NULL ) <  0 )
2684+             {
2685+                 assert (session -> error .info  ==  NULL );
2686+                 _destroy_sharedns (preserved );
2687+                 return  NULL ;
2688+             }
2689+         }
2690+         Py_CLEAR (session -> _preserved );
2691+     }
2692+     if  (preserved  ==  NULL ) {
2693+         return  NULL ;
2694+     }
2695+ 
2696+     // We need to switch back to the original interpreter long enough 
2697+     // to restore the preservd objects. 
2698+     (void )PyThreadState_Swap (session -> prev_tstate );
2699+ 
2700+     PyObject  * ns  =  PyDict_New ();
2701+     if  (ns  ==  NULL ) {
2702+         goto finally ;
2703+     }
2704+     if  (_apply_sharedns (preserved , ns , NULL ) <  0 ) {
2705+         Py_CLEAR (ns );
2706+         goto finally ;
2707+     }
2708+ 
2709+ finally :
2710+     // Swap back into the session. 
2711+     (void )PyThreadState_Swap (session -> init_tstate );
2712+     assert (preserved  !=  NULL );
2713+     _destroy_sharedns (preserved );
2714+     return  ns ;
2715+ }
2716+ 
2717+ static  void 
2718+ set_exc_with_cause (PyObject  * exctype , const  char  * msg )
2719+ {
2720+     PyObject  * cause  =  PyErr_GetRaisedException ();
2721+     PyErr_SetString (exctype , msg );
2722+     PyObject  * exc  =  PyErr_GetRaisedException ();
2723+     PyException_SetCause (exc , cause );
2724+     PyErr_SetRaisedException (exc );
2725+ }
2726+ 
2727+ int 
2728+ _PyXI_Preserve (_PyXI_session  * session , const  char  * name , PyObject  * value )
2729+ {
2730+     if  (session -> init_tstate  ==  NULL ) {
2731+         PyErr_SetString (PyExc_RuntimeError , "session not active" );
2732+         return  -1 ;
2733+     }
2734+     if  (session -> _preserved  ==  NULL ) {
2735+         session -> _preserved  =  PyDict_New ();
2736+         if  (session -> _preserved  ==  NULL ) {
2737+             set_exc_with_cause (PyExc_RuntimeError ,
2738+                                "failed to initialize preserved objects" );
2739+             return  -1 ;
2740+         }
2741+     }
2742+     if  (PyDict_SetItemString (session -> _preserved , name , value ) <  0 ) {
2743+         set_exc_with_cause (PyExc_RuntimeError , "failed to preserve object" );
2744+         return  -1 ;
2745+     }
2746+     return  0 ;
2747+ }
2748+ 
2749+ PyObject  * 
2750+ _PyXI_GetPreserved (_PyXI_session_result  * result , const  char  * name )
2751+ {
2752+     PyObject  * value  =  NULL ;
2753+     if  (result -> preserved  !=  NULL ) {
2754+         (void )PyDict_GetItemStringRef (result -> preserved , name , & value );
2755+     }
2756+     return  value ;
2757+ }
2758+ 
2759+ 
26372760/*********************/ 
26382761/* runtime lifecycle */ 
26392762/*********************/ 
0 commit comments