[Patches] Patch: AttributeError and NameError: second attempt.

Nick Mathewson nickm@MIT.EDU
Fri, 26 May 2000 00:57:01 -0400


>
>    Nick> A secondary advantage (noted by Mr. von Loewis) is that putting
>    Nick> the error-generation logic in Python makes it easier to improve
>    Nick> our search for suggestions.
>
>How does that square with Barry Warsaw's conversion (today) of the standard
>class exceptions to C?  Do you need to subclass NameError/AttributeError and
>replace the references in __builtins__ with the subclass with the __repr__
>magic?

On further thinking, this was the right approach.  We subclass NameError
and AttributeError in the exceptions module, and call a 'patch_exceptions'
function from Py_Initialize in order to import the revised exceptions from
the module, and patch them into PyExc_{NameError|AttributeError} and
__builtin__.

Here's my third try.  It has all the benefits and features I described
in the first message on this thread, plus one more:

  >>> class NumHolder:
  ...     def __init__(self, num):
  ...         self.x = num
  ...     def get(self):
  ...         return x
  ...
  >>> NumHolder(5).get()
  Traceback (most recent call last):
     File "<stdin>", line 1, in ?
     File "<stdin>", line 5, in get
  NameError: No such name as 'x'. Perhaps you meant 'self.x'?

This is one of the most common beginner errors on comp.lang.python.  The
extra code required to detect it: 4 lines!

===============
Description:

Added functionality to __str__ methods of NameError and AttributeError:
We suggest alternative names which differ only in case, if such names
exist.  Also added convience functions to generate new AttributeError
objects.

===============
Disclaimer:

I confirm that, to the best of my knowledge and belief, this
contribution is free of any claims of third parties under
copyright, patent or other rights or interests ("claims").  To
the extent that I have any such claims, I hereby grant to CNRI a
nonexclusive, irrevocable, royalty-free, worldwide license to
reproduce, distribute, perform and/or display publicly, prepare
derivative versions, and otherwise use this contribution as part
of the Python software and its related documentation, or any
derivative versions thereof, at no cost to CNRI or its licensed
users, and to authorize others to do so.

I acknowledge that CNRI may, at its sole discretion, decide
whether or not to incorporate this contribution in the Python
software and its related documentation.  I further grant CNRI
permission to use my name and other identifying information
provided to CNRI by me for use in connection with the Python
software and its related documentation.

THIS PATCH IS AGAINST THE CURRENT CVS TREE.
========================================
Index: dist/src/Include/pyerrors.h
===================================================================
RCS file: /cvsroot/python/python/dist/src/Include/pyerrors.h,v
retrieving revision 2.35
diff -c -t -r2.35 pyerrors.h
*** dist/src/Include/pyerrors.h	2000/03/10 22:33:32	2.35
--- dist/src/Include/pyerrors.h	2000/05/26 04:48:03
***************
*** 96,101 ****
--- 96,103 ----
  extern DL_IMPORT(PyObject *) PyErr_SetFromErrno Py_PROTO((PyObject *));
  extern DL_IMPORT(PyObject *) PyErr_SetFromErrnoWithFilename Py_PROTO((PyObject *, char *));
  extern DL_IMPORT(PyObject *) PyErr_Format Py_PROTO((PyObject *, const char *, ...));
+ extern DL_IMPORT(void) PyErr_AttributeError Py_PROTO((PyObject *, PyObject *));
+ extern DL_IMPORT(void) PyErr_AttributeErrorString Py_PROTO((PyObject *, char *));
  #ifdef MS_WINDOWS
  extern DL_IMPORT(PyObject *) PyErr_SetFromWindowsErrWithFilename(int, const char *);
  extern DL_IMPORT(PyObject *) PyErr_SetFromWindowsErr(int);
Index: dist/src/Include/pythonrun.h
===================================================================
RCS file: /cvsroot/python/python/dist/src/Include/pythonrun.h,v
retrieving revision 2.27
diff -c -t -r2.27 pythonrun.h
*** dist/src/Include/pythonrun.h	2000/05/25 23:05:36	2.27
--- dist/src/Include/pythonrun.h	2000/05/26 04:48:03
***************
*** 91,96 ****
--- 91,97 ----
  DL_IMPORT(PyObject *) _PySys_Init Py_PROTO((void));
  DL_IMPORT(void) _PyImport_Init Py_PROTO((void));
  DL_IMPORT(void) init_exceptions Py_PROTO((void));
