[Patches] indexerror: better error messages

Peter Schneider-Kamp peter@schneider-kamp.de
Sun, 11 Jun 2000 12:15:37 +0200


This is a multi-part message in MIME format.
--------------21F20B013F0333841F36363B
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

error messages like "list index out of range" are not very
helpful. a discussion about better error messages on the
python mailing list proposed to add information about the
value of the index which caused the IndexError and about
the valid range.a few examples:

"abc"[3]
IndexError: string index 3 out of range [0..2]

u"guido"[-10]
IndexError: unicode string index -10 out of range [-5..-1]

[][7]
IndexError: cannot get item from empty list

The speed penalty in case of a caught exception is constant
and imho negligibly.

an extra function PyErr_SetFromRange is added to Python/errors.c
for the sake of sanity (wanna see the first version? :]).

patch attached as plaintext context diff
--
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.
--
Peter Schneider-Kamp          ++47-7388-7331
Herman Krags veg 51-11        mailto:peter@schneider-kamp.de
N-7050 Trondheim              http://schneider-kamp.de
--------------21F20B013F0333841F36363B
Content-Type: text/plain; charset=us-ascii;
 name="indexerror-patch-4"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="indexerror-patch-4"

diff -c -b --recursive python/dist/src/Include/pyerrors.h python-mod/dist/src/Include/pyerrors.h
*** python/dist/src/Include/pyerrors.h	Fri Mar 10 23:33:32 2000
--- python-mod/dist/src/Include/pyerrors.h	Sun Jun 11 11:47:38 2000
***************
*** 95,100 ****
--- 95,101 ----
  extern DL_IMPORT(PyObject *) PyErr_NoMemory Py_PROTO((void));
  extern DL_IMPORT(PyObject *) PyErr_SetFromErrno Py_PROTO((PyObject *));
  extern DL_IMPORT(PyObject *) PyErr_SetFromErrnoWithFilename Py_PROTO((PyObject *, char *));
+ extern DL_IMPORT(PyObject *) PyErr_SetFromRange Py_PROTO((const char *, int, int));
  extern DL_IMPORT(PyObject *) PyErr_Format Py_PROTO((PyObject *, const char *, ...));
  #ifdef MS_WINDOWS
  extern DL_IMPORT(PyObject *) PyErr_SetFromWindowsErrWithFilename(int, const char *);
diff -c -b --recursive python/dist/src/Modules/arraymodule.c python-mod/dist/src/Modules/arraymodule.c
*** python/dist/src/Modules/arraymodule.c	Thu Jun  1 04:02:46 2000
--- python-mod/dist/src/Modules/arraymodule.c	Sun Jun 11 11:47:38 2000
***************
*** 389,395 ****
  	}
  	ap = (arrayobject *)op;
  	if (i < 0 || i >= ap->ob_size) {
! 		PyErr_SetString(PyExc_IndexError, "array index out of range");
  		return NULL;
  	}
  	return (*ap->ob_descr->getitem)(ap, i);
--- 389,400 ----
  	}
  	ap = (arrayobject *)op;
  	if (i < 0 || i >= ap->ob_size) {
!                 if (ap->ob_size) {
!                         PyErr_SetFromRange("array",i,ap->ob_size);
!                 } else {
!                         PyErr_SetString(PyExc_IndexError,
!                             "cannot get item from empty array");
!                 }
  		return NULL;
  	}
  	return (*ap->ob_descr->getitem)(ap, i);
