From a70b19147fd163744be34745d393af7be603629f Mon Sep 17 00:00:00 2001
From: Alex Martelli <aleaxit@gmail.com>
Date: Tue, 22 Apr 2003 08:12:33 +0000
Subject: Adding new built-in function sum, with docs and tests.

---
 Doc/lib/libfuncs.tex     |  8 +++++++
 Doc/tut/tut.tex          |  5 +++-
 Lib/test/test_builtin.py | 21 +++++++++++++++++
 Misc/NEWS                |  4 ++++
 Python/bltinmodule.c     | 61 ++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 98 insertions(+), 1 deletion(-)

diff --git a/Doc/lib/libfuncs.tex b/Doc/lib/libfuncs.tex
index 323a516..50f22f8 100644
--- a/Doc/lib/libfuncs.tex
+++ b/Doc/lib/libfuncs.tex
@@ -897,6 +897,14 @@ class C:
   \versionadded{2.2}
 \end{funcdesc}
 
+\begin{funcdesc}{sum}{sequence\optional{start=0}}
+  Sums up the items of a \var{sequence}, from left to right, and returns
+  the total.  The \var{sequence}'s items are normally numbers, and are
+  not allowed to be strings.  The fast, correct way to join up a
+  sequence of strings is by calling \code{''.join(\var{sequence})}.
+  \versionadded{2.3}
+\end{funcdesc}
+
 \begin{funcdesc}{super}{type\optional{object-or-type}}
   Return the superclass of \var{type}.  If the second argument is omitted
   the super object returned is unbound.  If the second argument is an
diff --git a/Doc/tut/tut.tex b/Doc/tut/tut.tex
index 71d3f60..b227576 100644
--- a/Doc/tut/tut.tex
+++ b/Doc/tut/tut.tex
@@ -1819,6 +1819,9 @@ item, then to the result and the next item, and so on.  For example,
 0
 \end{verbatim}
 
+Don't use this example's definition of \code{sum}: since summing numbers
+is such a common need, a built-in function \code{sum(\var{sequence})} is 
+already provided, and works exactly like this\versionadded{2,3}.
 
 \subsection{List Comprehensions}
 
@@ -2502,7 +2505,7 @@ standard module \module{__builtin__}\refbimodindex{__builtin__}:
  'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'min',
  'object', 'oct', 'open', 'ord', 'pow', 'property', 'quit',
  'range', 'raw_input', 'reduce', 'reload', 'repr', 'round',
- 'setattr', 'slice', 'staticmethod', 'str', 'string', 'super',
+ 'setattr', 'slice', 'staticmethod', 'str', 'string', 'sum', 'super',
  'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip']
 \end{verbatim}
 
diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py
index 3956214..e5c9770 100644
--- a/Lib/test/test_builtin.py
+++ b/Lib/test/test_builtin.py
@@ -1099,6 +1099,27 @@ class BuiltinTest(unittest.TestCase):
         a[0] = a
         self.assertEqual(str(a), '{0: {...}}')
 
+    def test_sum(self):
+        self.assertEqual(sum([]), 0)
+        self.assertEqual(sum(range(2,8)), 27)
+        self.assertEqual(sum(iter(range(2,8))), 27)
+        self.assertEqual(sum(Squares(10)), 285)
+        self.assertEqual(sum(iter(Squares(10))), 285)
+        self.assertEqual(sum([[1], [2], [3]], []), [1, 2, 3])
+
+        self.assertRaises(TypeError, sum)
+        self.assertRaises(TypeError, sum, 42)
+        self.assertRaises(TypeError, sum, ['a', 'b', 'c'])
+        self.assertRaises(TypeError, sum, ['a', 'b', 'c'], '')
+        self.assertRaises(TypeError, sum, [[1], [2], [3]])
+        self.assertRaises(TypeError, sum, [{2:3}])
+        self.assertRaises(TypeError, sum, [{2:3}]*2, {2:3})
+
+        class BadSeq:
+            def __getitem__(self, index):
+                raise ValueError
+        self.assertRaises(ValueError, sum, BadSeq())
+
     def test_tuple(self):
         self.assertEqual(tuple(()), ())
         t0_3 = (0, 1, 2, 3)
diff --git a/Misc/NEWS b/Misc/NEWS
index 2897793..330831d 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,10 @@ What's New in Python 2.3 beta 1?
 Core and builtins
 -----------------
 
+- New builtin function sum(seq, start=0) returns the sum of all the
+  items in iterable object seq, plus start (items are normally numbers,
+  and cannot be strings).
+
 - bool() called without arguments now returns False rather than
   raising an exception.  This is consistent with calling the
   constructors for the other builtin types -- called without argument
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c
index adefba2..0eb8357 100644
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -1798,6 +1798,66 @@ PyDoc_STRVAR(vars_doc,
 Without arguments, equivalent to locals().\n\
 With an argument, equivalent to object.__dict__.");
 
+
+static PyObject*
+builtin_sum(PyObject *self, PyObject *args)
+{
+	PyObject *seq;
+	PyObject *result = NULL;
+	PyObject *temp, *item, *iter;
+
+	if (!PyArg_ParseTuple(args, "O|O:sum", &seq, &result))
+		return NULL;
+
+	iter = PyObject_GetIter(seq);
+	if (iter == NULL)
+		return NULL;
+
+	if (result == NULL) {
+		result = PyInt_FromLong(0);
+		if (result == NULL) {
+			Py_DECREF(iter);
+			return NULL;
+		}
+	} else {
+		/* reject string values for 'start' parameter */
+		if (PyObject_TypeCheck(result, &PyBaseString_Type)) {
+			PyErr_SetString(PyExc_TypeError,
+				"can't sum strings [use ''.join(seq) instead]");
+			Py_DECREF(result);
+			Py_DECREF(iter);
+			return NULL;
+		}
+	}
+
+	for(;;) {
+		item = PyIter_Next(iter);
+		if (item == NULL) {
+			/* error, or end-of-sequence */
+			if (PyErr_Occurred()) {
+				Py_DECREF(result);
+				result = NULL;
+			}
+			break;
+		}
+		temp = PyNumber_Add(result, item);
+		Py_DECREF(result);
+		Py_DECREF(item);
+		result = temp;
+		if (result == NULL)
+			break;
+	}
+	Py_DECREF(iter);
+	return result;
+}
+
+PyDoc_STRVAR(sum_doc,
+"sum(sequence, start=0) -> value\n\
+\n\
+Returns the sum of a sequence of numbers (NOT strings) plus the value\n\
+of parameter 'start'.  When the sequence is empty, returns start.");
+
+
 static PyObject *
 builtin_isinstance(PyObject *self, PyObject *args)
 {
@@ -2001,6 +2061,7 @@ static PyMethodDef builtin_methods[] = {
  	{"repr",	builtin_repr,       METH_O, repr_doc},
  	{"round",	builtin_round,      METH_VARARGS, round_doc},
  	{"setattr",	builtin_setattr,    METH_VARARGS, setattr_doc},
+ 	{"sum",		builtin_sum,        METH_VARARGS, sum_doc},
 #ifdef Py_USING_UNICODE
  	{"unichr",	builtin_unichr,     METH_VARARGS, unichr_doc},
 #endif
-- 
cgit v0.12

