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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy