[Python-checkins] CVS: python/dist/src/Python bltinmodule.c,2.201,2.202

Tim Peters tim_one@users.sourceforge.net
Thu, 03 May 2001 16:54:51 -0700


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

Modified Files:
	bltinmodule.c 
Log Message:
Generalize map() to work with iterators.
NEEDS DOC CHANGES.
Possibly contentious:  The first time s.next() yields StopIteration (for
a given map argument s) is the last time map() *tries* s.next().  That
is, if other sequence args are longer, s will never again contribute
anything but None values to the result, even if trying s.next() again
could yield another result.  This is the same behavior map() used to have
wrt IndexError, so it's the only way to be wholly backward-compatible.
I'm not a fan of letting StopIteration mean "try again later" anyway.


Index: bltinmodule.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Python/bltinmodule.c,v
retrieving revision 2.201
retrieving revision 2.202
diff -C2 -r2.201 -r2.202
*** bltinmodule.c	2001/05/03 07:00:32	2.201
--- bltinmodule.c	2001/05/03 23:54:49	2.202
***************
*** 937,943 ****
  {
  	typedef struct {
! 		PyObject *seq;
! 		PySequenceMethods *sqf;
! 		int saw_IndexError;
  	} sequence;
  
--- 937,942 ----
  {
  	typedef struct {
! 		PyObject *it;	/* the iterator object */
! 		int saw_StopIteration;  /* bool:  did the iterator end? */
  	} sequence;
  
***************
*** 962,991 ****
  	}
  
  	if ((seqs = PyMem_NEW(sequence, n)) == NULL) {
  		PyErr_NoMemory();
! 		goto Fail_2;
  	}
  
! 	/* Do a first pass to (a) verify the args are sequences; (b) set
! 	 * len to the largest of their lengths; (c) initialize the seqs
! 	 * descriptor vector.
  	 */
! 	for (len = 0, i = 0, sqp = seqs; i < n; ++i, ++sqp) {
  		int curlen;
- 		PySequenceMethods *sqf;
- 
- 		if ((sqp->seq = PyTuple_GetItem(args, i + 1)) == NULL)
- 			goto Fail_2;
- 
- 		sqp->saw_IndexError = 0;
  
! 		sqp->sqf = sqf = sqp->seq->ob_type->tp_as_sequence;
! 		if (sqf == NULL ||
! 		    sqf->sq_item == NULL)
! 		{
  			static char errmsg[] =
! 			    "argument %d to map() must be a sequence object";
  			char errbuf[sizeof(errmsg) + 25];
- 
  			sprintf(errbuf, errmsg, i+2);
  			PyErr_SetString(PyExc_TypeError, errbuf);
--- 961,991 ----
  	}
  
+ 	/* Get space for sequence descriptors.  Must NULL out the iterator
+ 	 * pointers so that jumping to Fail_2 later doesn't see trash.
+ 	 */
  	if ((seqs = PyMem_NEW(sequence, n)) == NULL) {
  		PyErr_NoMemory();
! 		return NULL;
! 	}
! 	for (i = 0; i < n; ++i) {
! 		seqs[i].it = (PyObject*)NULL;
! 		seqs[i].saw_StopIteration = 0;
  	}
  
! 	/* Do a first pass to obtain iterators for the arguments, and set len
! 	 * to the largest of their lengths.
  	 */
! 	len = 0;
! 	for (i = 0, sqp = seqs; i < n; ++i, ++sqp) {
! 		PyObject *curseq;
  		int curlen;
  
! 		/* Get iterator. */
! 		curseq = PyTuple_GetItem(args, i+1);
! 		sqp->it = PyObject_GetIter(curseq);
! 		if (sqp->it == NULL) {
  			static char errmsg[] =
! 			    "argument %d to map() must support iteration";
  			char errbuf[sizeof(errmsg) + 25];
  			sprintf(errbuf, errmsg, i+2);
  			PyErr_SetString(PyExc_TypeError, errbuf);
***************
*** 993,1057 ****
  		}
  
! 		if (sqf->sq_length == NULL)
! 			/* doesn't matter -- make something up */
! 			curlen = 8;
! 		else
! 			curlen = (*sqf->sq_length)(sqp->seq);
  		if (curlen < 0)
! 			goto Fail_2;
  		if (curlen > len)
  			len = curlen;
  	}
  
  	if ((result = (PyObject *) PyList_New(len)) == NULL)
  		goto Fail_2;
  
! 	/* Iterate over the sequences until all have raised IndexError. */
  	for (i = 0; ; ++i) {
  		PyObject *alist, *item=NULL, *value;
! 		int any = 0;
  
  		if (func == Py_None && n == 1)
  			alist = NULL;
! 		else {
! 			if ((alist = PyTuple_New(n)) == NULL)
! 				goto Fail_1;
! 		}
  
  		for (j = 0, sqp = seqs; j < n; ++j, ++sqp) {
! 			if (sqp->saw_IndexError) {
  				Py_INCREF(Py_None);
  				item = Py_None;
  			}
  			else {
! 				item = (*sqp->sqf->sq_item)(sqp->seq, i);
! 				if (item == NULL) {
! 					if (PyErr_ExceptionMatches(
! 						PyExc_IndexError))
! 					{
! 						PyErr_Clear();
! 						Py_INCREF(Py_None);
! 						item = Py_None;
! 						sqp->saw_IndexError = 1;
! 					}
! 					else {
! 						goto Fail_0;
  					}
  				}
- 				else
- 					any = 1;
  
  			}
! 			if (!alist)
  				break;
- 			if (PyTuple_SetItem(alist, j, item) < 0) {
- 				Py_DECREF(item);
- 				goto Fail_0;
- 			}
- 			continue;
- 
- 		Fail_0:
- 			Py_XDECREF(alist);
- 			goto Fail_1;
  		}
  
--- 993,1057 ----
  		}
  
! 		/* Update len. */
! 		curlen = -1;  /* unknown */
! 		if (PySequence_Check(curseq) &&
! 		    curseq->ob_type->tp_as_sequence->sq_length) {
! 			curlen = PySequence_Size(curseq);
! 			if (curlen < 0)
! 				PyErr_Clear();
! 		}
  		if (curlen < 0)
! 			curlen = 8;  /* arbitrary */
  		if (curlen > len)
  			len = curlen;
  	}
  
+ 	/* Get space for the result list. */
  	if ((result = (PyObject *) PyList_New(len)) == NULL)
  		goto Fail_2;
  
! 	/* Iterate over the sequences until all have stopped. */
  	for (i = 0; ; ++i) {
  		PyObject *alist, *item=NULL, *value;
! 		int numactive = 0;
  
  		if (func == Py_None && n == 1)
  			alist = NULL;
! 		else if ((alist = PyTuple_New(n)) == NULL)
! 			goto Fail_1;
  
  		for (j = 0, sqp = seqs; j < n; ++j, ++sqp) {
! 			if (sqp->saw_StopIteration) {
  				Py_INCREF(Py_None);
  				item = Py_None;
  			}
  			else {
! 				item = PyIter_Next(sqp->it);
! 				if (item)
! 					++numactive;
! 				else {
! 					/* StopIteration is *implied* by a
! 					 * NULL return from PyIter_Next() if
! 					 * PyErr_Occurred() is false.
! 					 */
! 					if (PyErr_Occurred()) {
! 						if (PyErr_ExceptionMatches(
! 						    PyExc_StopIteration))
! 							PyErr_Clear();
! 						else {
! 							Py_XDECREF(alist);
! 							goto Fail_1;
! 						}
  					}
+ 					Py_INCREF(Py_None);
+ 					item = Py_None;
+ 					sqp->saw_StopIteration = 1;
  				}
  
  			}
! 			if (alist)
! 				PyTuple_SET_ITEM(alist, j, item);
! 			else
  				break;
  		}
  
***************
*** 1059,1063 ****
  			alist = item;
  
! 		if (!any) {
  			Py_DECREF(alist);
  			break;
--- 1059,1063 ----
  			alist = item;
  
! 		if (numactive == 0) {
  			Py_DECREF(alist);
  			break;
***************
*** 1077,1085 ****
  			if (status < 0)
  				goto Fail_1;
- 		}
- 		else {
- 			if (PyList_SetItem(result, i, value) < 0)
- 				goto Fail_1;
  		}
  	}
  
--- 1077,1083 ----
  			if (status < 0)
  				goto Fail_1;
  		}
+ 		else if (PyList_SetItem(result, i, value) < 0)
+ 		 	goto Fail_1;
  	}
  
***************
*** 1087,1098 ****
  		goto Fail_1;
  
! 	PyMem_DEL(seqs);
! 	return result;
  
  Fail_1:
  	Py_DECREF(result);
  Fail_2:
! 	if (seqs) PyMem_DEL(seqs);
! 	return NULL;
  }
  
--- 1085,1100 ----
  		goto Fail_1;
  
! 	goto Succeed;
  
  Fail_1:
  	Py_DECREF(result);
  Fail_2:
! 	result = NULL;
! Succeed:
! 	assert(seqs);
! 	for (i = 0; i < n; ++i)
! 		Py_XDECREF(seqs[i].it);
! 	PyMem_DEL(seqs);
! 	return result;
  }