Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8746082

Browse files
committed
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.]
1 parent 4312890 commit 8746082

4 files changed

Lines changed: 25 additions & 3 deletions

File tree

Include/pyerrors.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ extern DL_IMPORT(PyObject *) PyExc_SyntaxError;
7878
extern DL_IMPORT(PyObject *) PyExc_SystemError;
7979
extern DL_IMPORT(PyObject *) PyExc_SystemExit;
8080
extern DL_IMPORT(PyObject *) PyExc_TypeError;
81+
extern DL_IMPORT(PyObject *) PyExc_UnboundLocalError;
8182
extern DL_IMPORT(PyObject *) PyExc_ValueError;
8283
extern DL_IMPORT(PyObject *) PyExc_ZeroDivisionError;
8384

Lib/exceptions.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
| +-- NotImplementedError(*)
4141
|
4242
+-- NameError
43+
| |
44+
| +-- UnboundLocalError(*)
4345
+-- AttributeError
4446
+-- SyntaxError
4547
+-- TypeError
@@ -208,7 +210,11 @@ class AttributeError(StandardError):
208210
pass
209211

210212
class NameError(StandardError):
211-
"""Name not found locally or globally."""
213+
"""Name not found globally."""
214+
pass
215+
216+
class UnboundLocalError(NameError):
217+
"""Local name referenced but not bound to a value."""
212218
pass
213219

214220
class MemoryError(StandardError):

Python/bltinmodule.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2232,6 +2232,7 @@ PyObject *PyExc_NotImplementedError;
22322232
PyObject *PyExc_SyntaxError;
22332233
PyObject *PyExc_SystemError;
22342234
PyObject *PyExc_SystemExit;
2235+
PyObject *PyExc_UnboundLocalError;
22352236
PyObject *PyExc_TypeError;
22362237
PyObject *PyExc_ValueError;
22372238
PyObject *PyExc_ZeroDivisionError;
@@ -2261,13 +2262,19 @@ bltin_exc[] = {
22612262
{"KeyError", &PyExc_KeyError, 1},
22622263
{"KeyboardInterrupt", &PyExc_KeyboardInterrupt, 1},
22632264
{"MemoryError", &PyExc_MemoryError, 1},
2265+
/* Note: NameError is not a leaf in exceptions.py, but unlike
2266+
the other non-leafs NameError is meant to be raised directly
2267+
at times -- the leaf_exc member really seems to mean something
2268+
like "this is an abstract base class" when false.
2269+
*/
22642270
{"NameError", &PyExc_NameError, 1},
22652271
{"OverflowError", &PyExc_OverflowError, 1},
22662272
{"RuntimeError", &PyExc_RuntimeError, 1},
22672273
{"NotImplementedError",&PyExc_NotImplementedError,1},
22682274
{"SyntaxError", &PyExc_SyntaxError, 1},
22692275
{"SystemError", &PyExc_SystemError, 1},
22702276
{"SystemExit", &PyExc_SystemExit, 1},
2277+
{"UnboundLocalError", &PyExc_UnboundLocalError, 1},
22712278
{"TypeError", &PyExc_TypeError, 1},
22722279
{"ValueError", &PyExc_ValueError, 1},
22732280
{"ZeroDivisionError", &PyExc_ZeroDivisionError, 1},
@@ -2420,6 +2427,14 @@ initerrors(dict)
24202427
PyTuple_SET_ITEM(PyExc_EnvironmentError, 1, PyExc_OSError);
24212428
PyDict_SetItemString(dict, "EnvironmentError", PyExc_EnvironmentError);
24222429

2430+
/* Make UnboundLocalError an alias for NameError */
2431+
Py_INCREF(PyExc_NameError);
2432+
Py_DECREF(PyExc_UnboundLocalError);
2433+
PyExc_UnboundLocalError = PyExc_NameError;
2434+
if (PyDict_SetItemString(dict, "UnboundLocalError",
2435+
PyExc_NameError) != 0)
2436+
Py_FatalError("Cannot create string-based exceptions");
2437+
24232438
/* missing from the StandardError tuple: Exception, StandardError,
24242439
* and SystemExit
24252440
*/

Python/ceval.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1320,7 +1320,7 @@ eval_code2(co, globals, locals,
13201320
case LOAD_FAST:
13211321
x = GETLOCAL(oparg);
13221322
if (x == NULL) {
1323-
PyErr_SetObject(PyExc_NameError,
1323+
PyErr_SetObject(PyExc_UnboundLocalError,
13241324
PyTuple_GetItem(co->co_varnames,
13251325
oparg));
13261326
break;
@@ -1338,7 +1338,7 @@ eval_code2(co, globals, locals,
13381338
case DELETE_FAST:
13391339
x = GETLOCAL(oparg);
13401340
if (x == NULL) {
1341-
PyErr_SetObject(PyExc_NameError,
1341+
PyErr_SetObject(PyExc_UnboundLocalError,
13421342
PyTuple_GetItem(co->co_varnames,
13431343
oparg));
13441344
break;

0 commit comments

Comments
 (0)