implement builtin next() to make it available in Py2
authorStefan Behnel <scoder@users.berlios.de>
Fri, 5 Nov 2010 18:02:35 +0000 (19:02 +0100)
committerStefan Behnel <scoder@users.berlios.de>
Fri, 5 Nov 2010 18:02:35 +0000 (19:02 +0100)
Cython/Compiler/Builtin.py
tests/run/builtin_next.pyx [new file with mode: 0644]

index 096af2e88c85405b58abce4223a67c3bf069518c..bafccd0411e19aee40f10c14f46e6363beb924e4 100644 (file)
@@ -39,6 +39,8 @@ builtin_function_table = [
     #('map',       "",     "",      ""),
     #('max',       "",     "",      ""),
     #('min',       "",     "",      ""),
+    ('next',       "O",    "O",     "__Pyx_PyIter_Next"),   # not available in Py2 => implemented here
+    ('next',      "OO",    "O",     "__Pyx_PyIter_Next2"),  # not available in Py2 => implemented here
     #('oct',       "",     "",      ""),
     # Not worth doing open, when second argument would become mandatory
     #('open',       "ss",   "O",     "PyFile_FromString"),
@@ -158,6 +160,40 @@ proto = """
 #define __Pyx_PyNumber_Power2(a, b) PyNumber_Power(a, b, Py_None)
 """)
 
+iter_next_utility_code = UtilityCode(
+proto = """
+#define __Pyx_PyIter_Next(obj) __Pyx_PyIter_Next2(obj, NULL);
+static CYTHON_INLINE PyObject *__Pyx_PyIter_Next2(PyObject *, PyObject *); /*proto*/
+""",
+# copied from Py3's builtin_next()
+impl = '''
+static CYTHON_INLINE PyObject *__Pyx_PyIter_Next2(PyObject* iterator, PyObject* defval) {
+    PyObject* next;
+    if (unlikely(!PyIter_Check(iterator))) {
+        PyErr_Format(PyExc_TypeError,
+            "%.200s object is not an iterator", iterator->ob_type->tp_name);
+        return NULL;
+    }
+    next = (*iterator->ob_type->tp_iternext)(iterator);
+    if (likely(next)) {
+        return next;
+    } else if (defval) {
+        if (PyErr_Occurred()) {
+            if(!PyErr_ExceptionMatches(PyExc_StopIteration))
+                return NULL;
+            PyErr_Clear();
+        }
+        Py_INCREF(defval);
+        return defval;
+    } else if (PyErr_Occurred()) {
+        return NULL;
+    } else {
+        PyErr_SetNone(PyExc_StopIteration);
+        return NULL;
+    }
+}
+''')
+
 getattr3_utility_code = UtilityCode(
 proto = """
 static PyObject *__Pyx_GetAttr3(PyObject *, PyObject *, PyObject *); /*proto*/
@@ -390,6 +426,7 @@ builtin_utility_code = {
     'exec'      : pyexec_utility_code,
     'getattr3'  : getattr3_utility_code,
     'pow'       : pow2_utility_code,
+    'next'      : iter_next_utility_code,
     'intern'    : intern_utility_code,
     'set'       : py23_set_utility_code,
     'frozenset' : py23_set_utility_code,
diff --git a/tests/run/builtin_next.pyx b/tests/run/builtin_next.pyx
new file mode 100644 (file)
index 0000000..53a75cb
--- /dev/null
@@ -0,0 +1,85 @@
+
+import sys
+IS_PY3 = sys.version_info[0] >= 3
+
+__doc__ = """
+>>> it = iter([1,2,3])
+>>> if not IS_PY3:
+...     next = type(it).next
+>>> next(it)
+1
+>>> next(it)
+2
+>>> next(it)
+3
+
+>>> next(it)
+Traceback (most recent call last):
+StopIteration
+
+>>> next(it)
+Traceback (most recent call last):
+StopIteration
+
+>>> if IS_PY3: next(it, 123)
+... else: print(123)
+123
+"""
+
+def test_single_next(it):
+    """
+    >>> it = iter([1,2,3])
+    >>> test_single_next(it)
+    1
+    >>> test_single_next(it)
+    2
+    >>> test_single_next(it)
+    3
+    >>> test_single_next(it)
+    Traceback (most recent call last):
+    StopIteration
+    >>> test_single_next(it)
+    Traceback (most recent call last):
+    StopIteration
+    """
+    return next(it)
+
+def test_default_next(it, default):
+    """
+    >>> it = iter([1,2,3])
+    >>> test_default_next(it, 99)
+    1
+    >>> test_default_next(it, 99)
+    2
+    >>> test_default_next(it, 99)
+    3
+    >>> test_default_next(it, 99)
+    99
+    >>> test_default_next(it, 99)
+    99
+    """
+    return next(it, default)
+
+def test_next_not_iterable(it):
+    """
+    >>> test_next_not_iterable(123)
+    Traceback (most recent call last):
+    TypeError: int object is not an iterator
+    """
+    return next(it)
+
+def test_next_override(it):
+    """
+    >>> it = iter([1,2,3])
+    >>> test_next_override(it)
+    1
+    >>> test_next_override(it)
+    1
+    >>> test_next_override(it)
+    1
+    >>> test_next_override(it)
+    1
+    """
+    def next(it):
+        return 1
+    return next(it)