[Python-checkins] CVS: python/dist/src/Objects abstract.c,2.63,2.64

Tim Peters tim_one@users.sourceforge.net
Fri, 04 May 2001 20:56:39 -0700


Update of /cvsroot/python/python/dist/src/Objects
In directory usw-pr-cvs1:/tmp/cvs-serv17966/python/dist/src/Objects

Modified Files:
	abstract.c 
Log Message:
Generalize tuple() to work nicely with iterators.
NEEDS DOC CHANGES.
This one surprised me!  While I expected tuple() to be a no-brainer, turns
out it's actually dripping with consequences:
1. It will *allow* the popular PySequence_Fast() to work with any iterable
   object (code for that not yet checked in, but should be trivial).
2. It caused two std tests to fail.  This because some places used
   PyTuple_Sequence() (the C spelling of tuple()) as an indirect way to test
   whether something *is* a sequence.  But tuple() code only looked for the
   existence of sq->item to determine that, and e.g. an instance passed
   that test whether or not it supported the other operations tuple()
   needed (e.g., __len__).  So some things the tests *expected* to fail
   with an AttributeError now fail with a TypeError instead.  This looks
   like an improvement to me; e.g., test_coercion used to produce 559
   TypeErrors and 2 AttributeErrors, and now they're all TypeErrors.  The
   error details are more informative too, because the places calling this
   were *looking* for TypeErrors in order to replace the generic tuple()
   "not a sequence" msg with their own more specific text, and
   AttributeErrors snuck by that.


Index: abstract.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Objects/abstract.c,v
retrieving revision 2.63
retrieving revision 2.64
diff -C2 -r2.63 -r2.64
*** abstract.c	2001/05/05 00:14:56	2.63
--- abstract.c	2001/05/05 03:56:37	2.64
***************
*** 1177,1235 ****
  PySequence_Tuple(PyObject *v)
  {
! 	PySequenceMethods *m;
  
  	if (v == NULL)
  		return null_error();
  
  	if (PyTuple_Check(v)) {
  		Py_INCREF(v);
  		return v;
  	}
- 
  	if (PyList_Check(v))
  		return PyList_AsTuple(v);
- 
- 	/* There used to be code for strings here, but tuplifying strings is
- 	   not a common activity, so I nuked it.  Down with code bloat! */
  
! 	/* Generic sequence object */
! 	m = v->ob_type->tp_as_sequence;
! 	if (m && m->sq_item) {
! 		int i;
! 		PyObject *t;
! 		int n = PySequence_Size(v);
! 		if (n < 0)
! 			return NULL;
! 		t = PyTuple_New(n);
! 		if (t == NULL)
! 			return NULL;
! 		for (i = 0; ; i++) {
! 			PyObject *item = (*m->sq_item)(v, i);
! 			if (item == NULL) {
! 				if (PyErr_ExceptionMatches(PyExc_IndexError))
! 					PyErr_Clear();
! 				else {
! 					Py_DECREF(t);
! 					t = NULL;
! 				}
! 				break;
! 			}
! 			if (i >= n) {
! 				if (n < 500)
! 					n += 10;
! 				else
! 					n += 100;
! 				if (_PyTuple_Resize(&t, n, 0) != 0)
! 					break;
! 			}
! 			PyTuple_SET_ITEM(t, i, item);
  		}
! 		if (i < n && t != NULL)
! 			_PyTuple_Resize(&t, i, 0);
! 		return t;
  	}
  
! 	/* None of the above */
! 	return type_error("tuple() argument must be a sequence");
  }
  
--- 1177,1242 ----
  PySequence_Tuple(PyObject *v)
  {
! 	PyObject *it;  /* iter(v) */
! 	int n;         /* guess for result tuple size */
! 	PyObject *result;
! 	int j;
  
  	if (v == NULL)
  		return null_error();
  
+ 	/* Special-case the common tuple and list cases, for efficiency. */
  	if (PyTuple_Check(v)) {
  		Py_INCREF(v);
  		return v;
  	}
  	if (PyList_Check(v))
  		return PyList_AsTuple(v);
  
! 	/* Get iterator. */
! 	it = PyObject_GetIter(v);
! 	if (it == NULL)
! 		return type_error("tuple() argument must support iteration");
! 
! 	/* Guess result size and allocate space. */
! 	n = PySequence_Size(v);
! 	if (n < 0) {
! 		PyErr_Clear();
! 		n = 10;  /* arbitrary */
! 	}
! 	result = PyTuple_New(n);
! 	if (result == NULL)
! 		goto Fail;
! 
! 	/* Fill the tuple. */
! 	for (j = 0; ; ++j) {
! 		PyObject *item = PyIter_Next(it);
! 		if (item == NULL) {
! 			if (PyErr_Occurred())
! 				goto Fail;
! 			break;
! 		}
! 		if (j >= n) {
! 			if (n < 500)
! 				n += 10;
! 			else
! 				n += 100;
! 			if (_PyTuple_Resize(&result, n, 0) != 0)
! 				goto Fail;
  		}
! 		PyTuple_SET_ITEM(result, j, item);
  	}
  
! 	/* Cut tuple back if guess was too large. */
! 	if (j < n &&
! 	    _PyTuple_Resize(&result, j, 0) != 0)
! 		goto Fail;
! 
! 	Py_DECREF(it);
! 	return result;
! 
! Fail:
! 	Py_XDECREF(result);
! 	Py_DECREF(it);
! 	return NULL;
  }