Skip to content

Commit 5d7141d

Browse files
Improve handling of xidata-to-object for scripts and calls.
1 parent a9612e3 commit 5d7141d

File tree

1 file changed

+97
-26
lines changed

1 file changed

+97
-26
lines changed

Modules/_interpretersmodule.c

Lines changed: 97 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -512,60 +512,78 @@ _interp_call_pack(PyThreadState *tstate, struct interp_call *call,
512512
}
513513

514514
static int
515-
_make_call(struct interp_call *call, PyObject **p_result)
515+
_interp_call_unpack(struct interp_call *call,
516+
PyObject **p_func, PyObject **p_args, PyObject **p_kwargs)
516517
{
517-
assert(call != NULL && call->func != NULL);
518-
int res = -1;
519-
PyObject *args = NULL;
520-
PyObject *kwargs = NULL;
521-
PyObject *resobj = NULL;
522518
// Unpack the func.
523519
PyObject *func = _PyXIData_NewObject(call->func);
524520
if (func == NULL) {
525521
return -1;
526522
}
527523
// Unpack the args.
524+
PyObject *args;
528525
if (call->args == NULL) {
529526
args = PyTuple_New(0);
530527
if (args == NULL) {
531-
goto finally;
528+
Py_DECREF(func);
529+
return -1;
532530
}
533531
}
534532
else {
535533
args = _PyXIData_NewObject(call->args);
536534
if (args == NULL) {
537-
goto finally;
535+
Py_DECREF(func);
536+
return -1;
538537
}
539538
assert(PyTuple_Check(args));
540539
}
541540
// Unpack the kwargs.
541+
PyObject *kwargs = NULL;
542542
if (call->kwargs != NULL) {
543543
kwargs = _PyXIData_NewObject(call->kwargs);
544544
if (kwargs == NULL) {
545-
goto finally;
545+
Py_DECREF(func);
546+
Py_DECREF(args);
547+
return -1;
546548
}
547549
assert(PyDict_Check(kwargs));
548550
}
549-
// Make the call.
550-
resobj = PyObject_Call(func, args, kwargs);
551-
if (resobj == NULL) {
552-
goto finally;
551+
*p_func = func;
552+
*p_args = args;
553+
*p_kwargs = kwargs;
554+
return 0;
555+
}
556+
557+
static int
558+
_make_call(struct interp_call *call, PyObject **p_result, int *p_badtarget)
559+
{
560+
assert(call != NULL && call->func != NULL);
561+
562+
PyObject *func, *args, *kwargs;
563+
if (_interp_call_unpack(call, &func, &args, &kwargs) < 0) {
564+
*p_badtarget = 1;
565+
return -1;
553566
}
554-
*p_result = resobj;
555-
res = 0;
567+
*p_badtarget = 0;
556568

557-
finally:
569+
// Make the call.
570+
PyObject *resobj = PyObject_Call(func, args, kwargs);
558571
Py_DECREF(func);
559572
Py_XDECREF(args);
560573
Py_XDECREF(kwargs);
561-
return res;
574+
if (resobj == NULL) {
575+
return -1;
576+
}
577+
*p_result = resobj;
578+
return 0;
562579
}
563580

564581
static int
565-
_run_script(_PyXIData_t *script, PyObject *ns)
582+
_run_script(_PyXIData_t *script, PyObject *ns, int *p_badtarget)
566583
{
567584
PyObject *code = _PyXIData_NewObject(script);
568585
if (code == NULL) {
586+
*p_badtarget = 1;
569587
return -1;
570588
}
571589
PyObject *result = PyEval_EvalCode(code, ns, ns);
@@ -581,6 +599,7 @@ _run_script(_PyXIData_t *script, PyObject *ns)
581599
struct run_result {
582600
PyObject *result;
583601
PyObject *excinfo;
602+
int badtarget;
584603
};
585604

586605
static void
@@ -601,6 +620,7 @@ _run_in_interpreter(PyThreadState *tstate, PyInterpreterState *interp,
601620
return -1;
602621
}
603622
_PyXI_session_result result = {0};
623+
int badtarget = 0;
604624

605625
// Prep and switch interpreters.
606626
if (_PyXI_Enter(session, interp, shareables, &result) < 0) {
@@ -617,12 +637,12 @@ _run_in_interpreter(PyThreadState *tstate, PyInterpreterState *interp,
617637
if (mainns == NULL) {
618638
goto finally;
619639
}
620-
res = _run_script(script, mainns);
640+
res = _run_script(script, mainns, &badtarget);
621641
}
622642
else {
623643
assert(call != NULL);
624644
PyObject *resobj;
625-
res = _make_call(call, &resobj);
645+
res = _make_call(call, &resobj, &badtarget);
626646
if (res == 0) {
627647
(void)_PyXI_Preserve(session, "resobj", resobj);
628648
Py_DECREF(resobj);
@@ -635,9 +655,11 @@ _run_in_interpreter(PyThreadState *tstate, PyInterpreterState *interp,
635655
_PyXI_FreeSession(session);
636656
if (res < 0) {
637657
runres->excinfo = result.excinfo;
658+
runres->badtarget = badtarget;
638659
}
639660
else if (result.excinfo != NULL) {
640661
runres->excinfo = result.excinfo;
662+
runres->badtarget = badtarget;
641663
res = -1;
642664
}
643665
else {
@@ -1042,6 +1064,31 @@ unwrap_not_shareable(PyThreadState *tstate)
10421064
_PyErr_SetRaisedException(tstate, exc);
10431065
}
10441066

1067+
static PyObject *
1068+
_handle_script_error(_PyXIData_t *script, struct run_result *runres)
1069+
{
1070+
if (runres->excinfo == NULL) {
1071+
assert(PyErr_Occurred());
1072+
assert(!runres->badtarget);
1073+
return NULL;
1074+
}
1075+
assert(!PyErr_Occurred());
1076+
if (!runres->badtarget) {
1077+
return runres->excinfo;
1078+
}
1079+
PyObject *code = _PyXIData_NewObject(script);
1080+
if (code != NULL) {
1081+
// Either the problem is intermittent or only affects subinterpreters.
1082+
// This is highly unlikely.
1083+
return runres->excinfo;
1084+
}
1085+
assert(PyErr_Occurred());
1086+
PyThreadState *tstate = _PyThreadState_GET();
1087+
unwrap_not_shareable(tstate);
1088+
Py_DECREF(runres->excinfo);
1089+
return NULL;
1090+
}
1091+
10451092
static PyObject *
10461093
interp_exec(PyObject *self, PyObject *args, PyObject *kwds)
10471094
{
@@ -1080,7 +1127,7 @@ interp_exec(PyObject *self, PyObject *args, PyObject *kwds)
10801127
_PyXIData_Release(&xidata);
10811128
if (res < 0) {
10821129
assert((runres.excinfo == NULL) != (PyErr_Occurred() == NULL));
1083-
return runres.excinfo;
1130+
return _handle_script_error(&xidata, &runres);
10841131
}
10851132
Py_RETURN_NONE;
10861133
#undef FUNCNAME
@@ -1144,7 +1191,7 @@ interp_run_string(PyObject *self, PyObject *args, PyObject *kwds)
11441191
_PyXIData_Release(&xidata);
11451192
if (res < 0) {
11461193
assert((runres.excinfo == NULL) != (PyErr_Occurred() == NULL));
1147-
return runres.excinfo;
1194+
return _handle_script_error(&xidata, &runres);
11481195
}
11491196
Py_RETURN_NONE;
11501197
#undef FUNCNAME
@@ -1207,7 +1254,7 @@ interp_run_func(PyObject *self, PyObject *args, PyObject *kwds)
12071254
_PyXIData_Release(&xidata);
12081255
if (res < 0) {
12091256
assert((runres.excinfo == NULL) != (PyErr_Occurred() == NULL));
1210-
return runres.excinfo;
1257+
return _handle_script_error(&xidata, &runres);
12111258
}
12121259
Py_RETURN_NONE;
12131260
#undef FUNCNAME
@@ -1222,6 +1269,32 @@ are not supported. Methods and other callables are not supported either.\n\
12221269
\n\
12231270
(See " MODULE_NAME_STR ".exec().");
12241271

1272+
static int
1273+
handle_call_error(struct interp_call *call, struct run_result *runres)
1274+
{
1275+
if (runres->excinfo == NULL) {
1276+
assert(PyErr_Occurred());
1277+
assert(!runres->badtarget);
1278+
return -1;
1279+
}
1280+
assert(!PyErr_Occurred());
1281+
if (!runres->badtarget) {
1282+
return 0;
1283+
}
1284+
1285+
PyObject *func, *args, *kwargs;
1286+
if (_interp_call_unpack(call, &func, &args, &kwargs) == 0) {
1287+
// Either the problem is intermittent or only affects subinterpreters.
1288+
// This is highly unlikely.
1289+
return 0;
1290+
}
1291+
assert(PyErr_Occurred());
1292+
PyThreadState *tstate = _PyThreadState_GET();
1293+
unwrap_not_shareable(tstate);
1294+
Py_CLEAR(runres->excinfo);
1295+
return -1;
1296+
}
1297+
12251298
static PyObject *
12261299
interp_call(PyObject *self, PyObject *args, PyObject *kwds)
12271300
{
@@ -1259,11 +1332,9 @@ interp_call(PyObject *self, PyObject *args, PyObject *kwds)
12591332
PyObject *res_and_exc = NULL;
12601333
struct run_result runres = {0};
12611334
if (_run_in_interpreter(tstate, interp, NULL, &call, NULL, &runres) < 0) {
1262-
if (runres.excinfo == NULL) {
1263-
assert(_PyErr_Occurred(tstate));
1335+
if (handle_call_error(&call, &runres) < 0) {
12641336
goto finally;
12651337
}
1266-
assert(!_PyErr_Occurred(tstate));
12671338
}
12681339
assert(runres.result == NULL || runres.excinfo == NULL);
12691340
res_and_exc = Py_BuildValue("OO",

0 commit comments

Comments
 (0)