Skip to content

Commit ac0c6e1

Browse files
bpo-46527: allow calling enumerate(iterable=...) again (GH-30904)
1 parent 1e8a3a5 commit ac0c6e1

File tree

3 files changed

+55
-12
lines changed

3 files changed

+55
-12
lines changed

Lib/test/test_enumerate.py

+16-2
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,18 @@ def test_argumentcheck(self):
128128
self.assertRaises(TypeError, self.enum, 'abc', 'a') # wrong type
129129
self.assertRaises(TypeError, self.enum, 'abc', 2, 3) # too many arguments
130130

131+
def test_kwargs(self):
132+
self.assertEqual(list(self.enum(iterable=Ig(self.seq))), self.res)
133+
expected = list(self.enum(Ig(self.seq), 0))
134+
self.assertEqual(list(self.enum(iterable=Ig(self.seq), start=0)),
135+
expected)
136+
self.assertEqual(list(self.enum(start=0, iterable=Ig(self.seq))),
137+
expected)
138+
self.assertRaises(TypeError, self.enum, iterable=[], x=3)
139+
self.assertRaises(TypeError, self.enum, start=0, x=3)
140+
self.assertRaises(TypeError, self.enum, x=0, y=3)
141+
self.assertRaises(TypeError, self.enum, x=0)
142+
131143
@support.cpython_only
132144
def test_tuple_reuse(self):
133145
# Tests an implementation detail where tuple is reused
@@ -266,14 +278,16 @@ def test_basicfunction(self):
266278

267279

268280
class TestStart(EnumerateStartTestCase):
281+
def enum(self, iterable, start=11):
282+
return enumerate(iterable, start=start)
269283

270-
enum = lambda self, i: enumerate(i, start=11)
271284
seq, res = 'abc', [(11, 'a'), (12, 'b'), (13, 'c')]
272285

273286

274287
class TestLongStart(EnumerateStartTestCase):
288+
def enum(self, iterable, start=sys.maxsize + 1):
289+
return enumerate(iterable, start=start)
275290

276-
enum = lambda self, i: enumerate(i, start=sys.maxsize+1)
277291
seq, res = 'abc', [(sys.maxsize+1,'a'), (sys.maxsize+2,'b'),
278292
(sys.maxsize+3,'c')]
279293

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Allow passing ``iterable`` as a keyword argument to :func:`enumerate` again.
2+
Patch by Jelle Zijlstra.

Objects/enumobject.c

+37-10
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,18 @@ enum_new_impl(PyTypeObject *type, PyObject *iterable, PyObject *start)
8383
return (PyObject *)en;
8484
}
8585

86+
static int check_keyword(PyObject *kwnames, int index,
87+
const char *name)
88+
{
89+
PyObject *kw = PyTuple_GET_ITEM(kwnames, index);
90+
if (!_PyUnicode_EqualToASCIIString(kw, name)) {
91+
PyErr_Format(PyExc_TypeError,
92+
"'%S' is an invalid keyword argument for enumerate()", kw);
93+
return 0;
94+
}
95+
return 1;
96+
}
97+
8698
// TODO: Use AC when bpo-43447 is supported
8799
static PyObject *
88100
enumerate_vectorcall(PyObject *type, PyObject *const *args,
@@ -91,31 +103,46 @@ enumerate_vectorcall(PyObject *type, PyObject *const *args,
91103
PyTypeObject *tp = _PyType_CAST(type);
92104
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
93105
Py_ssize_t nkwargs = 0;
94-
if (nargs == 0) {
95-
PyErr_SetString(PyExc_TypeError,
96-
"enumerate() missing required argument 'iterable'");
97-
return NULL;
98-
}
99106
if (kwnames != NULL) {
100107
nkwargs = PyTuple_GET_SIZE(kwnames);
101108
}
102109

110+
// Manually implement enumerate(iterable, start=...)
103111
if (nargs + nkwargs == 2) {
104112
if (nkwargs == 1) {
105-
PyObject *kw = PyTuple_GET_ITEM(kwnames, 0);
106-
if (!_PyUnicode_EqualToASCIIString(kw, "start")) {
107-
PyErr_Format(PyExc_TypeError,
108-
"'%S' is an invalid keyword argument for enumerate()", kw);
113+
if (!check_keyword(kwnames, 0, "start")) {
114+
return NULL;
115+
}
116+
} else if (nkwargs == 2) {
117+
PyObject *kw0 = PyTuple_GET_ITEM(kwnames, 0);
118+
if (_PyUnicode_EqualToASCIIString(kw0, "start")) {
119+
if (!check_keyword(kwnames, 1, "iterable")) {
120+
return NULL;
121+
}
122+
return enum_new_impl(tp, args[1], args[0]);
123+
}
124+
if (!check_keyword(kwnames, 0, "iterable") ||
125+
!check_keyword(kwnames, 1, "start")) {
109126
return NULL;
110127
}
128+
111129
}
112130
return enum_new_impl(tp, args[0], args[1]);
113131
}
114132

115-
if (nargs == 1 && nkwargs == 0) {
133+
if (nargs + nkwargs == 1) {
134+
if (nkwargs == 1 && !check_keyword(kwnames, 0, "iterable")) {
135+
return NULL;
136+
}
116137
return enum_new_impl(tp, args[0], NULL);
117138
}
118139

140+
if (nargs == 0) {
141+
PyErr_SetString(PyExc_TypeError,
142+
"enumerate() missing required argument 'iterable'");
143+
return NULL;
144+
}
145+
119146
PyErr_Format(PyExc_TypeError,
120147
"enumerate() takes at most 2 arguments (%d given)", nargs + nkwargs);
121148
return NULL;

0 commit comments

Comments
 (0)