Index: Objects/dictobject.c =================================================================== --- Objects/dictobject.c (revision 62424) +++ Objects/dictobject.c (working copy) @@ -9,6 +9,7 @@ #include "Python.h" +static PyObject *keyerror_message = NULL; /* Set a key error with the specified argument, wrapping it in a * tuple automatically so that tuple keys are not unpacked as the @@ -17,7 +18,8 @@ set_key_error(PyObject *arg) { PyObject *tup; - tup = PyTuple_Pack(1, arg); + + tup = PyTuple_Pack(2, keyerror_message, arg); if (!tup) return; /* caller will expect error to be set anyway */ PyErr_SetObject(PyExc_KeyError, tup); @@ -233,6 +235,11 @@ #ifdef SHOW_ALLOC_COUNT Py_AtExit(show_alloc); #endif + + /* Also initialize the standard error message */ + keyerror_message = PyString_FromString("Not in dict"); + if (keyerror_message == NULL) + return NULL; } if (numfree) { mp = free_list[--numfree]; Index: Objects/exceptions.c =================================================================== --- Objects/exceptions.c (revision 62424) +++ Objects/exceptions.c (working copy) @@ -1217,17 +1217,26 @@ static PyObject * KeyError_str(PyBaseExceptionObject *self) { - /* If args is a tuple of exactly one item, apply repr to args[0]. + /* If args is a tuple of exactly two items, the first is supposed to be a message, + and the second is the offending key. This is done so that e.g. the exception raised by {}[''] prints - KeyError: '' + KeyError: not in dict: '' rather than the confusing KeyError - alone. The downside is that if KeyError is raised with an explanatory - string, that string will be displayed in quotes. Too bad. + alone. If args is anything else, use the default BaseException__str__(). */ - if (PyTuple_GET_SIZE(self->args) == 1) { - return PyObject_Repr(PyTuple_GET_ITEM(self->args, 0)); + if (PyTuple_GET_SIZE(self->args) == 2) { + PyObject *fmt, *ret; + + fmt = PyString_FromString("%s: %r"); + if (!fmt) + return NULL; + + ret = PyString_Format(fmt, self->args); + + Py_DECREF(fmt); + return ret; } return BaseException_str(self); } Index: Objects/setobject.c =================================================================== --- Objects/setobject.c (revision 62424) +++ Objects/setobject.c (working copy) @@ -17,7 +17,15 @@ set_key_error(PyObject *arg) { PyObject *tup; - tup = PyTuple_Pack(1, arg); + static PyObject *message = NULL; + + if (!message) { + message = PyString_FromString("Not in set"); + if (!message) + return; + } + + tup = PyTuple_Pack(2, message, arg); if (!tup) return; /* caller will expect error to be set anyway */ PyErr_SetObject(PyExc_KeyError, tup); Index: Lib/_abcoll.py =================================================================== --- Lib/_abcoll.py (revision 62424) +++ Lib/_abcoll.py (working copy) @@ -256,7 +256,7 @@ def remove(self, value): """Remove an element. If not a member, raise a KeyError.""" if value not in self: - raise KeyError(value) + raise KeyError("Not in set", value) self.discard(value) def pop(self): Index: Lib/weakref.py =================================================================== --- Lib/weakref.py (revision 62424) +++ Lib/weakref.py (working copy) @@ -53,7 +53,7 @@ def __getitem__(self, key): o = self.data[key]() if o is None: - raise KeyError, key + raise KeyError("Not in dict", key) else: return o @@ -151,7 +151,7 @@ return args[0] raise if o is None: - raise KeyError, key + raise KeyError("Not in dict", key) else: return o Index: Lib/UserDict.py =================================================================== --- Lib/UserDict.py (revision 62424) +++ Lib/UserDict.py (working copy) @@ -19,7 +19,7 @@ return self.data[key] if hasattr(self.__class__, "__missing__"): return self.__class__.__missing__(self, key) - raise KeyError(key) + raise KeyError("Not in dict", key) def __setitem__(self, key, item): self.data[key] = item def __delitem__(self, key): del self.data[key] def clear(self): self.data.clear() Index: Lib/ConfigParser.py =================================================================== --- Lib/ConfigParser.py (revision 62424) +++ Lib/ConfigParser.py (working copy) @@ -588,7 +588,7 @@ value = value % vars except KeyError, e: raise InterpolationMissingOptionError( - option, section, rawval, e[0]) + option, section, rawval, e.args[-1]) else: break if "%(" in value: Index: Lib/test/test_dict.py =================================================================== --- Lib/test/test_dict.py (revision 62424) +++ Lib/test/test_dict.py (working copy) @@ -450,7 +450,7 @@ try: f[42] except KeyError, err: - self.assertEqual(err.args, (42,)) + self.assertEqual(err.args, ("Not in dict", 42)) else: self.fail("f[42] didn't raise KeyError") class G(dict): @@ -459,7 +459,7 @@ try: g[42] except KeyError, err: - self.assertEqual(err.args, (42,)) + self.assertEqual(err.args, ("Not in dict", 42)) else: self.fail("g[42] didn't raise KeyError") @@ -469,7 +469,7 @@ try: d[(1,)] except KeyError, e: - self.assertEqual(e.args, ((1,),)) + self.assertEqual(e.args, ("Not in dict", (1,))) else: self.fail("missing KeyError") Index: Lib/test/test_set.py =================================================================== --- Lib/test/test_set.py (revision 62424) +++ Lib/test/test_set.py (working copy) @@ -367,7 +367,7 @@ try: self.s.remove(v1) except KeyError, e: - v2 = e.args[0] + v2 = e.args[1] self.assertEqual(v1, v2) else: self.fail() Index: Lib/test/test_defaultdict.py =================================================================== --- Lib/test/test_defaultdict.py (revision 62424) +++ Lib/test/test_defaultdict.py (working copy) @@ -44,7 +44,7 @@ try: d2[15] except KeyError, err: - self.assertEqual(err.args, (15,)) + self.assertEqual(err.args, ("Not in dict", 15)) else: self.fail("d2[15] didn't raise KeyError") self.assertRaises(TypeError, defaultdict, 1) @@ -137,7 +137,7 @@ try: d1[(1,)] except KeyError, err: - self.assertEqual(err.args[0], (1,)) + self.assertEqual(err.args[1], (1,)) else: self.fail("expected KeyError") Index: Lib/test/test_userdict.py =================================================================== --- Lib/test/test_userdict.py (revision 62424) +++ Lib/test/test_userdict.py (working copy) @@ -183,7 +183,7 @@ try: f[42] except KeyError, err: - self.assertEqual(err.args, (42,)) + self.assertEqual(err.args, ("Not in dict", 42)) else: self.fail("f[42] didn't raise KeyError") class G(UserDict.UserDict): @@ -192,7 +192,7 @@ try: g[42] except KeyError, err: - self.assertEqual(err.args, (42,)) + self.assertEqual(err.args, ("Not in dict", 42)) else: self.fail("g[42] didn't raise KeyError") Index: Modules/_collectionsmodule.c =================================================================== --- Modules/_collectionsmodule.c (revision 62424) +++ Modules/_collectionsmodule.c (working copy) @@ -1150,7 +1150,7 @@ PyDoc_STRVAR(defdict_missing_doc, "__missing__(key) # Called by __getitem__ for missing key; pseudo-code:\n\ - if self.default_factory is None: raise KeyError((key,))\n\ + if self.default_factory is None: raise KeyError('Not in dict', key)\n\ self[key] = value = self.default_factory()\n\ return value\n\ "); @@ -1162,8 +1162,15 @@ PyObject *value; if (factory == NULL || factory == Py_None) { /* XXX Call dict.__missing__(key) */ + static PyObject *message = NULL; PyObject *tup; - tup = PyTuple_Pack(1, key); + + if (!message) { + message = PyString_FromString("Not in dict"); + if (!message) return NULL; + } + + tup = PyTuple_Pack(2, message, key); if (!tup) return NULL; PyErr_SetObject(PyExc_KeyError, tup); Py_DECREF(tup);