+ DL_IMPORT(void) patch_exceptions Py_PROTO((void));
  
  /* Various internal finalizers */
  DL_IMPORT(void) fini_exceptions Py_PROTO((void));
Index: dist/src/Lib/exceptions.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/exceptions.py,v
retrieving revision 1.19
diff -c -t -r1.19 exceptions.py
*** dist/src/Lib/exceptions.py	2000/05/25 23:15:52	1.19
--- dist/src/Lib/exceptions.py	2000/05/26 04:48:03
***************
*** 1,4 ****
  # Standard class-base exceptions are now all implemented in C.  This remains
! # for backwards compatibility with pre-1.6 releases.
  from _exceptions import *
  from _exceptions import __doc__
--- 1,106 ----
  # Standard class-base exceptions are now all implemented in C.  This remains
! # for backwards compatibility with pre-1.6 releases, and for specialized
! # versions of the standard exceptions.
  from _exceptions import *
  from _exceptions import __doc__
+ 
+ _AttributeError = AttributeError
+ _NameError = NameError
+ 
+ class AttributeError(_AttributeError):
+     """Attribute not found."""
+         
+     def __str__(self):
+         if len(self.args) == 2:
+             obj  = self.args[0]
+             name = self.args[1]
+             if hasattr(obj, '__class__'):
+                 typename = "'%s' instance" % obj.__class__.__name__
+             else:
+                 typename = type(obj).__name__
+             nearest = _find_nearest_attribute(obj, name)
+             if nearest: 
+                 s = ("%s has no attribute '%s'. Perhaps you meant '%s'?"
+                      % (typename, name, nearest))
+             else:
+                 s = "%s has no attribute '%s'" % (typename, name)
+             self.args = (s,)
+             return s
+         elif len(self.args) == 1:
+             return str(self.args[0])
+         else:
+             return ""
+ 
+ class NameError(_NameError):
+     """Name not found globally."""
+ 
+     def __str__(self):
+         if len(self.args) == 2:
+             frame = self.args[0]
+             name  = self.args[1]
+             nearest = _find_nearest_name_in_frame(frame, name)
+             if nearest: 
+                 s = ("No such name as '%s'. Perhaps you meant '%s'?" 
+                      % (name,nearest))
+             else:
+                 s = ("No such name as '%s'" % name)
+             self.args = (s,)
+             return s
+         elif len(self.args) == 1:
+             return str(self.args[0])
+         else:
+             return ""
+ 
+ def _nearest_name_in_list(lst, name):
+     """Returns the element of 'lst' which is closest to name.  Name must
+        be in lowercase.  Returns None if no such element is found."""
+ 
+     for i in lst:
+         if i.lower() == name:
+             return i
+ 
+     return None
+ 
+ def _find_nearest_attribute(object, name):
+     """Returns the attribute on object which is closest to name.
+        Returns None if no such attribute is found."""
+ 
+     name = name.lower()
+     if hasattr(object, '__dict__'):
+         r = _nearest_name_in_list(object.__dict__.keys(), name)
+         if r:
+             return r
+     if hasattr(object, '__class__') and hasattr(object.__class__, '__dict__'):
+         r = _nearest_name_in_list(object.__class__.__dict__.keys(), name)
+         if r:
+             return r
+     if hasattr(object, '__members__'):
+         r = _nearest_name_in_list(object.__members__, name)
+         if r: 
+             return r
+     if hasattr(object, '__methods__'):
+         r = _nearest_name_in_list(object.__members__, name)
+         if r: 
+             return r
+ 
+     return None
+ 
+ def _find_nearest_name_in_frame(frame, name):
+     name = name.lower()
+     
+     for attr in ('f_locals', 'f_globals', 'f_builtins'):
+         if hasattr(frame, attr):
+             r = _nearest_name_in_list(getattr(frame,attr).keys(), name)
+             if r:
+                 return r
+ 
+     #XXX The name 'self' is just a convention.  What we really
+     #XXX need is a good way to tell if this is a method, and to find
+     #XXX the first variable name if it is.  The second part is easy;
+     #XXX the first, hard.
+     if hasattr(frame, 'f_locals') and frame.f_locals.has_key('self'):
+         r = _find_nearest_attribute(frame.f_locals['self'], name)
+         if r:
+             return "self." + r
+ 
+     return None
Index: dist/src/Modules/_exceptions.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Modules/_exceptions.c,v
retrieving revision 1.1
diff -c -t -r1.1 _exceptions.c
*** dist/src/Modules/_exceptions.c	2000/05/25 23:18:47	1.1
--- dist/src/Modules/_exceptions.c	2000/05/26 04:48:06
***************
*** 975,980 ****
--- 975,1022 ----
      Py_DECREF(bltinmod);
  }
  
