Skip to content

Commit 7dab242

Browse files
committed
- New builtin function enumerate(x), from PEP 279. Example:
enumerate("abc") is an iterator returning (0,"a"), (1,"b"), (2,"c"). The argument can be an arbitrary iterable object.
1 parent 17afa13 commit 7dab242

File tree

7 files changed

+281
-0
lines changed

7 files changed

+281
-0
lines changed

Include/Python.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
#include "tupleobject.h"
9292
#include "listobject.h"
9393
#include "dictobject.h"
94+
#include "enumobject.h"
9495
#include "methodobject.h"
9596
#include "moduleobject.h"
9697
#include "funcobject.h"

Include/enumobject.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#ifndef Py_ENUMOBJECT_H
2+
#define Py_ENUMOBJECT_H
3+
4+
/* Enumerate Object */
5+
6+
#ifdef __cplusplus
7+
extern "C" {
8+
#endif
9+
10+
extern DL_IMPORT(PyTypeObject) PyEnum_Type;
11+
12+
#ifdef __cplusplus
13+
}
14+
#endif
15+
16+
#endif /* !Py_ENUMOBJECT_H */

Lib/test/test_enumerate.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
from __future__ import generators
2+
import unittest
3+
4+
import test_support
5+
6+
seq, res = 'abc', [(0,'a'), (1,'b'), (2,'c')]
7+
8+
class G:
9+
'Sequence using __getitem__'
10+
def __init__(self, seqn):
11+
self.seqn = seqn
12+
def __getitem__(self, i):
13+
return self.seqn[i]
14+
15+
class I:
16+
'Sequence using iterator protocol'
17+
def __init__(self, seqn):
18+
self.seqn = seqn
19+
self.i = 0
20+
def __iter__(self):
21+
return self
22+
def next(self):
23+
if self.i >= len(self.seqn): raise StopIteration
24+
v = self.seqn[self.i]
25+
self.i += 1
26+
return v
27+
28+
class Ig:
29+
'Sequence using iterator protocol defined with a generator'
30+
def __init__(self, seqn):
31+
self.seqn = seqn
32+
self.i = 0
33+
def __iter__(self):
34+
for val in self.seqn:
35+
yield val
36+
37+
class X:
38+
'Missing __getitem__ and __iter__'
39+
def __init__(self, seqn):
40+
self.seqn = seqn
41+
self.i = 0
42+
def next(self):
43+
if self.i >= len(self.seqn): raise StopIteration
44+
v = self.seqn[self.i]
45+
self.i += 1
46+
return v
47+
48+
class E:
49+
'Test propagation of exceptions'
50+
def __init__(self, seqn):
51+
self.seqn = seqn
52+
self.i = 0
53+
def __iter__(self):
54+
return self
55+
def next(self):
56+
3/0
57+
58+
class N:
59+
'Iterator missing next()'
60+
def __init__(self, seqn):
61+
self.seqn = seqn
62+
self.i = 0
63+
def __iter__(self):
64+
return self
65+
66+
class EnumerateTestCase(unittest.TestCase):
67+
68+
enum = enumerate
69+
70+
def test_basicfunction(self):
71+
self.assertEqual(type(self.enum(seq)), self.enum)
72+
e = self.enum(seq)
73+
self.assertEqual(iter(e), e)
74+
self.assertEqual(list(self.enum(seq)), res)
75+
self.enum.__doc__
76+
77+
def test_getitemseqn(self):
78+
self.assertEqual(list(self.enum(G(seq))), res)
79+
e = self.enum(G(''))
80+
self.assertRaises(StopIteration, e.next)
81+
82+
def test_iteratorseqn(self):
83+
self.assertEqual(list(self.enum(I(seq))), res)
84+
e = self.enum(I(''))
85+
self.assertRaises(StopIteration, e.next)
86+
87+
def test_iteratorgenerator(self):
88+
self.assertEqual(list(self.enum(Ig(seq))), res)
89+
e = self.enum(Ig(''))
90+
self.assertRaises(StopIteration, e.next)
91+
92+
def test_noniterable(self):
93+
self.assertRaises(TypeError, self.enum, X(seq))
94+
95+
def test_illformediterable(self):
96+
self.assertRaises(TypeError, list, self.enum(N(seq)))
97+
98+
def test_exception_propagation(self):
99+
self.assertRaises(ZeroDivisionError, list, self.enum(E(seq)))
100+
101+
class MyEnum(enumerate):
102+
pass
103+
104+
class SubclassTestCase(EnumerateTestCase):
105+
106+
enum = MyEnum
107+
108+
def suite():
109+
suite = unittest.TestSuite()
110+
suite.addTest(unittest.makeSuite(EnumerateTestCase))
111+
suite.addTest(unittest.makeSuite(SubclassTestCase))
112+
return suite
113+
114+
def test_main():
115+
test_support.run_suite(suite())
116+
117+
if __name__ == "__main__":
118+
test_main()

Makefile.pre.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ OBJECT_OBJS= \
257257
Objects/cobject.o \
258258
Objects/complexobject.o \
259259
Objects/descrobject.o \
260+
Objects/enumobject.o \
260261
Objects/fileobject.o \
261262
Objects/floatobject.o \
262263
Objects/frameobject.o \
@@ -443,6 +444,7 @@ PYTHON_HEADERS= \
443444
Include/complexobject.h \
444445
Include/descrobject.h \
445446
Include/dictobject.h \
447+
Include/enumobject.h \
446448
Include/fileobject.h \
447449
Include/floatobject.h \
448450
Include/funcobject.h \

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ Type/class unification and new-style classes
66

77
Core and builtins
88

