diff --git a/Include/pyerrors.h b/Include/pyerrors.h index 8a7e4f4..34d3202 100644 --- a/Include/pyerrors.h +++ b/Include/pyerrors.h @@ -36,6 +36,18 @@ typedef struct { typedef struct { PyException_HEAD + PyObject *msg; + PyObject *name; +} PyNameErrorObject; + +typedef struct { + PyException_HEAD + PyObject *msg; + PyObject *name; +} PyUnboundLocalErrorObject; + +typedef struct { + PyException_HEAD PyObject *encoding; PyObject *object; Py_ssize_t start; @@ -283,6 +295,8 @@ PyAPI_FUNC(PyObject *) PyErr_SetExcWithArgsKwargs(PyObject *, PyObject *, PyObject *); PyAPI_FUNC(PyObject *) PyErr_SetImportError(PyObject *, PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) PyErr_SetNameError(PyObject *msg, PyObject *name); +PyAPI_FUNC(PyObject *) PyErr_SetUnboundLocalError(PyObject *msg, PyObject *name); /* Export the old function so that the existing API remains available: */ PyAPI_FUNC(void) PyErr_BadInternalCall(void); diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index b35a5e4..fb0294a 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -76,8 +76,18 @@ class ExceptionTests(unittest.TestCase): self.raise_catch(NameError, "NameError") try: x = undefined_variable - except NameError: pass - + except NameError as nameError: + self.assertEqual(nameError.name, "undefined_variable") + + self.raise_catch(UnboundLocalError, "UnboundLocalError") + try: + unboundLocalVar = 0 + def unboundLocalRaise(): + unboundLocalVar += 1 + unboundLocalRaise() + except UnboundLocalError as unboundLocalError: + self.assertEqual(unboundLocalError.name, "unboundLocalVar") + self.raise_catch(OverflowError, "OverflowError") x = 1 for dummy in range(128): diff --git a/Objects/exceptions.c b/Objects/exceptions.c index e09d384..8671ba3 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1234,13 +1234,160 @@ SimpleExtendsException(PyExc_RuntimeError, NotImplementedError, /* * NameError extends Exception */ -SimpleExtendsException(PyExc_Exception, NameError, +static int +NameError_init(PyNameErrorObject *self, PyObject *args, PyObject *kwds) +{ + PyObject *msg = NULL; + PyObject *name = NULL; + const char *obj_str = NULL; + static const char* NAME_ERROR_MSG = + "name '%.200s' is not defined"; + +/* Macro replacement doesn't allow ## to start the first line of a macro, + so we move the assignment and NULL check into the if-statement. */ +#define GET_KWD(kwd) { \ + kwd = PyDict_GetItemString(kwds, #kwd); \ + if (kwd) { \ + Py_CLEAR(self->kwd); \ + self->kwd = kwd; \ + Py_INCREF(self->kwd);\ + if (PyDict_DelItemString(kwds, #kwd)) \ + return -1; \ + } \ + } + + if (kwds) { + GET_KWD(name); + } + + if (BaseException_init((PyBaseExceptionObject *)self, args, kwds) == -1) + return -1; + if (PyTuple_GET_SIZE(args) != 1) { + if (name) { + obj_str = _PyUnicode_AsString(name); + if (!obj_str) + return 0; + + PyErr_Format(msg, NAME_ERROR_MSG, obj_str); + } else { + return 0; + } + } else if (!PyArg_UnpackTuple(args, "NameError", 1, 1, &msg)) + return -1; + + Py_CLEAR(self->msg); /* replacing */ + self->msg = msg; + Py_INCREF(self->msg); + + return 0; +} + +static int +NameError_clear(PyNameErrorObject *self) +{ + Py_CLEAR(self->msg); + Py_CLEAR(self->name); + return BaseException_clear((PyBaseExceptionObject *)self); +} + +static void +NameError_dealloc(PyNameErrorObject *self) +{ + _PyObject_GC_UNTRACK(self); + NameError_clear(self); + Py_TYPE(self)->tp_free((PyObject *)self); +} + +static int +NameError_traverse(PyNameErrorObject *self, visitproc visit, void *arg) +{ + Py_VISIT(self->msg); + Py_VISIT(self->name); + return BaseException_traverse((PyBaseExceptionObject *)self, visit, arg); +} + +static PyObject * +NameError_str(PyNameErrorObject *self) +{ + if (self->msg && PyUnicode_CheckExact(self->msg)) { + Py_INCREF(self->msg); + return self->msg; + } + else { + return BaseException_str((PyBaseExceptionObject *)self); + } +} + +static PyMemberDef NameError_members[] = { + {"msg", T_OBJECT, offsetof(PyNameErrorObject, msg), 0, + PyDoc_STR("exception message")}, + {"name", T_OBJECT, offsetof(PyNameErrorObject, name), 0, + PyDoc_STR("variable name")}, + {NULL} /* Sentinel */ +}; + +static PyMethodDef NameError_methods[] = { + {NULL} +}; + +ComplexExtendsException(PyExc_Exception, NameError, + NameError, 0 /* new */, + NameError_methods, NameError_members, + 0 /* getset */, NameError_str, "Name not found globally."); /* * UnboundLocalError extends NameError */ -SimpleExtendsException(PyExc_NameError, UnboundLocalError, +static int +UnboundLocalError_init(PyUnboundLocalErrorObject *self, PyObject *args, PyObject *kwds) +{ + NameError_init((PyNameErrorObject *)self, args, kwds); + return 0; +} + +static int +UnboundLocalError_clear(PyUnboundLocalErrorObject *self) +{ + return NameError_clear((PyNameErrorObject *)self); +} + +static void +UnboundLocalError_dealloc(PyUnboundLocalErrorObject *self) +{ + _PyObject_GC_UNTRACK(self); + UnboundLocalError_clear(self); + Py_TYPE(self)->tp_free((PyObject *)self); +} + +static int +UnboundLocalError_traverse(PyUnboundLocalErrorObject *self, visitproc visit, void *arg) +{ + return NameError_traverse((PyNameErrorObject *)self, visit, arg); +} + +static PyObject * +UnboundLocalError_str(PyUnboundLocalErrorObject *self) +{ + return NameError_str((PyNameErrorObject *)self); +} + +static PyMemberDef UnboundLocalError_members[] = { + {"msg", T_OBJECT, offsetof(PyUnboundLocalErrorObject, msg), 0, + PyDoc_STR("exception message")}, + {"name", T_OBJECT, offsetof(PyUnboundLocalErrorObject, name), 0, + PyDoc_STR("variable name")}, + {NULL} /* Sentinel */ +}; + +static PyMethodDef UnboundLocalError_methods[] = { + {NULL} +}; + +ComplexExtendsException(PyExc_NameError, UnboundLocalError, + UnboundLocalError, 0 /* new */, + UnboundLocalError_methods, UnboundLocalError_members, + 0 /* getset */, UnboundLocalError_str, "Local name referenced but not bound to a value."); /* diff --git a/Python/ceval.c b/Python/ceval.c index 25fbc0f..4a9a427 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -139,18 +139,17 @@ static int maybe_call_line_trace(Py_tracefunc, PyObject *, static PyObject * cmp_outcome(int, PyObject *, PyObject *); static PyObject * import_from(PyObject *, PyObject *); static int import_all_from(PyObject *, PyObject *); -static void format_exc_check_arg(PyObject *, const char *, PyObject *); static void format_exc_unbound(PyCodeObject *co, int oparg); static PyObject * unicode_concatenate(PyObject *, PyObject *, PyFrameObject *, unsigned char *); static PyObject * special_lookup(PyObject *, _Py_Identifier *); #define NAME_ERROR_MSG \ - "name '%.200s' is not defined" + "name '%.200S' is not defined" #define UNBOUNDLOCAL_ERROR_MSG \ - "local variable '%.200s' referenced before assignment" + "local variable '%.200S' referenced before assignment" #define UNBOUNDFREE_ERROR_MSG \ - "free variable '%.200s' referenced before assignment" \ + "free variable '%.200S' referenced before assignment" \ " in enclosing scope" /* Dynamic execution profile */ @@ -1364,10 +1363,19 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) TARGET(LOAD_FAST) { PyObject *value = GETLOCAL(oparg); + PyObject *name, *errorMsg; if (value == NULL) { - format_exc_check_arg(PyExc_UnboundLocalError, - UNBOUNDLOCAL_ERROR_MSG, - PyTuple_GetItem(co->co_varnames, oparg)); + name = PyTuple_GetItem(co->co_varnames, oparg); + if (name) + errorMsg = PyUnicode_FromFormat(UNBOUNDLOCAL_ERROR_MSG, + name); + else + errorMsg = NULL; + + PyErr_SetUnboundLocalError( + errorMsg, + name); + goto error; } Py_INCREF(value); @@ -2026,12 +2034,13 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) TARGET(LOAD_BUILD_CLASS) { _Py_IDENTIFIER(__build_class__); - PyObject *bc; + PyObject *bc, *errorMsg; if (PyDict_CheckExact(f->f_builtins)) { bc = _PyDict_GetItemId(f->f_builtins, &PyId___build_class__); if (bc == NULL) { - PyErr_SetString(PyExc_NameError, - "__build_class__ not found"); + errorMsg = PyUnicode_FromString("__build_class__ not found"); + PyErr_SetNameError(errorMsg, + Py_None); goto error; } Py_INCREF(bc); @@ -2042,9 +2051,11 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) break; bc = PyObject_GetItem(f->f_builtins, build_class_str); if (bc == NULL) { - if (PyErr_ExceptionMatches(PyExc_KeyError)) - PyErr_SetString(PyExc_NameError, - "__build_class__ not found"); + if (PyErr_ExceptionMatches(PyExc_KeyError)) { + errorMsg = PyUnicode_FromString("__build_class__ not found"); + PyErr_SetNameError(errorMsg, + Py_None); + } goto error; } } @@ -2076,6 +2087,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) TARGET(DELETE_NAME) { PyObject *name = GETITEM(names, oparg); PyObject *ns = f->f_locals; + PyObject *errorMsg; int err; if (ns == NULL) { PyErr_Format(PyExc_SystemError, @@ -2084,9 +2096,10 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) } err = PyObject_DelItem(ns, name); if (err != 0) { - format_exc_check_arg(PyExc_NameError, - NAME_ERROR_MSG, - name); + errorMsg = PyUnicode_FromFormat(NAME_ERROR_MSG, + name); + PyErr_SetNameError(errorMsg, + name); goto error; } DISPATCH(); @@ -2176,11 +2189,14 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) TARGET(DELETE_GLOBAL) { PyObject *name = GETITEM(names, oparg); + PyObject *errorMsg; int err; err = PyDict_DelItem(f->f_globals, name); if (err != 0) { - format_exc_check_arg( - PyExc_NameError, NAME_ERROR_MSG, name); + errorMsg = PyUnicode_FromFormat(NAME_ERROR_MSG, + name); + PyErr_SetNameError(errorMsg, + name); goto error; } DISPATCH(); @@ -2190,6 +2206,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) PyObject *name = GETITEM(names, oparg); PyObject *locals = f->f_locals; PyObject *v; + PyObject *errorMsg; if (locals == NULL) { PyErr_Format(PyExc_SystemError, "no locals when loading %R", name); @@ -2214,9 +2231,10 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) if (PyDict_CheckExact(f->f_builtins)) { v = PyDict_GetItem(f->f_builtins, name); if (v == NULL) { - format_exc_check_arg( - PyExc_NameError, - NAME_ERROR_MSG, name); + errorMsg = PyUnicode_FromFormat(NAME_ERROR_MSG, + name); + PyErr_SetNameError(errorMsg, + name); goto error; } Py_INCREF(v); @@ -2224,10 +2242,12 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) else { v = PyObject_GetItem(f->f_builtins, name); if (v == NULL) { - if (PyErr_ExceptionMatches(PyExc_KeyError)) - format_exc_check_arg( - PyExc_NameError, - NAME_ERROR_MSG, name); + if (PyErr_ExceptionMatches(PyExc_KeyError)) { + errorMsg = PyUnicode_FromFormat(NAME_ERROR_MSG, + name); + PyErr_SetNameError(errorMsg, + name); + } goto error; } } @@ -2240,15 +2260,19 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) TARGET(LOAD_GLOBAL) { PyObject *name = GETITEM(names, oparg); PyObject *v; + PyObject *errorMsg; if (PyDict_CheckExact(f->f_globals) && PyDict_CheckExact(f->f_builtins)) { v = _PyDict_LoadGlobal((PyDictObject *)f->f_globals, (PyDictObject *)f->f_builtins, name); if (v == NULL) { - if (!_PyErr_OCCURRED()) - format_exc_check_arg(PyExc_NameError, - NAME_ERROR_MSG, name); + if (!_PyErr_OCCURRED()) { + errorMsg = PyUnicode_FromFormat(NAME_ERROR_MSG, + name); + PyErr_SetNameError(errorMsg, + name); + } goto error; } Py_INCREF(v); @@ -2259,10 +2283,12 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) if (v == NULL) { v = PyObject_GetItem(f->f_builtins, name); if (v == NULL) { - if (PyErr_ExceptionMatches(PyExc_KeyError)) - format_exc_check_arg( - PyExc_NameError, - NAME_ERROR_MSG, name); + if (PyErr_ExceptionMatches(PyExc_KeyError)) { + errorMsg = PyUnicode_FromFormat(NAME_ERROR_MSG, + name); + PyErr_SetNameError(errorMsg, + name); + } goto error; } } @@ -2273,15 +2299,22 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) TARGET(DELETE_FAST) { PyObject *v = GETLOCAL(oparg); + PyObject *name, *errorMsg; if (v != NULL) { SETLOCAL(oparg, NULL); DISPATCH(); } - format_exc_check_arg( - PyExc_UnboundLocalError, - UNBOUNDLOCAL_ERROR_MSG, - PyTuple_GetItem(co->co_varnames, oparg) - ); + + name = PyTuple_GetItem(co->co_varnames, oparg); + if (name) + errorMsg = PyUnicode_FromFormat(UNBOUNDLOCAL_ERROR_MSG, + name); + else + errorMsg = NULL; + + PyErr_SetUnboundLocalError( + errorMsg, + name); goto error; } @@ -4784,39 +4817,27 @@ import_all_from(PyObject *locals, PyObject *v) } static void -format_exc_check_arg(PyObject *exc, const char *format_str, PyObject *obj) -{ - const char *obj_str; - - if (!obj) - return; - - obj_str = _PyUnicode_AsString(obj); - if (!obj_str) - return; - - PyErr_Format(exc, format_str, obj_str); -} - -static void format_exc_unbound(PyCodeObject *co, int oparg) { - PyObject *name; + PyObject *name, *errorMsg; /* Don't stomp existing exception */ if (PyErr_Occurred()) return; if (oparg < PyTuple_GET_SIZE(co->co_cellvars)) { name = PyTuple_GET_ITEM(co->co_cellvars, oparg); - format_exc_check_arg( - PyExc_UnboundLocalError, - UNBOUNDLOCAL_ERROR_MSG, - name); + + errorMsg = PyUnicode_FromFormat(UNBOUNDLOCAL_ERROR_MSG, + name); + PyErr_SetUnboundLocalError(errorMsg, + name); } else { name = PyTuple_GET_ITEM(co->co_freevars, oparg - PyTuple_GET_SIZE(co->co_cellvars)); - format_exc_check_arg(PyExc_NameError, - UNBOUNDFREE_ERROR_MSG, name); + errorMsg = PyUnicode_FromFormat(UNBOUNDFREE_ERROR_MSG, + name); + PyErr_SetNameError(errorMsg, + name); } } diff --git a/Python/errors.c b/Python/errors.c index 1d64efd..cbdf112 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -752,6 +752,76 @@ PyErr_SetImportError(PyObject *msg, PyObject *name, PyObject *path) return NULL; } +PyObject * +PyErr_SetNameError(PyObject *msg, PyObject *name) +{ + PyObject *args = NULL, *kwargs = NULL, *error = NULL; + + if (msg) + args = PyTuple_New(1); + + if (name == NULL) + name = Py_None; + + kwargs = PyDict_New(); + if (kwargs) { + if (msg) { + Py_INCREF(msg); + PyTuple_SET_ITEM(args, 0, msg); + } + + /* Ignoring failure here */ + PyDict_SetItemString(kwargs, "name", name); + + error = PyObject_Call(PyExc_NameError, args, kwargs); + if (error) { + PyErr_SetObject((PyObject *)Py_TYPE(error), error); + Py_DECREF(error); + } + } + + Py_XDECREF(msg); + Py_XDECREF(args); + Py_XDECREF(kwargs); + + return NULL; +} + +PyObject * +PyErr_SetUnboundLocalError(PyObject *msg, PyObject *name) +{ + PyObject *args = NULL, *kwargs = NULL, *error = NULL; + + if (msg) + args = PyTuple_New(1); + + if (name == NULL) + name = Py_None; + + kwargs = PyDict_New(); + if (kwargs) { + if (msg) { + Py_INCREF(msg); + PyTuple_SET_ITEM(args, 0, msg); + } + + /* Ignoring failure here */ + PyDict_SetItemString(kwargs, "name", name); + + error = PyObject_Call(PyExc_UnboundLocalError, args, kwargs); + if (error) { + PyErr_SetObject((PyObject *)Py_TYPE(error), error); + Py_DECREF(error); + } + } + + Py_XDECREF(msg); + Py_XDECREF(args); + Py_XDECREF(kwargs); + + return NULL; +} + void _PyErr_BadInternalCall(const char *filename, int lineno) {