+ void
+ #ifdef WIN32
+ __declspec(dllexport)
+ #endif /* WIN32 */
+ patch_exceptions()
+ {
+     PyObject *m = PyImport_ImportModule("exceptions");
+     PyObject *bltinmod = PyImport_ImportModule("__builtin__");
+     PyObject *bdict = PyModule_GetDict(bltinmod);
+     PyObject *d = NULL;
+     PyObject *c = NULL;
+     
+     if (m == NULL || bdict == NULL ||
+         (d = PyModule_GetDict(m)) == NULL) {
+         /* XXX We should probably care a little more about this
+          * XXX error case, but it shouldn't stop programs from running.
+          */
+         
+         goto finally;
+     } else {
+         c = PyDict_GetItemString(d, "AttributeError");
+         if (c) {
+             Py_INCREF(c);
+             Py_XDECREF(PyExc_AttributeError);
+             PyExc_AttributeError = c;
+             PyDict_SetItemString(bdict, "AttributeError", c);
+         }
+ 
+         c = PyDict_GetItemString(d, "NameError");
+         if (c) {
+             Py_INCREF(c);
+             Py_XDECREF(PyExc_NameError);
+             PyExc_NameError = c;
+             PyDict_SetItemString(bdict, "NameError", c);
+         }
+     }
+ 
+  finally:
+     Py_XDECREF(m);
+     Py_XDECREF(bltinmod);
+ }
+ 
  
  void
  #ifdef WIN32
Index: dist/src/Objects/abstract.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Objects/abstract.c,v
retrieving revision 2.34
diff -c -t -r2.34 abstract.c
*** dist/src/Objects/abstract.c	2000/04/05 20:11:20	2.34
--- dist/src/Objects/abstract.c	2000/05/26 04:48:06
***************
*** 1536,1542 ****
          func = PyObject_GetAttrString(o, name);
          if (func == NULL) {
                  va_end(va);
!                 PyErr_SetString(PyExc_AttributeError, name);
                  return 0;
          }
  
--- 1536,1542 ----
          func = PyObject_GetAttrString(o, name);
          if (func == NULL) {
                  va_end(va);
!                 PyErr_AttributeErrorString(o, name);
                  return 0;
          }
  
Index: dist/src/Objects/classobject.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Objects/classobject.c,v
retrieving revision 2.86
diff -c -t -r2.86 classobject.c
*** dist/src/Objects/classobject.c	2000/05/03 23:44:34	2.86
--- dist/src/Objects/classobject.c	2000/05/26 04:48:07
***************
*** 207,213 ****
          }
          v = class_lookup(op, name, &class);
          if (v == NULL) {
!                 PyErr_SetObject(PyExc_AttributeError, name);
                  return NULL;
          }
          Py_INCREF(v);
--- 207,213 ----
          }
          v = class_lookup(op, name, &class);
          if (v == NULL) {
!                 PyErr_AttributeError((PyObject*) op, name);
                  return NULL;
          }
          Py_INCREF(v);
***************
*** 331,338 ****
          if (v == NULL) {
                  int rv = PyDict_DelItem(op->cl_dict, name);
                  if (rv < 0)
!                         PyErr_SetString(PyExc_AttributeError,
!                                    "delete non-existing class attribute");
                  return rv;
          }
          else
--- 331,337 ----
          if (v == NULL) {
                  int rv = PyDict_DelItem(op->cl_dict, name);
                  if (rv < 0)
!                         PyErr_AttributeError((PyObject*) op, name);
                  return rv;
          }
          else
