From 87460821755ba72294b0c95b7d130b922d2a7ea4 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 22 Jun 1999 14:47:32 +0000 Subject: Patch by Tim Peters: Introduce a new builtin exception, UnboundLocalError, raised when ceval.c tries to retrieve or delete a local name that isn't bound to a value. Currently raises NameError, which makes this behavior a FAQ since the same error is raised for "missing" global names too: when the user has a global of the same name as the unbound local, NameError makes no sense to them. Even in the absence of shadowing, knowing whether a bogus name is local or global is a real aid to quick understanding. Example: D:\src\PCbuild>type local.py x = 42 def f(): print x x = 13 return x f() D:\src\PCbuild>python local.py Traceback (innermost last): File "local.py", line 8, in ? f() File "local.py", line 4, in f print x UnboundLocalError: x D:\src\PCbuild> Note that UnboundLocalError is a subclass of NameError, for compatibility with existing class-exception code that may be trying to catch this as a NameError. Unfortunately, I see no way to make this wholly compatible with -X (see comments in bltinmodule.c): under -X, [UnboundLocalError is an alias for NameError --GvR]. [The ceval.c patch differs slightly from the second version that Tim submitted; I decided not to raise UnboundLocalError for DELETE_NAME, only for DELETE_LOCAL. DELETE_NAME is only generated at the module level, and since at that level a NameError is raised for referencing an undefined name, it should also be raised for deleting one.] --- Include/pyerrors.h | 1 + Lib/exceptions.py | 8 +++++++- Python/bltinmodule.c | 15 +++++++++++++++ Python/ceval.c | 4 ++-- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Include/pyerrors.h b/Include/pyerrors.h index 8a8111a..becaabd 100644 --- a/Include/pyerrors.h +++ b/Include/pyerrors.h @@ -78,6 +78,7 @@ extern DL_IMPORT(PyObject *) PyExc_SyntaxError; extern DL_IMPORT(PyObject *) PyExc_SystemError; extern DL_IMPORT(PyObject *) PyExc_SystemExit; extern DL_IMPORT(PyObject *) PyExc_TypeError; +extern DL_IMPORT(PyObject *) PyExc_UnboundLocalError; extern DL_IMPORT(PyObject *) PyExc_ValueError; extern DL_IMPORT(PyObject *) PyExc_ZeroDivisionError; diff --git a/Lib/exceptions.py b/Lib/exceptions.py index e943f7b..af752d9 100644 --- a/Lib/exceptions.py +++ b/Lib/exceptions.py @@ -40,6 +40,8 @@ Exception(*) | +-- NotImplementedError(*) | +-- NameError + | | + | +-- UnboundLocalError(*) +-- AttributeError +-- SyntaxError +-- TypeError @@ -208,7 +210,11 @@ class AttributeError(StandardError): pass class NameError(StandardError): - """Name not found locally or globally.""" + """Name not found globally.""" + pass + +class UnboundLocalError(NameError): + """Local name referenced but not bound to a value.""" pass class MemoryError(StandardError): diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 3ddf885..4e20eda 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2232,6 +2232,7 @@ PyObject *PyExc_NotImplementedError; PyObject *PyExc_SyntaxError; PyObject *PyExc_SystemError; PyObject *PyExc_SystemExit; +PyObject *PyExc_UnboundLocalError; PyObject *PyExc_TypeError; PyObject *PyExc_ValueError; PyObject *PyExc_ZeroDivisionError; @@ -2261,6 +2262,11 @@ bltin_exc[] = { {"KeyError", &PyExc_KeyError, 1}, {"KeyboardInterrupt", &PyExc_KeyboardInterrupt, 1}, {"MemoryError", &PyExc_MemoryError, 1}, + /* Note: NameError is not a leaf in exceptions.py, but unlike + the other non-leafs NameError is meant to be raised directly + at times -- the leaf_exc member really seems to mean something + like "this is an abstract base class" when false. + */ {"NameError", &PyExc_NameError, 1}, {"OverflowError", &PyExc_OverflowError, 1}, {"RuntimeError", &PyExc_RuntimeError, 1}, @@ -2268,6 +2274,7 @@ bltin_exc[] = { {"SyntaxError", &PyExc_SyntaxError, 1}, {"SystemError", &PyExc_SystemError, 1}, {"SystemExit", &PyExc_SystemExit, 1}, + {"UnboundLocalError", &PyExc_UnboundLocalError, 1}, {"TypeError", &PyExc_TypeError, 1}, {"ValueError", &PyExc_ValueError, 1}, {"ZeroDivisionError", &PyExc_ZeroDivisionError, 1}, @@ -2420,6 +2427,14 @@ initerrors(dict) PyTuple_SET_ITEM(PyExc_EnvironmentError, 1, PyExc_OSError); PyDict_SetItemString(dict, "EnvironmentError", PyExc_EnvironmentError); + /* Make UnboundLocalError an alias for NameError */ + Py_INCREF(PyExc_NameError); + Py_DECREF(PyExc_UnboundLocalError); + PyExc_UnboundLocalError = PyExc_NameError; + if (PyDict_SetItemString(dict, "UnboundLocalError", + PyExc_NameError) != 0) + Py_FatalError("Cannot create string-based exceptions"); + /* missing from the StandardError tuple: Exception, StandardError, * and SystemExit */ diff --git a/Python/ceval.c b/Python/ceval.c index 6218397..1c51ccf 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1320,7 +1320,7 @@ eval_code2(co, globals, locals, case LOAD_FAST: x = GETLOCAL(oparg); if (x == NULL) { - PyErr_SetObject(PyExc_NameError, + PyErr_SetObject(PyExc_UnboundLocalError, PyTuple_GetItem(co->co_varnames, oparg)); break; @@ -1338,7 +1338,7 @@ eval_code2(co, globals, locals, case DELETE_FAST: x = GETLOCAL(oparg); if (x == NULL) { - PyErr_SetObject(PyExc_NameError, + PyErr_SetObject(PyExc_UnboundLocalError, PyTuple_GetItem(co->co_varnames, oparg)); break; -- cgit v0.12