***************
*** 502,508 ****
  	int i;
  {
  	if (i < 0 || i >= a->ob_size) {
! 		PyErr_SetString(PyExc_IndexError, "array index out of range");
  		return NULL;
  	}
  	return getarrayitem((PyObject *)a, i);
--- 507,518 ----
  	int i;
  {
  	if (i < 0 || i >= a->ob_size) {
!                 if (a->ob_size) {
!                         PyErr_SetFromRange("array",i,a->ob_size);
!                 } else {
!                         PyErr_SetString(PyExc_IndexError,
!                             "cannot get item from empty array");
!                 }
  		return NULL;
  	}
  	return getarrayitem((PyObject *)a, i);
***************
*** 668,675 ****
  	PyObject *v;
  {
  	if (i < 0 || i >= a->ob_size) {
  		PyErr_SetString(PyExc_IndexError,
! 			         "array assignment index out of range");
  		return -1;
  	}
  	if (v == NULL)
--- 678,689 ----
  	PyObject *v;
  {
  	if (i < 0 || i >= a->ob_size) {
+                 if (a->ob_size) {
+                         PyErr_SetFromRange("array",i,a->ob_size);
+                 } else {
  		PyErr_SetString(PyExc_IndexError,
!                             "cannot set item in empty array");
!                 }
  		return -1;
  	}
  	if (v == NULL)
diff -c -b --recursive python/dist/src/Modules/mmapmodule.c python-mod/dist/src/Modules/mmapmodule.c
*** python/dist/src/Modules/mmapmodule.c	Sat Jun  3 22:43:43 2000
--- python-mod/dist/src/Modules/mmapmodule.c	Sun Jun 11 11:58:16 2000
***************
*** 547,553 ****
  {
  	CHECK_VALID(NULL);
  	if (i < 0 || i >= self->size) {
! 		PyErr_SetString(PyExc_IndexError, "mmap index out of range");
  		return NULL;
  	}
  	return PyString_FromStringAndSize(self->data + i, 1);
--- 547,557 ----
  {
  	CHECK_VALID(NULL);
  	if (i < 0 || i >= self->size) {
!  		if (self->size)
!                          PyErr_SetFromRange("mmap",i,self->size);
!                 else
!   		         PyErr_SetString(PyExc_IndexError,
!                              "cannot get item from empty mmap");
  		return NULL;
  	}
  	return PyString_FromStringAndSize(self->data + i, 1);
***************
*** 621,628 ****
  		return -1;
  	}
  	if ( PyString_Size(v) != (ihigh - ilow) ) {
! 		PyErr_SetString(PyExc_IndexError, 
! 				"mmap slice assignment is wrong size");
  		return -1;
  	}
  	buf = PyString_AsString(v);
--- 625,631 ----
  		return -1;
  	}
  	if ( PyString_Size(v) != (ihigh - ilow) ) {
!                 PyErr_Format(PyExc_IndexError,"mmap slice assignment must have size %d but has size %d",ihigh-ilow,PyString_Size(v));
  		return -1;
  	}
  	buf = PyString_AsString(v);
***************
*** 640,651 ****
   
  	CHECK_VALID(-1);
  	if (i < 0 || i >= self->size) {
! 		PyErr_SetString(PyExc_IndexError, "mmap index out of range");
  		return -1;
  	}
! 	if (! (PyString_Check(v) && PyString_Size(v)==1) ) {
  		PyErr_SetString(PyExc_IndexError, 
  			"mmap assignment must be single-character string");
  		return -1;
  	}
  	buf = PyString_AsString(v);
--- 643,663 ----
   
  	CHECK_VALID(-1);
  	if (i < 0 || i >= self->size) {
! 		if (self->size)
!                         PyErr_SetFromRange("mmap",i,self->size);
!                 else
!                         PyErr_SetString(PyExc_IndexError,
!                             "cannot get item from empty mmap");
  		return -1;
  	}
! 	if (! (PyString_Check(v)) ) {
  		PyErr_SetString(PyExc_IndexError, 
  			"mmap assignment must be single-character string");
+ 		return -1;
+ 	}
+ 	if ( PyString_Size(v) != 1) {
+                 PyErr_Format(PyExc_IndexError,
+                         "mmap assignment must have single-character string but has size string of size %d",PyString_Size(v));
  		return -1;
  	}
  	buf = PyString_AsString(v);
diff -c -b --recursive python/dist/src/Objects/bufferobject.c python-mod/dist/src/Objects/bufferobject.c
*** python/dist/src/Objects/bufferobject.c	Thu May  4 01:44:34 2000
--- python-mod/dist/src/Objects/bufferobject.c	Sun Jun 11 11:47:38 2000
***************
*** 400,406 ****
  {
  	if ( idx < 0 || idx >= self->b_size )
  	{
! 		PyErr_SetString(PyExc_IndexError, "buffer index out of range");
  		return NULL;
  	}
  	return PyString_FromStringAndSize((char *)self->b_ptr + idx, 1);
--- 400,411 ----
  {
  	if ( idx < 0 || idx >= self->b_size )
  	{
!                 if (self->b_size) {
!                         PyErr_SetFromRange("buffer",idx,self->b_size);
!                 } else {
!                         PyErr_SetString(PyExc_IndexError,
!                             "cannot get item from empty buffer");
!                 }
  		return NULL;
  	}
  	return PyString_FromStringAndSize((char *)self->b_ptr + idx, 1);
***************
*** 447,454 ****
  	}
  
  	if (idx < 0 || idx >= self->b_size) {
  		PyErr_SetString(PyExc_IndexError,
! 				"buffer assignment index out of range");
  		return -1;
  	}
  
--- 452,463 ----
  	}
  
  	if (idx < 0 || idx >= self->b_size) {
+                 if (self->b_size) {
+                         PyErr_SetFromRange("buffer",idx,self->b_size);
+                 } else {
  		PyErr_SetString(PyExc_IndexError,
!                             "cannot set item in empty buffer");
!                 }
  		return -1;
  	}
  
diff -c -b --recursive python/dist/src/Objects/listobject.c python-mod/dist/src/Objects/listobject.c
*** python/dist/src/Objects/listobject.c	Thu Jun  1 16:31:03 2000
--- python-mod/dist/src/Objects/listobject.c	Sun Jun 11 11:47:38 2000
***************
*** 115,124 ****
  		return NULL;
  	}
  	if (i < 0 || i >= ((PyListObject *)op) -> ob_size) {
! 		if (indexerr == NULL)
! 			indexerr = PyString_FromString(
! 				"list index out of range");
  		PyErr_SetObject(PyExc_IndexError, indexerr);
  		return NULL;
  	}
  	return ((PyListObject *)op) -> ob_item[i];
--- 115,128 ----
  		return NULL;
  	}
  	if (i < 0 || i >= ((PyListObject *)op) -> ob_size) {
! 		if (indexerr == NULL) {
!                         if (((PyListObject *)op) -> ob_size) {
!                                 indexerr = PyErr_SetFromRange("list",i,((PyListObject *)op) -> ob_size);
!                         } else {
!                                 indexerr = PyString_FromString("cannot get item from empty list");
  		PyErr_SetObject(PyExc_IndexError, indexerr);
+                         }
+                 } else PyErr_SetObject(PyExc_IndexError, indexerr);
  		return NULL;
  	}
  	return ((PyListObject *)op) -> ob_item[i];
***************
*** 139,146 ****
  	}
  	if (i < 0 || i >= ((PyListObject *)op) -> ob_size) {
  		Py_XDECREF(newitem);
! 		PyErr_SetString(PyExc_IndexError,
! 				"list assignment index out of range");
  		return -1;
  	}
  	p = ((PyListObject *)op) -> ob_item + i;
--- 143,153 ----
  	}
  	if (i < 0 || i >= ((PyListObject *)op) -> ob_size) {
  		Py_XDECREF(newitem);
!                 if (((PyListObject *)op) -> ob_size) {
!                         PyErr_SetFromRange("list",i,((PyListObject *)op) -> ob_size);
!                 } else {
!                         PyErr_SetString(PyExc_IndexError,"cannot set item in empty list");
!                 }
  		return -1;
  	}
  	p = ((PyListObject *)op) -> ob_item + i;