***************
*** 588,595 ****
          }
          v = instance_getattr2(inst, name);
          if (v == NULL) {
!                 PyErr_Format(PyExc_AttributeError,"'%.50s' instance has no attribute '%.400s'",
!                              PyString_AS_STRING(inst->in_class->cl_name), sname);
          }
          return v;
  }
--- 587,593 ----
          }
          v = instance_getattr2(inst, name);
          if (v == NULL) {
!                 PyErr_AttributeError((PyObject*) inst, name);
          }
          return v;
  }
***************
*** 659,666 ****
          if (v == NULL) {
                  int rv = PyDict_DelItem(inst->in_dict, name);
                  if (rv < 0)
!                         PyErr_SetString(PyExc_AttributeError,
!                                    "delete non-existing instance attribute");
                  return rv;
          }
          else
--- 657,663 ----
          if (v == NULL) {
                  int rv = PyDict_DelItem(inst->in_dict, name);
                  if (rv < 0)
!                         PyErr_AttributeError((PyObject*) inst, name);
                  return rv;
          }
          else
Index: dist/src/Objects/methodobject.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Objects/methodobject.c,v
retrieving revision 2.26
diff -c -t -r2.26 methodobject.c
*** dist/src/Objects/methodobject.c	2000/05/03 23:44:35	2.26
--- dist/src/Objects/methodobject.c	2000/05/26 04:48:08
***************
*** 135,141 ****
                  return Py_BuildValue("[sss]",
                                       "__doc__", "__name__", "__self__");
          }
!         PyErr_SetString(PyExc_AttributeError, name);
          return NULL;
  }
  
--- 135,141 ----
                  return Py_BuildValue("[sss]",
                                       "__doc__", "__name__", "__self__");
          }
!         PyErr_AttributeErrorString((PyObject*) m, name);
          return NULL;
  }
  
Index: dist/src/Objects/moduleobject.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Objects/moduleobject.c,v
retrieving revision 2.25
diff -c -t -r2.25 moduleobject.c
*** dist/src/Objects/moduleobject.c	2000/05/03 23:44:35	2.25
--- dist/src/Objects/moduleobject.c	2000/05/26 04:48:08
***************
*** 208,214 ****
          }
          res = PyDict_GetItemString(m->md_dict, name);
          if (res == NULL)
!                 PyErr_SetString(PyExc_AttributeError, name);
          else
                  Py_INCREF(res);
          return res;
--- 208,214 ----
          }
          res = PyDict_GetItemString(m->md_dict, name);
          if (res == NULL)
!                 PyErr_AttributeErrorString((PyObject*) m, name);
          else
                  Py_INCREF(res);
          return res;
***************
*** 228,235 ****
          if (v == NULL) {
                  int rv = PyDict_DelItemString(m->md_dict, name);
                  if (rv < 0)
!                         PyErr_SetString(PyExc_AttributeError,
!                                    "delete non-existing module attribute");
                  return rv;
          }
          else
--- 228,234 ----
          if (v == NULL) {
                  int rv = PyDict_DelItemString(m->md_dict, name);
                  if (rv < 0)
!                         PyErr_AttributeErrorString((PyObject*) m, name);
                  return rv;
          }
          else
Index: dist/src/Objects/object.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Objects/object.c,v
retrieving revision 2.70
diff -c -t -r2.70 object.c
*** dist/src/Objects/object.c	2000/05/03 23:44:35	2.70
--- dist/src/Objects/object.c	2000/05/26 04:48:09
***************
*** 536,545 ****
          }
  
          if (v->ob_type->tp_getattr == NULL) {
!                 PyErr_Format(PyExc_AttributeError,
!                              "'%.50s' object has no attribute '%.400s'",
!                              v->ob_type->tp_name,
!                              name);
                  return NULL;
          }
          else {
--- 536,542 ----
          }
  
          if (v->ob_type->tp_getattr == NULL) {
!                 PyErr_AttributeErrorString(v, name);
                  return NULL;
          }
          else {
Index: dist/src/Objects/sliceobject.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Objects/sliceobject.c,v
retrieving revision 2.4
diff -c -t -r2.4 sliceobject.c
*** dist/src/Objects/sliceobject.c	2000/05/03 23:44:35	2.4
--- dist/src/Objects/sliceobject.c	2000/05/26 04:48:09
***************
*** 157,163 ****
                                       "start", "stop", "step");
          }
          else {
!                 PyErr_SetString(PyExc_AttributeError, name);
                  return NULL;
          }
          Py_INCREF(ret);
