]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-46527: allow calling enumerate(iterable=...) again (GH-30904)
authorJelle Zijlstra <jelle.zijlstra@gmail.com>
Wed, 26 Jan 2022 15:46:48 +0000 (07:46 -0800)
committerGitHub <noreply@github.com>
Wed, 26 Jan 2022 15:46:48 +0000 (00:46 +0900)
Lib/test/test_enumerate.py
Misc/NEWS.d/next/Core and Builtins/2022-01-25-19-34-55.bpo-46527.mQLNPk.rst [new file with mode: 0644]
Objects/enumobject.c

index 906bfc21a26aede0a6a9d9014e95c385b43c53b2..5cb54cff9b76fdd588e08116f35a7777bbfa3385 100644 (file)
@@ -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 (file)
index 0000000..c9fd0ed
--- /dev/null
@@ -0,0 +1,2 @@
+Allow passing ``iterable`` as a keyword argument to :func:`enumerate` again.
+Patch by Jelle Zijlstra.
index 36f592d7c239cf332bdd03ae8a8e50ec4adce1aa..828f1f925a0a1d9a8082d9d6271428df5625aa9d 100644 (file)
@@ -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;