From 9ff761fa58d90f71c8d5aa02f430782c20dd977b Mon Sep 17 00:00:00 2001 From: Oren Milman Date: Fri, 18 Aug 2017 10:20:43 +0300 Subject: [PATCH 1/4] init commit --- Python/getargs.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Python/getargs.c b/Python/getargs.c index 471f72924f6e227..8206dd490515b05 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -1655,10 +1655,11 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, nkwargs = (kwargs == NULL) ? 0 : PyDict_GET_SIZE(kwargs); if (nargs + nkwargs > len) { PyErr_Format(PyExc_TypeError, - "%.200s%s takes at most %d argument%s (%zd given)", + "%.200s%s takes at most %d %sargument%s (%zd given)", (fname == NULL) ? "function" : fname, (fname == NULL) ? "" : "()", len, + (nargs == 0) ? "keyword " : "", (len == 1) ? "" : "s", nargs + nkwargs); return cleanreturn(0, &freelist); @@ -2078,10 +2079,11 @@ vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs, } if (nargs + nkwargs > len) { PyErr_Format(PyExc_TypeError, - "%.200s%s takes at most %d argument%s (%zd given)", + "%.200s%s takes at most %d %sargument%s (%zd given)", (parser->fname == NULL) ? "function" : parser->fname, (parser->fname == NULL) ? "" : "()", len, + (nargs == 0) ? "keyword " : "", (len == 1) ? "" : "s", nargs + nkwargs); return cleanreturn(0, &freelist); From dec733446a7f3c704178cbeebf821d66e913d256 Mon Sep 17 00:00:00 2001 From: Oren Milman Date: Fri, 18 Aug 2017 19:54:30 +0300 Subject: [PATCH 2/4] added comments referring to the issue, and tests. --- Lib/test/test_call.py | 19 +++++++++++++++++++ Python/getargs.c | 4 ++++ 2 files changed, 23 insertions(+) diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index b004b5803c30bf6..3f669e8705b8569 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -7,6 +7,7 @@ _testcapi = None import struct import collections +import itertools # The test cases here cover several paths through the function calling # code. They depend on the METH_XXX flag that is used to define a C @@ -194,6 +195,24 @@ def test_varargs13_kw(self): msg = r"^classmethod\(\) takes no keyword arguments$" self.assertRaisesRegex(TypeError, msg, classmethod, func=id) + def test_varargs14_kw(self): + msg = r"^product\(\) takes at most 1 keyword argument \(2 given\)$" + self.assertRaisesRegex(TypeError, msg, itertools.product, 0, a=1, b=2) + + def test_varargs15_kw(self): + msg = r"^ImportError\(\) takes at most 2 keyword arguments \(3 given\)$" + self.assertRaisesRegex(TypeError, msg, ImportError, 0, a=1, b=2, c=3) + + def test_varargs16_kw(self): + msg = r"^function takes at most 2 keyword arguments \(3 given\)$" + self.assertRaisesRegex(TypeError, msg, min, 0, a=1, b=2, c=3) + self.assertRaisesRegex(TypeError, msg, max, 0, a=1, b=2, c=3) + + def test_varargs17_kw(self): + msg = r"^print\(\) takes at most 4 keyword arguments \(5 given\)$" + self.assertRaisesRegex(TypeError, msg, + print, 0, a=1, b=2, c=3, d=4, e=5) + def test_oldargs0_1(self): msg = r"keys\(\) takes no arguments \(1 given\)" self.assertRaisesRegex(TypeError, msg, {}.keys, 0) diff --git a/Python/getargs.c b/Python/getargs.c index 8206dd490515b05..063a5afe2b6abe4 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -1654,6 +1654,8 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, nargs = PyTuple_GET_SIZE(args); nkwargs = (kwargs == NULL) ? 0 : PyDict_GET_SIZE(kwargs); if (nargs + nkwargs > len) { + /* adding "keyword" (when nargs == 0) prevents producing wrong error + messages in some special cases (see bpo-31229). */ PyErr_Format(PyExc_TypeError, "%.200s%s takes at most %d %sargument%s (%zd given)", (fname == NULL) ? "function" : fname, @@ -2078,6 +2080,8 @@ vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs, nkwargs = 0; } if (nargs + nkwargs > len) { + /* adding "keyword" (when nargs == 0) prevents producing wrong error + messages in some special cases (see bpo-31229). */ PyErr_Format(PyExc_TypeError, "%.200s%s takes at most %d %sargument%s (%zd given)", (parser->fname == NULL) ? "function" : parser->fname, From 2fad3a5dd076684f8627c70a4416539f776790de Mon Sep 17 00:00:00 2001 From: Oren Milman Date: Tue, 22 Aug 2017 15:09:37 +0300 Subject: [PATCH 3/4] fix tests to fit the changes from bpo-31236 --- Lib/test/test_call.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index 3f669e8705b8569..116d1eaec6fa3fc 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -204,11 +204,14 @@ def test_varargs15_kw(self): self.assertRaisesRegex(TypeError, msg, ImportError, 0, a=1, b=2, c=3) def test_varargs16_kw(self): - msg = r"^function takes at most 2 keyword arguments \(3 given\)$" + msg = r"^min\(\) takes at most 2 keyword arguments \(3 given\)$" self.assertRaisesRegex(TypeError, msg, min, 0, a=1, b=2, c=3) - self.assertRaisesRegex(TypeError, msg, max, 0, a=1, b=2, c=3) def test_varargs17_kw(self): + msg = r"^max\(\) takes at most 2 keyword arguments \(3 given\)$" + self.assertRaisesRegex(TypeError, msg, max, 0, a=1, b=2, c=3) + + def test_varargs18_kw(self): msg = r"^print\(\) takes at most 4 keyword arguments \(5 given\)$" self.assertRaisesRegex(TypeError, msg, print, 0, a=1, b=2, c=3, d=4, e=5) From c9d6d8dd0ae6e5fa6cb7871c3ca52955e336524a Mon Sep 17 00:00:00 2001 From: Oren Milman Date: Tue, 22 Aug 2017 21:20:13 +0300 Subject: [PATCH 4/4] update according to Serhiy's review --- Lib/test/test_call.py | 15 +++++++-------- Python/getargs.c | 4 ++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index 116d1eaec6fa3fc..3f9987c9e777f17 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -197,24 +197,23 @@ def test_varargs13_kw(self): def test_varargs14_kw(self): msg = r"^product\(\) takes at most 1 keyword argument \(2 given\)$" - self.assertRaisesRegex(TypeError, msg, itertools.product, 0, a=1, b=2) + self.assertRaisesRegex(TypeError, msg, + itertools.product, 0, repeat=1, foo=2) def test_varargs15_kw(self): msg = r"^ImportError\(\) takes at most 2 keyword arguments \(3 given\)$" - self.assertRaisesRegex(TypeError, msg, ImportError, 0, a=1, b=2, c=3) + self.assertRaisesRegex(TypeError, msg, + ImportError, 0, name=1, path=2, foo=3) def test_varargs16_kw(self): msg = r"^min\(\) takes at most 2 keyword arguments \(3 given\)$" - self.assertRaisesRegex(TypeError, msg, min, 0, a=1, b=2, c=3) + self.assertRaisesRegex(TypeError, msg, + min, 0, default=1, key=2, foo=3) def test_varargs17_kw(self): - msg = r"^max\(\) takes at most 2 keyword arguments \(3 given\)$" - self.assertRaisesRegex(TypeError, msg, max, 0, a=1, b=2, c=3) - - def test_varargs18_kw(self): msg = r"^print\(\) takes at most 4 keyword arguments \(5 given\)$" self.assertRaisesRegex(TypeError, msg, - print, 0, a=1, b=2, c=3, d=4, e=5) + print, 0, sep=1, end=2, file=3, flush=4, foo=5) def test_oldargs0_1(self): msg = r"keys\(\) takes no arguments \(1 given\)" diff --git a/Python/getargs.c b/Python/getargs.c index ca33e0ede3653fb..4b969d924a96da6 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -1654,7 +1654,7 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, nargs = PyTuple_GET_SIZE(args); nkwargs = (kwargs == NULL) ? 0 : PyDict_GET_SIZE(kwargs); if (nargs + nkwargs > len) { - /* adding "keyword" (when nargs == 0) prevents producing wrong error + /* Adding "keyword" (when nargs == 0) prevents producing wrong error messages in some special cases (see bpo-31229). */ PyErr_Format(PyExc_TypeError, "%.200s%s takes at most %d %sargument%s (%zd given)", @@ -2080,7 +2080,7 @@ vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs, nkwargs = 0; } if (nargs + nkwargs > len) { - /* adding "keyword" (when nargs == 0) prevents producing wrong error + /* Adding "keyword" (when nargs == 0) prevents producing wrong error messages in some special cases (see bpo-31229). */ PyErr_Format(PyExc_TypeError, "%.200s%s takes at most %d %sargument%s (%zd given)",