--- 157,163 ----
                                       "start", "stop", "step");
          }
          else {
!                 PyErr_AttributeErrorString((PyObject*) self, name);
                  return NULL;
          }
          Py_INCREF(ret);
Index: dist/src/Objects/typeobject.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Objects/typeobject.c,v
retrieving revision 2.12
diff -c -t -r2.12 typeobject.c
*** dist/src/Objects/typeobject.c	1997/06/02 14:43:07	2.12
--- dist/src/Objects/typeobject.c	2000/05/26 04:48:09
***************
*** 51,57 ****
          }
          if (strcmp(name, "__members__") == 0)
                  return Py_BuildValue("[ss]", "__doc__", "__name__");
!         PyErr_SetString(PyExc_AttributeError, name);
          return NULL;
  }
  
--- 51,57 ----
          }
          if (strcmp(name, "__members__") == 0)
                  return Py_BuildValue("[ss]", "__doc__", "__name__");
!         PyErr_AttributeErrorString((PyObject*) t, name);
          return NULL;
  }
  
Index: dist/src/Objects/xxobject.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Objects/xxobject.c,v
retrieving revision 2.15
diff -c -t -r2.15 xxobject.c
*** dist/src/Objects/xxobject.c	2000/05/03 23:44:36	2.15
--- dist/src/Objects/xxobject.c	2000/05/26 04:48:09
***************
*** 119,126 ****
          if (v == NULL) {
                  int rv = PyDict_DelItemString(xp->x_attr, name);
                  if (rv < 0)
!                         PyErr_SetString(PyExc_AttributeError,
!                                 "delete non-existing xx attribute");
                  return rv;
          }
          else
--- 119,125 ----
          if (v == NULL) {
                  int rv = PyDict_DelItemString(xp->x_attr, name);
                  if (rv < 0)
!                         PyErr_AttributeErrorString((PyObject*) xp, name);
                  return rv;
          }
          else
Index: dist/src/Python/ceval.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Python/ceval.c,v
retrieving revision 2.179
diff -c -t -r2.179 ceval.c
*** dist/src/Python/ceval.c	2000/05/08 14:06:50	2.179
--- dist/src/Python/ceval.c	2000/05/26 04:48:11
***************
*** 92,99 ****
  static void set_exc_info Py_PROTO((PyThreadState *,
                                  PyObject *, PyObject *, PyObject *));
  static void reset_exc_info Py_PROTO((PyThreadState *));
  
- 
  /* Dynamic execution profile */
  #ifdef DYNAMIC_EXECUTION_PROFILE
  #ifdef DXPAIRS
--- 92,99 ----
  static void set_exc_info Py_PROTO((PyThreadState *,
                                  PyObject *, PyObject *, PyObject *));
  static void reset_exc_info Py_PROTO((PyThreadState *));
+ static void set_name_error Py_PROTO((PyFrameObject *, PyObject *));
  
  /* Dynamic execution profile */
  #ifdef DYNAMIC_EXECUTION_PROFILE
  #ifdef DXPAIRS
***************
*** 1194,1200 ****
                                  break;
                          }
                          if ((err = PyDict_DelItem(x, w)) != 0)
!                                 PyErr_SetObject(PyExc_NameError, w);
                          break;
  
  #ifdef CASE_TOO_BIG
--- 1194,1200 ----
                                  break;
                          }
                          if ((err = PyDict_DelItem(x, w)) != 0)
!                                 set_name_error(f, w);
                          break;
  
  #ifdef CASE_TOO_BIG
***************
*** 1274,1280 ****
                  case DELETE_GLOBAL:
                          w = GETNAMEV(oparg);
                          if ((err = PyDict_DelItem(f->f_globals, w)) != 0)
!                                 PyErr_SetObject(PyExc_NameError, w);
                          break;
                  
                  case LOAD_CONST:
