@@ -1835,7 +1835,8 @@ _sharednsitem_has_value(_PyXI_namespace_item *item, int64_t *p_interpid)
1835
1835
}
1836
1836
1837
1837
static 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 )
1839
1840
{
1840
1841
assert (_sharednsitem_is_initialized (item ));
1841
1842
assert (item -> xidata == NULL );
@@ -1844,8 +1845,9 @@ _sharednsitem_set_value(_PyXI_namespace_item *item, PyObject *value)
1844
1845
return -1 ;
1845
1846
}
1846
1847
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
+ {
1849
1851
PyMem_RawFree (item -> xidata );
1850
1852
item -> xidata = NULL ;
1851
1853
// The caller may want to propagate PyExc_NotShareableError
@@ -1877,7 +1879,8 @@ _sharednsitem_clear(_PyXI_namespace_item *item)
1877
1879
}
1878
1880
1879
1881
static 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 )
1881
1884
{
1882
1885
assert (item -> name != NULL );
1883
1886
assert (item -> xidata == NULL );
@@ -1889,7 +1892,7 @@ _sharednsitem_copy_from_ns(struct _sharednsitem *item, PyObject *ns)
1889
1892
// When applied, this item will be set to the default (or fail).
1890
1893
return 0 ;
1891
1894
}
1892
- if (_sharednsitem_set_value (item , value ) < 0 ) {
1895
+ if (_sharednsitem_set_value (item , value , fallback ) < 0 ) {
1893
1896
return -1 ;
1894
1897
}
1895
1898
return 0 ;
@@ -2143,14 +2146,15 @@ _create_sharedns(PyObject *names)
2143
2146
static void _propagate_not_shareable_error (_PyXI_session * );
2144
2147
2145
2148
static 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 )
2147
2151
{
2148
2152
// All items are expected to be shareable.
2149
2153
assert (_sharedns_check_counts (ns ));
2150
2154
assert (ns -> numnames == ns -> maxitems );
2151
2155
assert (ns -> numvalues == 0 );
2152
2156
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 ) {
2154
2158
if (session != NULL ) {
2155
2159
_propagate_not_shareable_error (session );
2156
2160
}
@@ -2259,10 +2263,14 @@ struct xi_session {
2259
2263
// beginning of the session as a convenience.
2260
2264
PyObject * main_ns ;
2261
2265
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
+
2262
2271
struct xi_session_error error ;
2263
2272
};
2264
2273
2265
-
2266
2274
_PyXI_session *
2267
2275
_PyXI_NewSession (void )
2268
2276
{
@@ -2355,14 +2363,16 @@ _exit_session(_PyXI_session *session)
2355
2363
PyThreadState * tstate = session -> init_tstate ;
2356
2364
assert (tstate != NULL );
2357
2365
assert (PyThreadState_Get () == tstate );
2366
+ assert (!_PyErr_Occurred (tstate ));
2358
2367
2359
2368
// Release any of the entered interpreters resources.
2360
2369
Py_CLEAR (session -> main_ns );
2370
+ Py_CLEAR (session -> _preserved );
2361
2371
2362
2372
// Ensure this thread no longer owns __main__.
2363
2373
if (session -> running ) {
2364
2374
_PyInterpreterState_SetNotRunningMain (tstate -> interp );
2365
- assert (!PyErr_Occurred ( ));
2375
+ assert (!_PyErr_Occurred ( tstate ));
2366
2376
session -> running = 0 ;
2367
2377
}
2368
2378
@@ -2420,7 +2430,9 @@ _PyXI_Enter(_PyXI_session *session,
2420
2430
if (sharedns == NULL ) {
2421
2431
return -1 ;
2422
2432
}
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 ) {
2424
2436
assert (session -> error .info == NULL );
2425
2437
_destroy_sharedns (sharedns );
2426
2438
return -1 ;
@@ -2490,12 +2502,27 @@ _PyXI_Enter(_PyXI_session *session,
2490
2502
return -1 ;
2491
2503
}
2492
2504
2505
+ static PyObject * _capture_preserved (_PyXI_session * );
2506
+
2493
2507
int
2494
2508
_PyXI_Exit (_PyXI_session * session , _PyXI_session_result * result )
2495
2509
{
2496
2510
_capture_current_exception (session );
2497
2511
assert (!PyErr_Occurred ());
2498
2512
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
+
2499
2526
struct xi_session_error err ;
2500
2527
(void )_session_pop_error (session , & err );
2501
2528
_exit_session (session );
@@ -2639,6 +2666,104 @@ _PyXI_GetMainNamespace(_PyXI_session *session)
2639
2666
}
2640
2667
2641
2668
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
+
2642
2767
/*********************/
2643
2768
/* runtime lifecycle */
2644
2769
/*********************/
0 commit comments