@@ -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