summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_enumerate.py18
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2022-01-25-19-34-55.bpo-46527.mQLNPk.rst2
-rw-r--r--Objects/enumobject.c47
3 files changed, 55 insertions, 12 deletions
diff --git a/Lib/test/test_enumerate.py b/Lib/test/test_enumerate.py
index 906bfc2..5cb54cf 100644
--- a/Lib/test/test_enumerate.py
+++ b/Lib/test/test_enumerate.py
@@ -128,6 +128,18 @@ class EnumerateTestCase(unittest.TestCase, PickleTest):
self.assertRaises(TypeError, self.enum, 'abc', 'a') # wrong type
self.assertRaises(TypeError, self.enum, 'abc', 2, 3) # too many arguments
+ def test_kwargs(self):
+ self.assertEqual(list(self.enum(iterable=Ig(self.seq))), self.res)
+ expected = list(self.enum(Ig(self.seq), 0))
+ self.assertEqual(list(self.enum(iterable=Ig(self.seq), start=0)),
+ expected)
+ self.assertEqual(list(self.enum(start=0, iterable=Ig(self.seq))),
+ expected)
+ self.assertRaises(TypeError, self.enum, iterable=[], x=3)
+ self.assertRaises(TypeError, self.enum, start=0, x=3)
+ self.assertRaises(TypeError, self.enum, x=0, y=3)
+ self.assertRaises(TypeError, self.enum, x=0)
+
@support.cpython_only
def test_tuple_reuse(self):
# Tests an implementation detail where tuple is reused
@@ -266,14 +278,16 @@ class EnumerateStartTestCase(EnumerateTestCase):
class TestStart(EnumerateStartTestCase):
+ def enum(self, iterable, start=11):
+ return enumerate(iterable, start=start)
- enum = lambda self, i: enumerate(i, start=11)
seq, res = 'abc', [(11, 'a'), (12, 'b'), (13, 'c')]
class TestLongStart(EnumerateStartTestCase):
+ def enum(self, iterable, start=sys.maxsize + 1):
+ return enumerate(iterable, start=start)
- enum = lambda self, i: enumerate(i, start=sys.maxsize+1)
seq, res = 'abc', [(sys.maxsize+1,'a'), (sys.maxsize+2,'b'),
(sys.maxsize+3,'c')]
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-01-25-19-34-55.bpo-46527.mQLNPk.rst b/Misc/NEWS.d/next/Core and Builtins/2022-01-25-19-34-55.bpo-46527.mQLNPk.rst
new file mode 100644
index 0000000..c9fd0ed
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2022-01-25-19-34-55.bpo-46527.mQLNPk.rst
@@ -0,0 +1,2 @@
+Allow passing ``iterable`` as a keyword argument to :func:`enumerate` again.
+Patch by Jelle Zijlstra.
diff --git a/Objects/enumobject.c b/Objects/enumobject.c
index 36f592d..828f1f9 100644
--- a/Objects/enumobject.c
+++ b/Objects/enumobject.c
@@ -83,6 +83,18 @@ enum_new_impl(PyTypeObject *type, PyObject *iterable, PyObject *start)
return (PyObject *)en;
}
+static int check_keyword(PyObject *kwnames, int index,
+ const char *name)
+{
+ PyObject *kw = PyTuple_GET_ITEM(kwnames, index);
+ if (!_PyUnicode_EqualToASCIIString(kw, name)) {
+ PyErr_Format(PyExc_TypeError,
+ "'%S' is an invalid keyword argument for enumerate()", kw);
+ return 0;
+ }
+ return 1;
+}
+
// TODO: Use AC when bpo-43447 is supported
static PyObject *
enumerate_vectorcall(PyObject *type, PyObject *const *args,
@@ -91,31 +103,46 @@ enumerate_vectorcall(PyObject *type, PyObject *const *args,
PyTypeObject *tp = _PyType_CAST(type);
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
Py_ssize_t nkwargs = 0;
- if (nargs == 0) {
- PyErr_SetString(PyExc_TypeError,
- "enumerate() missing required argument 'iterable'");
- return NULL;
- }
if (kwnames != NULL) {
nkwargs = PyTuple_GET_SIZE(kwnames);
}
+ // Manually implement enumerate(iterable, start=...)
if (nargs + nkwargs == 2) {
if (nkwargs == 1) {
- PyObject *kw = PyTuple_GET_ITEM(kwnames, 0);
- if (!_PyUnicode_EqualToASCIIString(kw, "start")) {
- PyErr_Format(PyExc_TypeError,
- "'%S' is an invalid keyword argument for enumerate()", kw);
+ if (!check_keyword(kwnames, 0, "start")) {
+ return NULL;
+ }
+ } else if (nkwargs == 2) {
+ PyObject *kw0 = PyTuple_GET_ITEM(kwnames, 0);
+ if (_PyUnicode_EqualToASCIIString(kw0, "start")) {
+ if (!check_keyword(kwnames, 1, "iterable")) {
+ return NULL;
+ }
+ return enum_new_impl(tp, args[1], args[0]);
+ }
+ if (!check_keyword(kwnames, 0, "iterable") ||
+ !check_keyword(kwnames, 1, "start")) {
return NULL;
}
+
}
return enum_new_impl(tp, args[0], args[1]);
}
- if (nargs == 1 && nkwargs == 0) {
+ if (nargs + nkwargs == 1) {
+ if (nkwargs == 1 && !check_keyword(kwnames, 0, "iterable")) {
+ return NULL;
+ }
return enum_new_impl(tp, args[0], NULL);
}
+ if (nargs == 0) {
+ PyErr_SetString(PyExc_TypeError,
+ "enumerate() missing required argument 'iterable'");
+ return NULL;
+ }
+
PyErr_Format(PyExc_TypeError,
"enumerate() takes at most 2 arguments (%d given)", nargs + nkwargs);
return NULL;