Skip to content

Commit 1ad1002

Browse files
Make _PyXI_session opaque.
1 parent 9fed16e commit 1ad1002

File tree

3 files changed

+454
-365
lines changed

3 files changed

+454
-365
lines changed

Include/internal/pycore_crossinterp.h

+8-50
Original file line numberDiff line numberDiff line change
@@ -335,71 +335,29 @@ typedef struct _sharedexception {
335335
PyAPI_FUNC(PyObject *) _PyXI_ApplyError(_PyXI_error *err);
336336

337337

338-
typedef struct xi_session _PyXI_session;
339-
typedef struct _sharedns _PyXI_namespace;
340-
341-
PyAPI_FUNC(void) _PyXI_FreeNamespace(_PyXI_namespace *ns);
342-
PyAPI_FUNC(_PyXI_namespace *) _PyXI_NamespaceFromNames(PyObject *names);
343-
PyAPI_FUNC(int) _PyXI_FillNamespaceFromDict(
344-
_PyXI_namespace *ns,
345-
PyObject *nsobj,
346-
_PyXI_session *session);
347-
PyAPI_FUNC(int) _PyXI_ApplyNamespace(
348-
_PyXI_namespace *ns,
349-
PyObject *nsobj,
350-
PyObject *dflt);
351-
352-
353338
// A cross-interpreter session involves entering an interpreter
354-
// (_PyXI_Enter()), doing some work with it, and finally exiting
355-
// that interpreter (_PyXI_Exit()).
339+
// with _PyXI_Enter(), doing some work with it, and finally exiting
340+
// that interpreter with _PyXI_Exit().
356341
//
357342
// At the boundaries of the session, both entering and exiting,
358343
// data may be exchanged between the previous interpreter and the
359344
// target one in a thread-safe way that does not violate the
360345
// isolation between interpreters. This includes setting objects
361346
// in the target's __main__ module on the way in, and capturing
362347
// uncaught exceptions on the way out.
363-
struct xi_session {
364-
// Once a session has been entered, this is the tstate that was
365-
// current before the session. If it is different from cur_tstate
366-
// then we must have switched interpreters. Either way, this will
367-
// be the current tstate once we exit the session.
368-
PyThreadState *prev_tstate;
369-
// Once a session has been entered, this is the current tstate.
370-
// It must be current when the session exits.
371-
PyThreadState *init_tstate;
372-
// This is true if init_tstate needs cleanup during exit.
373-
int own_init_tstate;
374-
375-
// This is true if, while entering the session, init_thread took
376-
// "ownership" of the interpreter's __main__ module. This means
377-
// it is the only thread that is allowed to run code there.
378-
// (Caveat: for now, users may still run exec() against the
379-
// __main__ module's dict, though that isn't advisable.)
380-
int running;
381-
// This is a cached reference to the __dict__ of the entered
382-
// interpreter's __main__ module. It is looked up when at the
383-
// beginning of the session as a convenience.
384-
PyObject *main_ns;
385-
386-
// This is set if the interpreter is entered and raised an exception
387-
// that needs to be handled in some special way during exit.
388-
_PyXI_errcode *error_override;
389-
// This is set if exit captured an exception to propagate.
390-
_PyXI_error *error;
391-
392-
// -- pre-allocated memory --
393-
_PyXI_error _error;
394-
_PyXI_errcode _error_override;
395-
};
348+
typedef struct xi_session _PyXI_session;
349+
350+
PyAPI_FUNC(_PyXI_session *) _PyXI_NewSession(void);
351+
PyAPI_FUNC(void) _PyXI_FreeSession(_PyXI_session *);
396352

397353
PyAPI_FUNC(int) _PyXI_Enter(
398354
_PyXI_session *session,
399355
PyInterpreterState *interp,
400356
PyObject *nsupdates);
401357
PyAPI_FUNC(void) _PyXI_Exit(_PyXI_session *session);
402358

359+
PyAPI_FUNC(PyObject *) _PyXI_GetMainNamespace(_PyXI_session *);
360+
403361
PyAPI_FUNC(PyObject *) _PyXI_ApplyCapturedException(_PyXI_session *session);
404362
PyAPI_FUNC(int) _PyXI_HasCapturedException(_PyXI_session *session);
405363

Modules/_interpretersmodule.c

+28-12
Original file line numberDiff line numberDiff line change
@@ -444,42 +444,53 @@ _exec_in_interpreter(PyThreadState *tstate, PyInterpreterState *interp,
444444
PyObject **p_excinfo)
445445
{
446446
assert(!_PyErr_Occurred(tstate));
447-
_PyXI_session session = {0};
447+
_PyXI_session *session = _PyXI_NewSession();
448+
if (session == NULL) {
449+
return -1;
450+
}
448451

449452
// Prep and switch interpreters.
450-
if (_PyXI_Enter(&session, interp, shareables) < 0) {
453+
if (_PyXI_Enter(session, interp, shareables) < 0) {
451454
if (_PyErr_Occurred(tstate)) {
452455
// If an error occured at this step, it means that interp
453456
// was not prepared and switched.
457+
_PyXI_FreeSession(session);
454458
return -1;
455459
}
456460
// Now, apply the error from another interpreter:
457-
PyObject *excinfo = _PyXI_ApplyError(session.error);
461+
PyObject *excinfo = _PyXI_ApplyCapturedException(session);
458462
if (excinfo != NULL) {
459463
*p_excinfo = excinfo;
460464
}
461465
assert(PyErr_Occurred());
466+
_PyXI_FreeSession(session);
462467
return -1;
463468
}
464469

465470
// Run the script.
466-
int res = _run_script(script, session.main_ns);
471+
PyObject *mainns = _PyXI_GetMainNamespace(session);
472+
if (mainns == NULL) {
473+
goto finally;
474+
}
475+
int res = _run_script(script, mainns);
467476

477+
finally:
468478
// Clean up and switch back.
469-
_PyXI_Exit(&session);
479+
_PyXI_Exit(session);
470480

471481
// Propagate any exception out to the caller.
472482
assert(!PyErr_Occurred());
473483
if (res < 0) {
474-
PyObject *excinfo = _PyXI_ApplyCapturedException(&session);
484+
PyObject *excinfo = _PyXI_ApplyCapturedException(session);
475485
if (excinfo != NULL) {
476486
*p_excinfo = excinfo;
477487
}
478488
}
479489
else {
480-
assert(!_PyXI_HasCapturedException(&session));
490+
assert(!_PyXI_HasCapturedException(session));
481491
}
482492

493+
_PyXI_FreeSession(session);
483494
return res;
484495
}
485496

@@ -824,22 +835,27 @@ interp_set___main___attrs(PyObject *self, PyObject *args, PyObject *kwargs)
824835
}
825836
}
826837

827-
_PyXI_session session = {0};
838+
_PyXI_session *session = _PyXI_NewSession();
839+
if (session == NULL) {
840+
return NULL;
841+
}
828842

829843
// Prep and switch interpreters, including apply the updates.
830-
if (_PyXI_Enter(&session, interp, updates) < 0) {
844+
if (_PyXI_Enter(session, interp, updates) < 0) {
831845
if (!PyErr_Occurred()) {
832-
_PyXI_ApplyCapturedException(&session);
846+
_PyXI_ApplyCapturedException(session);
833847
assert(PyErr_Occurred());
834848
}
835849
else {
836-
assert(!_PyXI_HasCapturedException(&session));
850+
assert(!_PyXI_HasCapturedException(session));
837851
}
852+
_PyXI_FreeSession(session);
838853
return NULL;
839854
}
840855

841856
// Clean up and switch back.
842-
_PyXI_Exit(&session);
857+
_PyXI_Exit(session);
858+
_PyXI_FreeSession(session);
843859

844860
Py_RETURN_NONE;
845861
}

0 commit comments

Comments
 (0)