9+
- New builtin function enumerate(x), from PEP 279. Example:
10+
enumerate("abc") is an iterator returning (0,"a"), (1,"b"), (2,"c").
11+
The argument can be an arbitrary iterable object.
12+
913
- The assert statement no longer tests __debug__ at runtime. This means
1014
that assert statements cannot be disabled by assigning a false value
1115
to __debug__.

Objects/enumobject.c

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/* enumerate object */
2+
3+
#include "Python.h"
4+
5+
typedef struct {
6+
PyObject_HEAD
7+
long en_index; /* current index of enumeration */
8+
PyObject* en_sit; /* secondary iterator of enumeration */
9+
} enumobject;
10+
11+
PyTypeObject PyEnum_Type;
12+
13+
static PyObject *
14+
enum_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
15+
{
16+
enumobject *en;
17+
PyObject *seq = NULL;
18+
static char *kwlist[] = {"sequence", 0};
19+
20+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:enumerate", kwlist,
21+
&seq))
22+
return NULL;
23+
24+
en = (enumobject *)type->tp_alloc(type, 0);
25+
if (en == NULL)
26+
return NULL;
27+
en->en_index = 0;
28+
en->en_sit = PyObject_GetIter(seq);
29+
if (en->en_sit == NULL) {
30+
Py_DECREF(en);
31+
return NULL;
32+
}
33+
return (PyObject *)en;
34+
}
35+
36+
static void
37+
enum_dealloc(enumobject *en)
38+
{
39+
PyObject_GC_UnTrack(en);
40+
Py_XDECREF(en->en_sit);
41+
en->ob_type->tp_free(en);
42+
}
43+
44+
static int
45+
enum_traverse(enumobject *en, visitproc visit, void *arg)
46+
{
47+
if (en->en_sit)
48+
return visit(en->en_sit, arg);
49+
return 0;
50+
}
51+
52+
static PyObject *
53+
enum_next(enumobject *en)
54+
{
55+
PyObject *result;
56+
PyObject *next_index;
57+
58+
PyObject *next_item = PyIter_Next(en->en_sit);
59+
if (next_item == NULL)
60+
return NULL;
61+
62+
result = PyTuple_New(2);
63+
if (result == NULL) {
64+
Py_DECREF(next_item);
65+
return NULL;
66+
}
67+
68+
next_index = PyInt_FromLong(en->en_index++);
69+
if (next_index == NULL) {
70+
Py_DECREF(next_item);
71+
Py_DECREF(result);
72+
return NULL;
73+
}
74+
75+
PyTuple_SET_ITEM(result, 0, next_index);
76+
PyTuple_SET_ITEM(result, 1, next_item);
77+
return result;
78+
}
79+
80+
static PyObject *
81+
enum_getiter(PyObject *en)
82+
{
83+
Py_INCREF(en);
84+
return en;
85+
}
86+
87+
static PyMethodDef enum_methods[] = {
88+
{"next", (PyCFunction)enum_next, METH_NOARGS,
89+
"return the next (index, value) pair, or raise StopIteration"},
90+
{NULL, NULL} /* sentinel */
91+
};
92+
93+
static char enum_doc[] =
94+
"enumerate(iterable) -> create an enumerating-iterator";
95+
96+
PyTypeObject PyEnum_Type = {
97+
PyObject_HEAD_INIT(&PyType_Type)
98+
0, /* ob_size */
99+
"enumerate", /* tp_name */
100+
sizeof(enumobject), /* tp_basicsize */
101+
0, /* tp_itemsize */
102+
/* methods */
103+
(destructor)enum_dealloc, /* tp_dealloc */
104+
0, /* tp_print */
105+
0, /* tp_getattr */
106+
0, /* tp_setattr */
107+
0, /* tp_compare */
108+
0, /* tp_repr */
109+
0, /* tp_as_number */
110+
0, /* tp_as_sequence */
111+
0, /* tp_as_mapping */
112+
0, /* tp_hash */
113+
0, /* tp_call */
114+
0, /* tp_str */
115+
PyObject_GenericGetAttr, /* tp_getattro */
116+
0, /* tp_setattro */
117+
0, /* tp_as_buffer */
118+
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
119+
Py_TPFLAGS_BASETYPE, /* tp_flags */
120+
enum_doc, /* tp_doc */
121+
(traverseproc)enum_traverse, /* tp_traverse */
122+
0, /* tp_clear */
123+
0, /* tp_richcompare */
124+
0, /* tp_weaklistoffset */
125+
(getiterfunc)enum_getiter, /* tp_iter */
126+
(iternextfunc)enum_next, /* tp_iternext */
127+
enum_methods, /* tp_methods */
128+
0, /* tp_members */
129+
0, /* tp_getset */
130+
0, /* tp_base */
131+
0, /* tp_dict */
132+
0, /* tp_descr_get */
133+
0, /* tp_descr_set */
134+
0, /* tp_dictoffset */
135+
0, /* tp_init */
136+
PyType_GenericAlloc, /* tp_alloc */
137+
enum_new, /* tp_new */
138+
PyObject_GC_Del, /* tp_free */
139+
};

Python/bltinmodule.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1864,6 +1864,7 @@ _PyBuiltin_Init(void)
18641864
SETBUILTIN("complex", &PyComplex_Type);
18651865
#endif
18661866
SETBUILTIN("dict", &PyDict_Type);
1867+
SETBUILTIN("enumerate", &PyEnum_Type);
18671868
SETBUILTIN("float", &PyFloat_Type);
18681869
SETBUILTIN("property", &PyProperty_Type);
18691870
SETBUILTIN("int", &PyInt_Type);

0 commit comments

Comments
 (0)