--- 1274,1280 ----
                  case DELETE_GLOBAL:
                          w = GETNAMEV(oparg);
                          if ((err = PyDict_DelItem(f->f_globals, w)) != 0)
!                                 set_name_error(f, w);
                          break;
                  
                  case LOAD_CONST:
***************
*** 1296,1303 ****
                                  if (x == NULL) {
                                          x = PyDict_GetItem(f->f_builtins, w);
                                          if (x == NULL) {
!                                                 PyErr_SetObject(
!                                                         PyExc_NameError, w);
                                                  break;
                                          }
                                  }
--- 1296,1302 ----
                                  if (x == NULL) {
                                          x = PyDict_GetItem(f->f_builtins, w);
                                          if (x == NULL) {
!                                                 set_name_error(f, w);
                                                  break;
                                          }
                                  }
***************
*** 1312,1318 ****
                          if (x == NULL) {
                                  x = PyDict_GetItem(f->f_builtins, w);
                                  if (x == NULL) {
!                                         PyErr_SetObject(PyExc_NameError, w);
                                          break;
                                  }
                          }
--- 1311,1317 ----
                          if (x == NULL) {
                                  x = PyDict_GetItem(f->f_builtins, w);
                                  if (x == NULL) {
!                                         set_name_error(f, w);
                                          break;
                                  }
                          }
***************
*** 2929,2934 ****
--- 2928,2950 ----
          return list;
  }
  
+ static void
+ set_name_error(f, name) 
+         PyFrameObject *f; 
+         PyObject *name;
+ {
+         PyObject *tuple = PyTuple_New(2);
+ 
+         PyTuple_SET_ITEM(tuple, 0, (PyObject*) f);
+         PyTuple_SET_ITEM(tuple, 1, name);
+ 
+         Py_INCREF(f);
+         Py_INCREF(name);
+ 
+         PyErr_SetObject(PyExc_NameError, tuple);
+ 
+         Py_DECREF(tuple);
+ }     
  
  #ifdef DYNAMIC_EXECUTION_PROFILE
  
Index: dist/src/Python/errors.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Python/errors.c,v
retrieving revision 2.45
diff -c -t -r2.45 errors.c
*** dist/src/Python/errors.c	2000/05/02 19:27:51	2.45
--- dist/src/Python/errors.c	2000/05/26 04:48:12
***************
*** 349,360 ****
          return NULL;
  }
  
- 
  PyObject *
  PyErr_SetFromErrno(exc)
          PyObject *exc;
  {
          return PyErr_SetFromErrnoWithFilename(exc, NULL);
  }
  
  #ifdef MS_WINDOWS 
--- 349,383 ----
          return NULL;
  }
  
  PyObject *
  PyErr_SetFromErrno(exc)
          PyObject *exc;
  {
          return PyErr_SetFromErrnoWithFilename(exc, NULL);
+ }
+ 
+ void
+ PyErr_AttributeError(object, name)
+      PyObject *object;
+      PyObject *name;
+ {
+         PyObject *tuple = PyTuple_New(2);
+         Py_INCREF(object);
+         Py_INCREF(name);
+         PyTuple_SET_ITEM(tuple, 0, object);
+         PyTuple_SET_ITEM(tuple, 1, name);
+         PyErr_SetObject(PyExc_AttributeError, tuple);
+         Py_DECREF(tuple);
+ }
+ 
+ void
+ PyErr_AttributeErrorString(object, name)
+      PyObject *object;
+      char *name;
+ {
+         PyObject *str = PyString_FromString(name);
+         PyErr_AttributeError(object, str);
+         Py_DECREF(str);
  }
  
  #ifdef MS_WINDOWS 
Index: dist/src/Python/pythonrun.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Python/pythonrun.c,v
retrieving revision 2.97
diff -c -t -r2.97 pythonrun.c
*** dist/src/Python/pythonrun.c	2000/05/25 23:09:49	2.97
--- dist/src/Python/pythonrun.c	2000/05/26 04:48:13
***************
*** 175,180 ****
--- 175,181 ----
  
          /* phase 2 of builtins */
          _PyImport_FixupExtension("__builtin__", "__builtin__");
+         patch_exceptions();
  
          initsigs(); /* Signal handling stuff, including initintr() */
  
========================================