@@ -1835,7 +1835,8 @@ _sharednsitem_has_value(_PyXI_namespace_item *item, int64_t *p_interpid)
18351835}
18361836
18371837static int
1838- _sharednsitem_set_value (_PyXI_namespace_item * item , PyObject * value )
1838+ _sharednsitem_set_value (_PyXI_namespace_item * item , PyObject * value ,
1839+ xidata_fallback_t fallback )
18391840{
18401841 assert (_sharednsitem_is_initialized (item ));
18411842 assert (item -> xidata == NULL );
@@ -1844,8 +1845,9 @@ _sharednsitem_set_value(_PyXI_namespace_item *item, PyObject *value)
18441845 return -1 ;
18451846 }
18461847 PyThreadState * tstate = PyThreadState_Get ();
1847- // XXX Use _PyObject_GetXIDataWithFallback()?
1848- if (_PyObject_GetXIData (tstate , value , item -> xidata ) != 0 ) {
1848+ if (_PyObject_GetXIDataWithFallback (
1849+ tstate , value , fallback , item -> xidata ) < 0 )
1850+ {
18491851 PyMem_RawFree (item -> xidata );
18501852 item -> xidata = NULL ;
18511853 // The caller may want to propagate PyExc_NotShareableError
@@ -1877,7 +1879,8 @@ _sharednsitem_clear(_PyXI_namespace_item *item)
18771879}
18781880
18791881static int
1880- _sharednsitem_copy_from_ns (struct _sharednsitem * item , PyObject * ns )
1882+ _sharednsitem_copy_from_ns (struct _sharednsitem * item , PyObject * ns ,
1883+ xidata_fallback_t fallback )
18811884{
18821885 assert (item -> name != NULL );
18831886 assert (item -> xidata == NULL );
@@ -1889,7 +1892,7 @@ _sharednsitem_copy_from_ns(struct _sharednsitem *item, PyObject *ns)
18891892 // When applied, this item will be set to the default (or fail).
18901893 return 0 ;
18911894 }
1892- if (_sharednsitem_set_value (item , value ) < 0 ) {
1895+ if (_sharednsitem_set_value (item , value , fallback ) < 0 ) {
18931896 return -1 ;
18941897 }
18951898 return 0 ;
@@ -2143,14 +2146,15 @@ _create_sharedns(PyObject *names)
21432146static void _propagate_not_shareable_error (_PyXI_session * );
21442147
21452148static int
2146- _fill_sharedns (_PyXI_namespace * ns , PyObject * nsobj , _PyXI_session * session )
2149+ _fill_sharedns (_PyXI_namespace * ns , PyObject * nsobj ,
2150+ xidata_fallback_t fallback , _PyXI_session * session )
21472151{
21482152 // All items are expected to be shareable.
21492153 assert (_sharedns_check_counts (ns ));
21502154 assert (ns -> numnames == ns -> maxitems );
21512155 assert (ns -> numvalues == 0 );
21522156 for (Py_ssize_t i = 0 ; i < ns -> maxitems ; i ++ ) {
2153- if (_sharednsitem_copy_from_ns (& ns -> items [i ], nsobj ) < 0 ) {
2157+ if (_sharednsitem_copy_from_ns (& ns -> items [i ], nsobj , fallback ) < 0 ) {
21542158 if (session != NULL ) {
21552159 _propagate_not_shareable_error (session );
21562160 }
@@ -2259,10 +2263,14 @@ struct xi_session {
22592263 // beginning of the session as a convenience.
22602264 PyObject * main_ns ;
22612265
2266+ // This is a dict of objects that will be available (via sharing)
2267+ // once the session exits. Do not access this directly; use
2268+ // _PyXI_Preserve() and _PyXI_GetPreserved() instead;
2269+ PyObject * _preserved ;
2270+
22622271 struct xi_session_error error ;
22632272};
22642273
2265-
22662274_PyXI_session *
22672275_PyXI_NewSession (void )
22682276{
@@ -2355,14 +2363,16 @@ _exit_session(_PyXI_session *session)
23552363 PyThreadState * tstate = session -> init_tstate ;
23562364 assert (tstate != NULL );
23572365 assert (PyThreadState_Get () == tstate );
2366+ assert (!_PyErr_Occurred (tstate ));
23582367
23592368 // Release any of the entered interpreters resources.
23602369 Py_CLEAR (session -> main_ns );
2370+ Py_CLEAR (session -> _preserved );
23612371
23622372 // Ensure this thread no longer owns __main__.
23632373 if (session -> running ) {
23642374 _PyInterpreterState_SetNotRunningMain (tstate -> interp );
2365- assert (!PyErr_Occurred ( ));
2375+ assert (!_PyErr_Occurred ( tstate ));
23662376 session -> running = 0 ;
23672377 }
23682378
@@ -2420,7 +2430,9 @@ _PyXI_Enter(_PyXI_session *session,
24202430 if (sharedns == NULL ) {
24212431 return -1 ;
24222432 }
2423- if (_fill_sharedns (sharedns , nsupdates , NULL ) < 0 ) {
2433+ // For now we limit it to shareable objects.
2434+ xidata_fallback_t fallback = _PyXIDATA_XIDATA_ONLY ;
2435+ if (_fill_sharedns (sharedns , nsupdates , fallback , NULL ) < 0 ) {
24242436 assert (session -> error .info == NULL );
24252437 _destroy_sharedns (sharedns );
24262438 return -1 ;
@@ -2490,12 +2502,27 @@ _PyXI_Enter(_PyXI_session *session,
24902502 return -1 ;
24912503}
24922504
2505+ static PyObject * _capture_preserved (_PyXI_session * );
2506+
24932507int
24942508_PyXI_Exit (_PyXI_session * session , _PyXI_session_result * result )
24952509{
24962510 _capture_current_exception (session );
24972511 assert (!PyErr_Occurred ());
24982512
2513+ if (result != NULL ) {
2514+ result -> preserved = _capture_preserved (session );
2515+ if (result -> preserved == NULL && PyErr_Occurred ()) {
2516+ if (session -> error .info != NULL ) {
2517+ PyErr_FormatUnraisable (
2518+ "Exception ignored while capturing preserved objects" );
2519+ }
2520+ else {
2521+ _capture_current_exception (session );
2522+ }
2523+ }
2524+ }
2525+
24992526 struct xi_session_error err ;
25002527 (void )_session_pop_error (session , & err );
25012528 _exit_session (session );
@@ -2639,6 +2666,104 @@ _PyXI_GetMainNamespace(_PyXI_session *session)
26392666}
26402667
26412668
2669+ static PyObject *
2670+ _capture_preserved (_PyXI_session * session )
2671+ {
2672+ assert (_PyThreadState_GET () == session -> init_tstate ); // active session
2673+ if (session -> init_tstate == session -> prev_tstate ) {
2674+ // didn't switch
2675+ return Py_XNewRef (session -> _preserved );
2676+ }
2677+
2678+ _PyXI_namespace * preserved = NULL ;
2679+ if (session -> _preserved != NULL ) {
2680+ Py_ssize_t len = PyDict_Size (session -> _preserved );
2681+ if (len < 0 ) {
2682+ return NULL ;
2683+ }
2684+ if (len > 0 ) {
2685+ preserved = _create_sharedns (session -> _preserved );
2686+ if (preserved == NULL ) {
2687+ return NULL ;
2688+ }
2689+ if (_fill_sharedns (preserved , session -> _preserved ,
2690+ _PyXIDATA_FULL_FALLBACK , NULL ) < 0 )
2691+ {
2692+ assert (session -> error .info == NULL );
2693+ _destroy_sharedns (preserved );
2694+ return NULL ;
2695+ }
2696+ }
2697+ Py_CLEAR (session -> _preserved );
2698+ }
2699+ if (preserved == NULL ) {
2700+ return NULL ;
2701+ }
2702+
2703+ // We need to switch back to the original interpreter long enough
2704+ // to restore the preservd objects.
2705+ (void )PyThreadState_Swap (session -> prev_tstate );
2706+
2707+ PyObject * ns = PyDict_New ();
2708+ if (ns == NULL ) {
2709+ goto finally ;
2710+ }
2711+ if (_apply_sharedns (preserved , ns , NULL ) < 0 ) {
2712+ Py_CLEAR (ns );
2713+ goto finally ;
2714+ }
2715+
2716+ finally :
2717+ // Swap back into the session.
2718+ (void )PyThreadState_Swap (session -> init_tstate );
2719+ assert (preserved != NULL );
2720+ _destroy_sharedns (preserved );
2721+ return ns ;
2722+ }
2723+
2724+ static void
2725+ set_exc_with_cause (PyObject * exctype , const char * msg )
2726+ {
2727+ PyObject * cause = PyErr_GetRaisedException ();
2728+ PyErr_SetString (exctype , msg );
2729+ PyObject * exc = PyErr_GetRaisedException ();
2730+ PyException_SetCause (exc , cause );
2731+ PyErr_SetRaisedException (exc );
2732+ }
2733+
2734+ int
2735+ _PyXI_Preserve (_PyXI_session * session , const char * name , PyObject * value )
2736+ {
2737+ if (session -> init_tstate == NULL ) {
2738+ PyErr_SetString (PyExc_RuntimeError , "session not active" );
2739+ return -1 ;
2740+ }
2741+ if (session -> _preserved == NULL ) {
2742+ session -> _preserved = PyDict_New ();
2743+ if (session -> _preserved == NULL ) {
2744+ set_exc_with_cause (PyExc_RuntimeError ,
2745+ "failed to initialize preserved objects" );
2746+ return -1 ;
2747+ }
2748+ }
2749+ if (PyDict_SetItemString (session -> _preserved , name , value ) < 0 ) {
2750+ set_exc_with_cause (PyExc_RuntimeError , "failed to preserve object" );
2751+ return -1 ;
2752+ }
2753+ return 0 ;
2754+ }
2755+
2756+ PyObject *
2757+ _PyXI_GetPreserved (_PyXI_session_result * result , const char * name )
2758+ {
2759+ PyObject * value = NULL ;
2760+ if (result -> preserved != NULL ) {
2761+ (void )PyDict_GetItemStringRef (result -> preserved , name , & value );
2762+ }
2763+ return value ;
2764+ }
2765+
2766+
26422767/*********************/
26432768/* runtime lifecycle */
26442769/*********************/
0 commit comments