mirror of https://github.com/python/cpython
gh-76785: Raise InterpreterError, Not RuntimeError (gh-117489)
I had meant to switch everything to InterpreterError when I added it a while back. At the time I missed a few key spots. As part of this, I've added print-the-exception to _PyXI_InitTypes() and fixed an error case in `_PyStaticType_InitBuiltin().
This commit is contained in:
parent
7ecd55d604
commit
976bcb2379
|
@ -43,7 +43,7 @@ Uncaught in the interpreter:
|
||||||
{formatted}
|
{formatted}
|
||||||
""".strip()
|
""".strip()
|
||||||
|
|
||||||
class ExecutionFailed(RuntimeError):
|
class ExecutionFailed(InterpreterError):
|
||||||
"""An unhandled exception happened during execution.
|
"""An unhandled exception happened during execution.
|
||||||
|
|
||||||
This is raised from Interpreter.exec() and Interpreter.call().
|
This is raised from Interpreter.exec() and Interpreter.call().
|
||||||
|
@ -158,7 +158,7 @@ class Interpreter:
|
||||||
"""Finalize and destroy the interpreter.
|
"""Finalize and destroy the interpreter.
|
||||||
|
|
||||||
Attempting to destroy the current interpreter results
|
Attempting to destroy the current interpreter results
|
||||||
in a RuntimeError.
|
in an InterpreterError.
|
||||||
"""
|
"""
|
||||||
return _interpreters.destroy(self._id)
|
return _interpreters.destroy(self._id)
|
||||||
|
|
||||||
|
|
|
@ -80,7 +80,7 @@ def clean_up_interpreters():
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
interpreters.destroy(id)
|
interpreters.destroy(id)
|
||||||
except RuntimeError:
|
except interpreters.InterpreterError:
|
||||||
pass # already destroyed
|
pass # already destroyed
|
||||||
|
|
||||||
|
|
||||||
|
@ -464,11 +464,11 @@ class DestroyTests(TestBase):
|
||||||
|
|
||||||
def test_main(self):
|
def test_main(self):
|
||||||
main, = interpreters.list_all()
|
main, = interpreters.list_all()
|
||||||
with self.assertRaises(RuntimeError):
|
with self.assertRaises(interpreters.InterpreterError):
|
||||||
interpreters.destroy(main)
|
interpreters.destroy(main)
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
with self.assertRaises(RuntimeError):
|
with self.assertRaises(interpreters.InterpreterError):
|
||||||
interpreters.destroy(main)
|
interpreters.destroy(main)
|
||||||
|
|
||||||
t = threading.Thread(target=f)
|
t = threading.Thread(target=f)
|
||||||
|
@ -496,7 +496,7 @@ class DestroyTests(TestBase):
|
||||||
import _xxsubinterpreters as _interpreters
|
import _xxsubinterpreters as _interpreters
|
||||||
try:
|
try:
|
||||||
_interpreters.destroy({id})
|
_interpreters.destroy({id})
|
||||||
except RuntimeError:
|
except interpreters.InterpreterError:
|
||||||
pass
|
pass
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
@ -531,7 +531,7 @@ class DestroyTests(TestBase):
|
||||||
self.assertTrue(interpreters.is_running(interp),
|
self.assertTrue(interpreters.is_running(interp),
|
||||||
msg=f"Interp {interp} should be running before destruction.")
|
msg=f"Interp {interp} should be running before destruction.")
|
||||||
|
|
||||||
with self.assertRaises(RuntimeError,
|
with self.assertRaises(interpreters.InterpreterError,
|
||||||
msg=f"Should not be able to destroy interp {interp} while it's still running."):
|
msg=f"Should not be able to destroy interp {interp} while it's still running."):
|
||||||
interpreters.destroy(interp)
|
interpreters.destroy(interp)
|
||||||
self.assertTrue(interpreters.is_running(interp))
|
self.assertTrue(interpreters.is_running(interp))
|
||||||
|
@ -676,7 +676,7 @@ class RunStringTests(TestBase):
|
||||||
|
|
||||||
def test_already_running(self):
|
def test_already_running(self):
|
||||||
with _running(self.id):
|
with _running(self.id):
|
||||||
with self.assertRaises(RuntimeError):
|
with self.assertRaises(interpreters.InterpreterError):
|
||||||
interpreters.run_string(self.id, 'print("spam")')
|
interpreters.run_string(self.id, 'print("spam")')
|
||||||
|
|
||||||
def test_does_not_exist(self):
|
def test_does_not_exist(self):
|
||||||
|
|
|
@ -364,11 +364,11 @@ class TestInterpreterClose(TestBase):
|
||||||
|
|
||||||
def test_main(self):
|
def test_main(self):
|
||||||
main, = interpreters.list_all()
|
main, = interpreters.list_all()
|
||||||
with self.assertRaises(RuntimeError):
|
with self.assertRaises(interpreters.InterpreterError):
|
||||||
main.close()
|
main.close()
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
with self.assertRaises(RuntimeError):
|
with self.assertRaises(interpreters.InterpreterError):
|
||||||
main.close()
|
main.close()
|
||||||
|
|
||||||
t = threading.Thread(target=f)
|
t = threading.Thread(target=f)
|
||||||
|
@ -389,7 +389,7 @@ class TestInterpreterClose(TestBase):
|
||||||
interp = interpreters.Interpreter({interp.id})
|
interp = interpreters.Interpreter({interp.id})
|
||||||
try:
|
try:
|
||||||
interp.close()
|
interp.close()
|
||||||
except RuntimeError:
|
except interpreters.InterpreterError:
|
||||||
print('failed')
|
print('failed')
|
||||||
"""))
|
"""))
|
||||||
self.assertEqual(out.strip(), 'failed')
|
self.assertEqual(out.strip(), 'failed')
|
||||||
|
@ -424,7 +424,7 @@ class TestInterpreterClose(TestBase):
|
||||||
main, = interpreters.list_all()
|
main, = interpreters.list_all()
|
||||||
interp = interpreters.create()
|
interp = interpreters.create()
|
||||||
with _running(interp):
|
with _running(interp):
|
||||||
with self.assertRaises(RuntimeError):
|
with self.assertRaises(interpreters.InterpreterError):
|
||||||
interp.close()
|
interp.close()
|
||||||
self.assertTrue(interp.is_running())
|
self.assertTrue(interp.is_running())
|
||||||
|
|
||||||
|
@ -1103,7 +1103,7 @@ class LowLevelTests(TestBase):
|
||||||
self.assert_ns_equal(config, default)
|
self.assert_ns_equal(config, default)
|
||||||
|
|
||||||
with self.subTest('arg: \'empty\''):
|
with self.subTest('arg: \'empty\''):
|
||||||
with self.assertRaises(RuntimeError):
|
with self.assertRaises(interpreters.InterpreterError):
|
||||||
# The "empty" config isn't viable on its own.
|
# The "empty" config isn't viable on its own.
|
||||||
_interpreters.create('empty')
|
_interpreters.create('empty')
|
||||||
|
|
||||||
|
|
|
@ -612,7 +612,7 @@ interp_create(PyObject *self, PyObject *args, PyObject *kwds)
|
||||||
// XXX Move the chained exception to interpreters.create()?
|
// XXX Move the chained exception to interpreters.create()?
|
||||||
PyObject *exc = PyErr_GetRaisedException();
|
PyObject *exc = PyErr_GetRaisedException();
|
||||||
assert(exc != NULL);
|
assert(exc != NULL);
|
||||||
PyErr_SetString(PyExc_RuntimeError, "interpreter creation failed");
|
PyErr_SetString(PyExc_InterpreterError, "interpreter creation failed");
|
||||||
_PyErr_ChainExceptions1(exc);
|
_PyErr_ChainExceptions1(exc);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -664,7 +664,7 @@ interp_destroy(PyObject *self, PyObject *args, PyObject *kwds)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (interp == current) {
|
if (interp == current) {
|
||||||
PyErr_SetString(PyExc_RuntimeError,
|
PyErr_SetString(PyExc_InterpreterError,
|
||||||
"cannot destroy the current interpreter");
|
"cannot destroy the current interpreter");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -673,7 +673,7 @@ interp_destroy(PyObject *self, PyObject *args, PyObject *kwds)
|
||||||
/* XXX We *could* support destroying a running interpreter but
|
/* XXX We *could* support destroying a running interpreter but
|
||||||
aren't going to worry about it for now. */
|
aren't going to worry about it for now. */
|
||||||
if (is_running_main(interp)) {
|
if (is_running_main(interp)) {
|
||||||
PyErr_Format(PyExc_RuntimeError, "interpreter running");
|
PyErr_Format(PyExc_InterpreterError, "interpreter running");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -693,7 +693,7 @@ PyDoc_STRVAR(destroy_doc,
|
||||||
\n\
|
\n\
|
||||||
Destroy the identified interpreter.\n\
|
Destroy the identified interpreter.\n\
|
||||||
\n\
|
\n\
|
||||||
Attempting to destroy the current interpreter results in a RuntimeError.\n\
|
Attempting to destroy the current interpreter raises InterpreterError.\n\
|
||||||
So does an unrecognized ID.");
|
So does an unrecognized ID.");
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7963,6 +7963,7 @@ _PyStaticType_InitBuiltin(PyInterpreterState *interp, PyTypeObject *self)
|
||||||
res = type_ready(self, !ismain);
|
res = type_ready(self, !ismain);
|
||||||
END_TYPE_LOCK()
|
END_TYPE_LOCK()
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
|
_PyStaticType_ClearWeakRefs(interp, self);
|
||||||
static_builtin_state_clear(interp, self);
|
static_builtin_state_clear(interp, self);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -845,7 +845,7 @@ _PyXI_ApplyErrorCode(_PyXI_errcode code, PyInterpreterState *interp)
|
||||||
return 0;
|
return 0;
|
||||||
case _PyXI_ERR_OTHER:
|
case _PyXI_ERR_OTHER:
|
||||||
// XXX msg?
|
// XXX msg?
|
||||||
PyErr_SetNone(PyExc_RuntimeError);
|
PyErr_SetNone(PyExc_InterpreterError);
|
||||||
break;
|
break;
|
||||||
case _PyXI_ERR_NO_MEMORY:
|
case _PyXI_ERR_NO_MEMORY:
|
||||||
PyErr_NoMemory();
|
PyErr_NoMemory();
|
||||||
|
@ -856,11 +856,11 @@ _PyXI_ApplyErrorCode(_PyXI_errcode code, PyInterpreterState *interp)
|
||||||
_PyInterpreterState_FailIfRunningMain(interp);
|
_PyInterpreterState_FailIfRunningMain(interp);
|
||||||
break;
|
break;
|
||||||
case _PyXI_ERR_MAIN_NS_FAILURE:
|
case _PyXI_ERR_MAIN_NS_FAILURE:
|
||||||
PyErr_SetString(PyExc_RuntimeError,
|
PyErr_SetString(PyExc_InterpreterError,
|
||||||
"failed to get __main__ namespace");
|
"failed to get __main__ namespace");
|
||||||
break;
|
break;
|
||||||
case _PyXI_ERR_APPLY_NS_FAILURE:
|
case _PyXI_ERR_APPLY_NS_FAILURE:
|
||||||
PyErr_SetString(PyExc_RuntimeError,
|
PyErr_SetString(PyExc_InterpreterError,
|
||||||
"failed to apply namespace to __main__");
|
"failed to apply namespace to __main__");
|
||||||
break;
|
break;
|
||||||
case _PyXI_ERR_NOT_SHAREABLE:
|
case _PyXI_ERR_NOT_SHAREABLE:
|
||||||
|
@ -935,7 +935,7 @@ _PyXI_ApplyError(_PyXI_error *error)
|
||||||
if (error->uncaught.type.name != NULL || error->uncaught.msg != NULL) {
|
if (error->uncaught.type.name != NULL || error->uncaught.msg != NULL) {
|
||||||
// __context__ will be set to a proxy of the propagated exception.
|
// __context__ will be set to a proxy of the propagated exception.
|
||||||
PyObject *exc = PyErr_GetRaisedException();
|
PyObject *exc = PyErr_GetRaisedException();
|
||||||
_PyXI_excinfo_Apply(&error->uncaught, PyExc_RuntimeError);
|
_PyXI_excinfo_Apply(&error->uncaught, PyExc_InterpreterError);
|
||||||
PyObject *exc2 = PyErr_GetRaisedException();
|
PyObject *exc2 = PyErr_GetRaisedException();
|
||||||
PyException_SetContext(exc, exc2);
|
PyException_SetContext(exc, exc2);
|
||||||
PyErr_SetRaisedException(exc);
|
PyErr_SetRaisedException(exc);
|
||||||
|
@ -1671,6 +1671,7 @@ PyStatus
|
||||||
_PyXI_InitTypes(PyInterpreterState *interp)
|
_PyXI_InitTypes(PyInterpreterState *interp)
|
||||||
{
|
{
|
||||||
if (init_exceptions(interp) < 0) {
|
if (init_exceptions(interp) < 0) {
|
||||||
|
PyErr_PrintEx(0);
|
||||||
return _PyStatus_ERR("failed to initialize an exception type");
|
return _PyStatus_ERR("failed to initialize an exception type");
|
||||||
}
|
}
|
||||||
return _PyStatus_OK();
|
return _PyStatus_OK();
|
||||||
|
|
|
@ -5,6 +5,9 @@ static PyTypeObject _PyExc_InterpreterError = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0)
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
.tp_name = "interpreters.InterpreterError",
|
.tp_name = "interpreters.InterpreterError",
|
||||||
.tp_doc = PyDoc_STR("A cross-interpreter operation failed"),
|
.tp_doc = PyDoc_STR("A cross-interpreter operation failed"),
|
||||||
|
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
|
||||||
|
//.tp_traverse = ((PyTypeObject *)PyExc_BaseException)->tp_traverse,
|
||||||
|
//.tp_clear = ((PyTypeObject *)PyExc_BaseException)->tp_clear,
|
||||||
//.tp_base = (PyTypeObject *)PyExc_BaseException,
|
//.tp_base = (PyTypeObject *)PyExc_BaseException,
|
||||||
};
|
};
|
||||||
PyObject *PyExc_InterpreterError = (PyObject *)&_PyExc_InterpreterError;
|
PyObject *PyExc_InterpreterError = (PyObject *)&_PyExc_InterpreterError;
|
||||||
|
@ -15,6 +18,9 @@ static PyTypeObject _PyExc_InterpreterNotFoundError = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0)
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
.tp_name = "interpreters.InterpreterNotFoundError",
|
.tp_name = "interpreters.InterpreterNotFoundError",
|
||||||
.tp_doc = PyDoc_STR("An interpreter was not found"),
|
.tp_doc = PyDoc_STR("An interpreter was not found"),
|
||||||
|
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
|
||||||
|
//.tp_traverse = ((PyTypeObject *)PyExc_BaseException)->tp_traverse,
|
||||||
|
//.tp_clear = ((PyTypeObject *)PyExc_BaseException)->tp_clear,
|
||||||
.tp_base = &_PyExc_InterpreterError,
|
.tp_base = &_PyExc_InterpreterError,
|
||||||
};
|
};
|
||||||
PyObject *PyExc_InterpreterNotFoundError = (PyObject *)&_PyExc_InterpreterNotFoundError;
|
PyObject *PyExc_InterpreterNotFoundError = (PyObject *)&_PyExc_InterpreterNotFoundError;
|
||||||
|
@ -55,16 +61,25 @@ _get_not_shareable_error_type(PyInterpreterState *interp)
|
||||||
static int
|
static int
|
||||||
init_exceptions(PyInterpreterState *interp)
|
init_exceptions(PyInterpreterState *interp)
|
||||||
{
|
{
|
||||||
|
PyTypeObject *base = (PyTypeObject *)PyExc_BaseException;
|
||||||
|
|
||||||
// builtin static types
|
// builtin static types
|
||||||
_PyExc_InterpreterError.tp_base = (PyTypeObject *)PyExc_BaseException;
|
|
||||||
|
_PyExc_InterpreterError.tp_base = base;
|
||||||
|
_PyExc_InterpreterError.tp_traverse = base->tp_traverse;
|
||||||
|
_PyExc_InterpreterError.tp_clear = base->tp_clear;
|
||||||
if (_PyStaticType_InitBuiltin(interp, &_PyExc_InterpreterError) < 0) {
|
if (_PyStaticType_InitBuiltin(interp, &_PyExc_InterpreterError) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_PyExc_InterpreterNotFoundError.tp_traverse = base->tp_traverse;
|
||||||
|
_PyExc_InterpreterNotFoundError.tp_clear = base->tp_clear;
|
||||||
if (_PyStaticType_InitBuiltin(interp, &_PyExc_InterpreterNotFoundError) < 0) {
|
if (_PyStaticType_InitBuiltin(interp, &_PyExc_InterpreterNotFoundError) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// heap types
|
// heap types
|
||||||
|
|
||||||
// We would call _init_not_shareable_error_type() here too,
|
// We would call _init_not_shareable_error_type() here too,
|
||||||
// but that leads to ref leaks
|
// but that leads to ref leaks
|
||||||
|
|
||||||
|
|
|
@ -1073,7 +1073,7 @@ int
|
||||||
_PyInterpreterState_FailIfRunningMain(PyInterpreterState *interp)
|
_PyInterpreterState_FailIfRunningMain(PyInterpreterState *interp)
|
||||||
{
|
{
|
||||||
if (interp->threads.main != NULL) {
|
if (interp->threads.main != NULL) {
|
||||||
PyErr_SetString(PyExc_RuntimeError,
|
PyErr_SetString(PyExc_InterpreterError,
|
||||||
"interpreter already running");
|
"interpreter already running");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue