Skip to content

Commit 61845dd

Browse files
Return an excinfo object from _interpreters.exec().
1 parent 362412d commit 61845dd

File tree

6 files changed

+51
-56
lines changed

6 files changed

+51
-56
lines changed

Lib/test/support/interpreters.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,11 @@ def run(self, src_str, /, channels=None):
110110
that time, the previous interpreter is allowed to run
111111
in other threads.
112112
"""
113-
_interpreters.exec(self._id, src_str, channels)
113+
err = _interpreters.exec(self._id, src_str, channels)
114+
if err is not None:
115+
exc = RunFailedError(err.formatted)
116+
exc.snapshot = err
117+
raise exc
114118

115119

116120
def create_channel():

Lib/test/test__xxinterpchannels.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,16 +1017,16 @@ def test_close_multiple_users(self):
10171017
_channels.recv({cid})
10181018
"""))
10191019
channels.close(cid)
1020-
with self.assertRaises(interpreters.RunFailedError) as cm:
1021-
interpreters.run_string(id1, dedent(f"""
1020+
1021+
excsnap = interpreters.run_string(id1, dedent(f"""
10221022
_channels.send({cid}, b'spam')
10231023
"""))
1024-
self.assertIn('ChannelClosedError', str(cm.exception))
1025-
with self.assertRaises(interpreters.RunFailedError) as cm:
1026-
interpreters.run_string(id2, dedent(f"""
1024+
self.assertEqual(excsnap.type.__name__, 'ChannelClosedError')
1025+
1026+
excsnap = interpreters.run_string(id2, dedent(f"""
10271027
_channels.send({cid}, b'spam')
10281028
"""))
1029-
self.assertIn('ChannelClosedError', str(cm.exception))
1029+
self.assertEqual(excsnap.type.__name__, 'ChannelClosedError')
10301030

10311031
def test_close_multiple_times(self):
10321032
cid = channels.create()

Lib/test/test__xxsubinterpreters.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -956,11 +956,12 @@ class NeverError(Exception): pass
956956
raise NeverError # never raised
957957
""").format(dedent(text))
958958
if fails:
959-
with self.assertRaises(excwrapper) as caught:
960-
interpreters.run_string(self.id, script)
961-
return caught.exception
959+
err = interpreters.run_string(self.id, script)
960+
self.assertIsNot(err, None)
961+
return err
962962
else:
963-
interpreters.run_string(self.id, script)
963+
err = interpreters.run_string(self.id, script)
964+
self.assertIs(err, None)
964965
return None
965966
except:
966967
raise # re-raise
@@ -982,11 +983,12 @@ def _assert_run_failed(self, exctype, msg, script):
982983
exc = self.run_script(script, fails=True)
983984

984985
# Check the wrapper exception.
986+
self.assertEqual(exc.type.__name__, exctype_name)
985987
if msg is None:
986-
self.assertEqual(str(exc).split(':')[0],
988+
self.assertEqual(exc.formatted.split(':')[0],
987989
exctype_name)
988990
else:
989-
self.assertEqual(str(exc),
991+
self.assertEqual(exc.formatted,
990992
'{}: {}'.format(exctype_name, msg))
991993

992994
return exc

Lib/test/test_import/__init__.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1968,10 +1968,12 @@ def test_disallowed_reimport(self):
19681968
print(_testsinglephase)
19691969
''')
19701970
interpid = _interpreters.create()
1971-
with self.assertRaises(_interpreters.RunFailedError):
1972-
_interpreters.run_string(interpid, script)
1973-
with self.assertRaises(_interpreters.RunFailedError):
1974-
_interpreters.run_string(interpid, script)
1971+
1972+
excsnap = _interpreters.run_string(interpid, script)
1973+
self.assertIsNot(excsnap, None)
1974+
1975+
excsnap = _interpreters.run_string(interpid, script)
1976+
self.assertIsNot(excsnap, None)
19751977

19761978

19771979
class TestSinglePhaseSnapshot(ModuleSnapshot):

Lib/test/test_importlib/test_util.py

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -655,25 +655,19 @@ def test_magic_number(self):
655655
@unittest.skipIf(_interpreters is None, 'subinterpreters required')
656656
class IncompatibleExtensionModuleRestrictionsTests(unittest.TestCase):
657657

658-
ERROR = re.compile("^ImportError: module (.*) does not support loading in subinterpreters")
659-
660658
def run_with_own_gil(self, script):
661659
interpid = _interpreters.create(isolated=True)
662-
try:
663-
_interpreters.run_string(interpid, script)
664-
except _interpreters.RunFailedError as exc:
665-
if m := self.ERROR.match(str(exc)):
666-
modname, = m.groups()
667-
raise ImportError(modname)
660+
excsnap = _interpreters.run_string(interpid, script)
661+
if excsnap is not None:
662+
if excsnap.type.__name__ == 'ImportError':
663+
raise ImportError(excsnap.msg)
668664

669665
def run_with_shared_gil(self, script):
670666
interpid = _interpreters.create(isolated=False)
671-
try:
672-
_interpreters.run_string(interpid, script)
673-
except _interpreters.RunFailedError as exc:
674-
if m := self.ERROR.match(str(exc)):
675-
modname, = m.groups()
676-
raise ImportError(modname)
667+
excsnap = _interpreters.run_string(interpid, script)
668+
if excsnap is not None:
669+
if excsnap.type.__name__ == 'ImportError':
670+
raise ImportError(excsnap.msg)
677671

678672
@unittest.skipIf(_testsinglephase is None, "test requires _testsinglephase module")
679673
def test_single_phase_init_module(self):

Modules/_xxsubinterpretersmodule.c

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -225,23 +225,11 @@ _run_script(PyObject *ns, const char *codestr, Py_ssize_t codestrlen, int flags)
225225
return 0;
226226
}
227227

228-
static void
229-
_raise_formatted(PyObject *exctype, PyObject *excinfo)
230-
{
231-
PyObject *formatted = PyObject_GetAttrString(excinfo, "formatted");
232-
if (formatted == NULL) {
233-
assert(PyErr_Occurred());
234-
return;
235-
}
236-
PyErr_SetObject(exctype, formatted);
237-
Py_DECREF(formatted);
238-
}
239-
240228
static int
241229
_run_in_interpreter(PyInterpreterState *interp,
242230
const char *codestr, Py_ssize_t codestrlen,
243231
PyObject *shareables, int flags,
244-
PyObject *excwrapper)
232+
PyObject **p_excinfo)
245233
{
246234
assert(!PyErr_Occurred());
247235
_PyXI_session session = {0};
@@ -251,7 +239,7 @@ _run_in_interpreter(PyInterpreterState *interp,
251239
assert(!PyErr_Occurred());
252240
PyObject *excinfo = _PyXI_ApplyError(session.error);
253241
if (excinfo != NULL) {
254-
_raise_formatted(excwrapper, excinfo);
242+
*p_excinfo = excinfo;
255243
}
256244
assert(PyErr_Occurred());
257245
return -1;
@@ -268,7 +256,7 @@ _run_in_interpreter(PyInterpreterState *interp,
268256
if (res < 0) {
269257
PyObject *excinfo = _PyXI_ApplyCapturedException(&session);
270258
if (excinfo != NULL) {
271-
_raise_formatted(excwrapper, excinfo);
259+
*p_excinfo = excinfo;
272260
}
273261
}
274262
else {
@@ -539,7 +527,8 @@ convert_code_arg(PyObject *arg, const char *fname, const char *displayname,
539527

540528
static int
541529
_interp_exec(PyObject *self,
542-
PyObject *id_arg, PyObject *code_arg, PyObject *shared_arg)
530+
PyObject *id_arg, PyObject *code_arg, PyObject *shared_arg,
531+
PyObject **p_excinfo)
543532
{
544533
// Look up the interpreter.
545534
PyInterpreterState *interp = PyInterpreterID_LookUp(id_arg);
@@ -558,10 +547,8 @@ _interp_exec(PyObject *self,
558547
}
559548

560549
// Run the code in the interpreter.
561-
module_state *state = get_module_state(self);
562-
assert(state != NULL);
563550
int res = _run_in_interpreter(interp, codestr, codestrlen,
564-
shared_arg, flags, state->RunFailedError);
551+
shared_arg, flags, p_excinfo);
565552
Py_XDECREF(bytes_obj);
566553
if (res < 0) {
567554
return -1;
@@ -595,10 +582,12 @@ interp_exec(PyObject *self, PyObject *args, PyObject *kwds)
595582
return NULL;
596583
}
597584

598-
int res = _interp_exec(self, id, code, shared);
585+
PyObject *excinfo = NULL;
586+
int res = _interp_exec(self, id, code, shared, &excinfo);
599587
Py_DECREF(code);
600588
if (res < 0) {
601-
return NULL;
589+
assert((excinfo == NULL) != (PyErr_Occurred() == NULL));
590+
return excinfo;
602591
}
603592
Py_RETURN_NONE;
604593
}
@@ -638,10 +627,12 @@ interp_run_string(PyObject *self, PyObject *args, PyObject *kwds)
638627
return NULL;
639628
}
640629

641-
int res = _interp_exec(self, id, script, shared);
630+
PyObject *excinfo = NULL;
631+
int res = _interp_exec(self, id, script, shared, &excinfo);
642632
Py_DECREF(script);
643633
if (res < 0) {
644-
return NULL;
634+
assert((excinfo == NULL) != (PyErr_Occurred() == NULL));
635+
return excinfo;
645636
}
646637
Py_RETURN_NONE;
647638
}
@@ -672,10 +663,12 @@ interp_run_func(PyObject *self, PyObject *args, PyObject *kwds)
672663
return NULL;
673664
}
674665

675-
int res = _interp_exec(self, id, (PyObject *)code, shared);
666+
PyObject *excinfo = NULL;
667+
int res = _interp_exec(self, id, (PyObject *)code, shared, &excinfo);
676668
Py_DECREF(code);
677669
if (res < 0) {
678-
return NULL;
670+
assert((excinfo == NULL) != (PyErr_Occurred() == NULL));
671+
return excinfo;
679672
}
680673
Py_RETURN_NONE;
681674
}

0 commit comments

Comments
 (0)