***************
*** 331,340 ****
  	int i;
  {
  	if (i < 0 || i >= a->ob_size) {
! 		if (indexerr == NULL)
! 			indexerr = PyString_FromString(
! 				"list index out of range");
  		PyErr_SetObject(PyExc_IndexError, indexerr);
  		return NULL;
  	}
  	Py_INCREF(a->ob_item[i]);
--- 338,352 ----
  	int i;
  {
  	if (i < 0 || i >= a->ob_size) {
! 		if (indexerr == NULL) {
!                         if (a->ob_size) {
!                                 indexerr = PyErr_SetFromRange("list",i,a->ob_size);
!                         } else {
!                                 indexerr = PyString_FromString("cannot get item from empty list");
  		PyErr_SetObject(PyExc_IndexError, indexerr);
+                                 
+                         }
+                 } else PyErr_SetObject(PyExc_IndexError, indexerr);
  		return NULL;
  	}
  	Py_INCREF(a->ob_item[i]);
***************
*** 551,558 ****
  {
  	PyObject *old_value;
  	if (i < 0 || i >= a->ob_size) {
  		PyErr_SetString(PyExc_IndexError,
! 				"list assignment index out of range");
  		return -1;
  	}
  	if (v == NULL)
--- 563,574 ----
  {
  	PyObject *old_value;
  	if (i < 0 || i >= a->ob_size) {
+                 if (a->ob_size) {
+                         PyErr_SetFromRange("list",i,a->ob_size);
+                 } else {
  		PyErr_SetString(PyExc_IndexError,
!                             "cannot set item in empty list");
!                 }
  		return -1;
  	}
  	if (v == NULL)
***************
*** 701,707 ****
  	if (i < 0)
  		i += self->ob_size;
  	if (i < 0 || i >= self->ob_size) {
! 		PyErr_SetString(PyExc_IndexError, "pop index out of range");
  		return NULL;
  	}
  	v = self->ob_item[i];
--- 717,723 ----
  	if (i < 0)
  		i += self->ob_size;
  	if (i < 0 || i >= self->ob_size) {
!                 PyErr_SetFromRange("list",i,self->ob_size);
  		return NULL;
  	}
  	v = self->ob_item[i];
diff -c -b --recursive python/dist/src/Objects/rangeobject.c python-mod/dist/src/Objects/rangeobject.c
*** python/dist/src/Objects/rangeobject.c	Thu May  4 01:44:35 2000
--- python-mod/dist/src/Objects/rangeobject.c	Sun Jun 11 11:47:38 2000
***************
*** 70,77 ****
  	int i;
  {
  	if (i < 0 || i >= r->len * r->reps) {
  		PyErr_SetString(PyExc_IndexError,
! 				"xrange object index out of range");
  		return NULL;
  	}
  
--- 70,80 ----
  	int i;
  {
  	if (i < 0 || i >= r->len * r->reps) {
+                 if (r->len * r->reps)
+                         PyErr_SetFromRange("xrange",i,r->len * r->reps);
+                 else
  		PyErr_SetString(PyExc_IndexError,
!                                         "cannot get item from empty xrange");
  		return NULL;
  	}
  
diff -c -b --recursive python/dist/src/Objects/stringobject.c python-mod/dist/src/Objects/stringobject.c
*** python/dist/src/Objects/stringobject.c	Fri Jun  9 16:04:53 2000
--- python-mod/dist/src/Objects/stringobject.c	Sun Jun 11 11:47:38 2000
***************
*** 414,420 ****
  	int c;
  	PyObject *v;
  	if (i < 0 || i >= a->ob_size) {
! 		PyErr_SetString(PyExc_IndexError, "string index out of range");
  		return NULL;
  	}
  	c = a->ob_sval[i] & UCHAR_MAX;
--- 414,425 ----
  	int c;
  	PyObject *v;
  	if (i < 0 || i >= a->ob_size) {
!                 if (a->ob_size) {
!                         PyErr_SetFromRange("string",i,a->ob_size);
!                 } else {
!                         PyErr_SetString(PyExc_IndexError,
!                             "cannot get item from empty string");
!                 }
  		return NULL;
  	}
  	c = a->ob_sval[i] & UCHAR_MAX;
diff -c -b --recursive python/dist/src/Objects/tupleobject.c python-mod/dist/src/Objects/tupleobject.c
*** python/dist/src/Objects/tupleobject.c	Thu Jun  1 05:12:13 2000
--- python-mod/dist/src/Objects/tupleobject.c	Sun Jun 11 11:47:38 2000
***************
*** 139,145 ****
  		return NULL;
  	}
  	if (i < 0 || i >= ((PyTupleObject *)op) -> ob_size) {
! 		PyErr_SetString(PyExc_IndexError, "tuple index out of range");
  		return NULL;
  	}
  	return ((PyTupleObject *)op) -> ob_item[i];
--- 139,150 ----
  		return NULL;
  	}
  	if (i < 0 || i >= ((PyTupleObject *)op) -> ob_size) {
!                 if (((PyListObject *)op) -> ob_size) {
!                         PyErr_SetFromRange("tuple",i,((PyListObject *)op) -> ob_size);
!                 } else {
!                         PyErr_SetString(PyExc_IndexError,
!                             "cannot get item from empty tuple");
!                 }
  		return NULL;
  	}
  	return ((PyTupleObject *)op) -> ob_item[i];
***************
*** 160,167 ****
  	}
  	if (i < 0 || i >= ((PyTupleObject *)op) -> ob_size) {
  		Py_XDECREF(newitem);
  		PyErr_SetString(PyExc_IndexError,
! 				"tuple assignment index out of range");
  		return -1;
  	}
  	p = ((PyTupleObject *)op) -> ob_item + i;
--- 165,176 ----
  	}
  	if (i < 0 || i >= ((PyTupleObject *)op) -> ob_size) {
  		Py_XDECREF(newitem);
+                 if (((PyListObject *)op) -> ob_size) {
+                         PyErr_SetFromRange("tuple",i,((PyListObject *)op) -> ob_size);
+                 } else {
  		PyErr_SetString(PyExc_IndexError,
!                             "cannot set item in empty tuple");
!                 }
  		return -1;
  	}
  	p = ((PyTupleObject *)op) -> ob_item + i;
***************
*** 304,310 ****
  	register int i;
  {
  	if (i < 0 || i >= a->ob_size) {
! 		PyErr_SetString(PyExc_IndexError, "tuple index out of range");
  		return NULL;
  	}
  	Py_INCREF(a->ob_item[i]);
--- 313,324 ----
  	register int i;
  {
  	if (i < 0 || i >= a->ob_size) {
!                 if (a->ob_size) {
!                         PyErr_SetFromRange("tuple",i,a->ob_size);
!                 } else {
!                         PyErr_SetString(PyExc_IndexError,
!                             "cannot get item from empty tuple");
!                 }
  		return NULL;
  	}
  	Py_INCREF(a->ob_item[i]);
diff -c -b --recursive python/dist/src/Objects/unicodeobject.c python-mod/dist/src/Objects/unicodeobject.c
*** python/dist/src/Objects/unicodeobject.c	Sun Jun 11 11:23:16 2000
--- python-mod/dist/src/Objects/unicodeobject.c	Sun Jun 11 11:55:27 2000
***************
*** 3222,3228 ****
  unicode_getitem(PyUnicodeObject *self, int index)
  {
      if (index < 0 || index >= self->length) {
!         PyErr_SetString(PyExc_IndexError, "string index out of range");
          return NULL;
      }
  
--- 3222,3233 ----
  unicode_getitem(PyUnicodeObject *self, int index)
  {
      if (index < 0 || index >= self->length) {
!         if (self->length) {
!                 PyErr_SetFromRange("unicode string",index,self->length);
!         } else {
!                 PyErr_SetString(PyExc_IndexError,
!                     "cannot get item from empty unicode string");
!         }
          return NULL;
      }
  
diff -c -b --recursive python/dist/src/Python/ceval.c python-mod/dist/src/Python/ceval.c
*** python/dist/src/Python/ceval.c	Mon May  8 16:06:50 2000
--- python-mod/dist/src/Python/ceval.c	Sun Jun 11 11:47:38 2000
***************
*** 858,865 ****
  					i += PyList_GET_SIZE(v);
  				if (i < 0 ||
  				    i >= PyList_GET_SIZE(v)) {
  					PyErr_SetString(PyExc_IndexError,
! 						"list index out of range");
  					x = NULL;
  				}
  				else {
--- 858,869 ----
  					i += PyList_GET_SIZE(v);
  				if (i < 0 ||
  				    i >= PyList_GET_SIZE(v)) {
+                                         if (PyList_GET_SIZE(v)) {
+                                             PyErr_SetFromRange("list",i,PyList_GET_SIZE(v));
+                                         } else {
  					PyErr_SetString(PyExc_IndexError,
!                                                 "empty list cannot be subscripted");
!                                         }
  					x = NULL;
  				}
  				else {
diff -c -b --recursive python/dist/src/Python/errors.c python-mod/dist/src/Python/errors.c
*** python/dist/src/Python/errors.c	Tue May  2 21:27:51 2000
--- python-mod/dist/src/Python/errors.c	Sun Jun 11 11:47:38 2000
***************
*** 402,407 ****
--- 402,420 ----
  }
  #endif /* MS_WINDOWS */
  
+ PyObject *
+ PyErr_SetFromRange(name,index,max)
+         const char* name;
+         int         index;
+         int         max;
+ {
+         if (index < 0)
+                 PyErr_Format(PyExc_IndexError,"%s index %d out of range [-%d..-1]",name,index-max,max);
+         else
+                 PyErr_Format(PyExc_IndexError,"%s index %d out of range [0..%d]",name,index,max-1);
+         return PyThreadState_Get()->curexc_value;
+ }
+ 
  void
  PyErr_BadInternalCall()
  {

--------------21F20B013F0333841F36363B--