From 60405e878dacf47f850f0ba6ae7d692711a1ce63 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Fri, 31 Mar 2017 01:53:25 +0200 Subject: [PATCH 1/4] bpo-29951: Include function name for some error messages in PyArg_ParseTupleAndKeywords --- Python/getargs.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/Python/getargs.c b/Python/getargs.c index 8cb672d6abc38a..ddbbcb0e0a1f2e 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -1705,8 +1705,10 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, } if (max < nargs) { PyErr_Format(PyExc_TypeError, - "Function takes %s %d positional arguments" + "%s%s takes %s %d positional arguments" " (%d given)", + (fname == NULL) ? "Function" : fname, + (fname == NULL) ? "" : "()", (min != INT_MAX) ? "at most" : "exactly", max, nargs); return cleanreturn(0, &freelist); @@ -1779,8 +1781,10 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, if (skip) { PyErr_Format(PyExc_TypeError, - "Function takes %s %d positional arguments" + "%s%s takes %s %d positional arguments" " (%d given)", + (fname == NULL) ? "Function" : fname, + (fname == NULL) ? "" : "()", (Py_MIN(pos, min) < i) ? "at least" : "exactly", Py_MIN(pos, min), nargs); return cleanreturn(0, &freelist); @@ -1826,8 +1830,10 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, if (!match) { PyErr_Format(PyExc_TypeError, "'%U' is an invalid keyword " - "argument for this function", - key); + "argument for %s%s", + key, + (fname == NULL) ? "this function" : fname, + (fname == NULL) ? "" : "()"); return cleanreturn(0, &freelist); } } @@ -2070,7 +2076,9 @@ vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs, } if (parser->max < nargs) { PyErr_Format(PyExc_TypeError, - "Function takes %s %d positional arguments (%d given)", + "%s%s takes %s %d positional arguments (%d given)", + (parser->fname == NULL) ? "Function" : parser->fname, + (parser->fname == NULL) ? "" : "()", (parser->min != INT_MAX) ? "at most" : "exactly", parser->max, nargs); return cleanreturn(0, &freelist); @@ -2115,8 +2123,10 @@ vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs, if (i < pos) { Py_ssize_t min = Py_MIN(pos, parser->min); PyErr_Format(PyExc_TypeError, - "Function takes %s %d positional arguments" + "%s%s takes %s %d positional arguments" " (%d given)", + (parser->fname == NULL) ? "Function" : parser->fname, + (parser->fname == NULL) ? "" : "()", min < parser->max ? "at least" : "exactly", min, nargs); } @@ -2184,8 +2194,10 @@ vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs, if (!match) { PyErr_Format(PyExc_TypeError, "'%U' is an invalid keyword " - "argument for this function", - keyword); + "argument for %s%s", + keyword, + (parser->fname == NULL) ? "this function" : parser->fname, + (parser->fname == NULL) ? "" : "()"); } return cleanreturn(0, &freelist); } From 5330e2c330f40337dfb7d9cd9d5521333fdf387b Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Fri, 31 Mar 2017 10:54:47 +0200 Subject: [PATCH 2/4] bpo-29951: Include function name for PyArg_ParseTupleAndKeywords errors Also changed format specifier for function name from "%s" to "%.200s" and exception messages should start with lowercase letter --- Lib/test/test_capi.py | 8 ++--- Lib/test/test_exceptions.py | 2 +- Lib/test/test_extcall.py | 2 +- Lib/test/test_getargs2.py | 12 ++++---- Lib/test/test_unpack_ex.py | 2 +- Python/ceval.c | 4 +-- Python/getargs.c | 58 +++++++++++++++++++++---------------- 7 files changed, 48 insertions(+), 40 deletions(-) diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index 2a53f3d081ff70..6e14248a125be9 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -533,19 +533,19 @@ def test_positional_only(self): parse((1, 2, 3), {}, b'OOO', ['', '', 'a']) parse((1, 2), {'a': 3}, b'OOO', ['', '', 'a']) with self.assertRaisesRegex(TypeError, - r'Function takes at least 2 positional arguments \(1 given\)'): + r'function takes at least 2 positional arguments \(1 given\)'): parse((1,), {'a': 3}, b'OOO', ['', '', 'a']) parse((1,), {}, b'O|OO', ['', '', 'a']) with self.assertRaisesRegex(TypeError, - r'Function takes at least 1 positional arguments \(0 given\)'): + r'function takes at least 1 positional arguments \(0 given\)'): parse((), {}, b'O|OO', ['', '', 'a']) parse((1, 2), {'a': 3}, b'OO$O', ['', '', 'a']) with self.assertRaisesRegex(TypeError, - r'Function takes exactly 2 positional arguments \(1 given\)'): + r'function takes exactly 2 positional arguments \(1 given\)'): parse((1,), {'a': 3}, b'OO$O', ['', '', 'a']) parse((1,), {}, b'O|O$O', ['', '', 'a']) with self.assertRaisesRegex(TypeError, - r'Function takes at least 1 positional arguments \(0 given\)'): + r'function takes at least 1 positional arguments \(0 given\)'): parse((), {}, b'O|O$O', ['', '', 'a']) with self.assertRaisesRegex(SystemError, r'Empty parameter name after \$'): parse((1,), {}, b'O|$OO', ['', '', 'a']) diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 2cac6c530ae7d1..90535daec16ada 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -1089,7 +1089,7 @@ def test_attributes(self): self.assertEqual(exc.name, 'somename') self.assertEqual(exc.path, 'somepath') - msg = "'invalid' is an invalid keyword argument for this function" + msg = "'invalid' is an invalid keyword argument for ImportError()" with self.assertRaisesRegex(TypeError, msg): ImportError('test', invalid='keyword') diff --git a/Lib/test/test_extcall.py b/Lib/test/test_extcall.py index 043df013112517..1f77972c5aac14 100644 --- a/Lib/test/test_extcall.py +++ b/Lib/test/test_extcall.py @@ -221,7 +221,7 @@ >>> f(**{1:2}) Traceback (most recent call last): ... - TypeError: f() keywords must be strings + TypeError: f() keyword arguments must be strings >>> h(**{'e': 2}) Traceback (most recent call last): diff --git a/Lib/test/test_getargs2.py b/Lib/test/test_getargs2.py index e5d9aa64ee2dfd..c58d12466246b2 100644 --- a/Lib/test/test_getargs2.py +++ b/Lib/test/test_getargs2.py @@ -553,7 +553,7 @@ def test_required_args(self): try: getargs_keywords(arg1=(1,2)) except TypeError as err: - self.assertEqual(str(err), "Required argument 'arg2' (pos 2) not found") + self.assertEqual(str(err), "required argument for function 'arg2' (pos 2) not found") else: self.fail('TypeError should have been raised') @@ -626,16 +626,16 @@ def test_required_args(self): ) # required arg missing with self.assertRaisesRegex(TypeError, - r"Required argument 'required' \(pos 1\) not found"): + r"required argument for function 'required' \(pos 1\) not found"): getargs_keyword_only(optional=2) with self.assertRaisesRegex(TypeError, - r"Required argument 'required' \(pos 1\) not found"): + r"required argument for function 'required' \(pos 1\) not found"): getargs_keyword_only(keyword_only=3) def test_too_many_args(self): with self.assertRaisesRegex(TypeError, - r"Function takes at most 2 positional arguments \(3 given\)"): + r"function takes at most 2 positional arguments \(3 given\)"): getargs_keyword_only(1, 2, 3) with self.assertRaisesRegex(TypeError, @@ -674,11 +674,11 @@ def test_required_args(self): self.assertEqual(self.getargs(1), (1, -1, -1)) # required positional arg missing with self.assertRaisesRegex(TypeError, - r"Function takes at least 1 positional arguments \(0 given\)"): + r"function takes at least 1 positional arguments \(0 given\)"): self.getargs() with self.assertRaisesRegex(TypeError, - r"Function takes at least 1 positional arguments \(0 given\)"): + r"function takes at least 1 positional arguments \(0 given\)"): self.getargs(keyword=3) def test_empty_keyword(self): diff --git a/Lib/test/test_unpack_ex.py b/Lib/test/test_unpack_ex.py index 6be8f551fcc7ff..d0899f5d8608f6 100644 --- a/Lib/test/test_unpack_ex.py +++ b/Lib/test/test_unpack_ex.py @@ -256,7 +256,7 @@ >>> f(**{1: 3}, **{1: 5}) Traceback (most recent call last): ... - TypeError: f() keywords must be strings + TypeError: f() keyword arguments must be strings Unpacking non-sequence diff --git a/Python/ceval.c b/Python/ceval.c index afd305cf9af44e..286b49b45c7ca3 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2721,7 +2721,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) PyObject *key = PyTuple_GET_ITEM(val, 0); if (!PyUnicode_Check(key)) { PyErr_Format(PyExc_TypeError, - "%.200s%.200s keywords must be strings", + "%.200s%.200s keyword arguments must be strings", PyEval_GetFuncName(func), PyEval_GetFuncDesc(func)); } else { @@ -3990,7 +3990,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, if (keyword == NULL || !PyUnicode_Check(keyword)) { PyErr_Format(PyExc_TypeError, - "%U() keywords must be strings", + "%U() keyword arguments must be strings", co->co_name); goto fail; } diff --git a/Python/getargs.c b/Python/getargs.c index ddbbcb0e0a1f2e..c8a807791d0307 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -1655,7 +1655,7 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, nkwargs = (kwargs == NULL) ? 0 : PyDict_GET_SIZE(kwargs); if (nargs + nkwargs > len) { PyErr_Format(PyExc_TypeError, - "%s%s takes at most %d argument%s (%zd given)", + "%.200s%s takes at most %d argument%s (%zd given)", (fname == NULL) ? "function" : fname, (fname == NULL) ? "" : "()", len, @@ -1705,9 +1705,9 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, } if (max < nargs) { PyErr_Format(PyExc_TypeError, - "%s%s takes %s %d positional arguments" + "%.200s%s takes %s %d positional arguments" " (%d given)", - (fname == NULL) ? "Function" : fname, + (fname == NULL) ? "function" : fname, (fname == NULL) ? "" : "()", (min != INT_MAX) ? "at most" : "exactly", max, nargs); @@ -1754,8 +1754,10 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, * or the end of the format. */ } else { - PyErr_Format(PyExc_TypeError, "Required argument " - "'%s' (pos %d) not found", + PyErr_Format(PyExc_TypeError, "required argument " + "for %.200s%s '%s' (pos %d) not found", + (fname == NULL) ? "function" : fname, + (fname == NULL) ? "" : "()", kwlist[i], i+1); return cleanreturn(0, &freelist); } @@ -1781,9 +1783,9 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, if (skip) { PyErr_Format(PyExc_TypeError, - "%s%s takes %s %d positional arguments" + "%.200s%s takes %s %d positional arguments" " (%d given)", - (fname == NULL) ? "Function" : fname, + (fname == NULL) ? "function" : fname, (fname == NULL) ? "" : "()", (Py_MIN(pos, min) < i) ? "at least" : "exactly", Py_MIN(pos, min), nargs); @@ -1806,8 +1808,10 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, if (current_arg) { /* arg present in tuple and in dict */ PyErr_Format(PyExc_TypeError, - "Argument given by name ('%s') " + "argument for %.200s%s given by name ('%s') " "and position (%d)", + (fname == NULL) ? "function" : fname, + (fname == NULL) ? "" : "()", kwlist[i], i+1); return cleanreturn(0, &freelist); } @@ -1818,7 +1822,7 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, int match = 0; if (!PyUnicode_Check(key)) { PyErr_SetString(PyExc_TypeError, - "keywords must be strings"); + "keyword arguments must be strings"); return cleanreturn(0, &freelist); } for (i = pos; i < len; i++) { @@ -1830,7 +1834,7 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, if (!match) { PyErr_Format(PyExc_TypeError, "'%U' is an invalid keyword " - "argument for %s%s", + "argument for %.200s%s", key, (fname == NULL) ? "this function" : fname, (fname == NULL) ? "" : "()"); @@ -2066,7 +2070,7 @@ vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs, } if (nargs + nkwargs > len) { PyErr_Format(PyExc_TypeError, - "%s%s takes at most %d argument%s (%zd given)", + "%.200s%s takes at most %d argument%s (%zd given)", (parser->fname == NULL) ? "function" : parser->fname, (parser->fname == NULL) ? "" : "()", len, @@ -2076,8 +2080,8 @@ vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs, } if (parser->max < nargs) { PyErr_Format(PyExc_TypeError, - "%s%s takes %s %d positional arguments (%d given)", - (parser->fname == NULL) ? "Function" : parser->fname, + "%200s%s takes %s %d positional arguments (%d given)", + (parser->fname == NULL) ? "function" : parser->fname, (parser->fname == NULL) ? "" : "()", (parser->min != INT_MAX) ? "at most" : "exactly", parser->max, nargs); @@ -2123,17 +2127,19 @@ vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs, if (i < pos) { Py_ssize_t min = Py_MIN(pos, parser->min); PyErr_Format(PyExc_TypeError, - "%s%s takes %s %d positional arguments" + "%.200s%s takes %s %d positional arguments" " (%d given)", - (parser->fname == NULL) ? "Function" : parser->fname, + (parser->fname == NULL) ? "function" : parser->fname, (parser->fname == NULL) ? "" : "()", min < parser->max ? "at least" : "exactly", min, nargs); } else { keyword = PyTuple_GET_ITEM(kwtuple, i - pos); - PyErr_Format(PyExc_TypeError, "Required argument " - "'%U' (pos %d) not found", + PyErr_Format(PyExc_TypeError, "required argument " + "for %.200s%s '%U' (pos %d) not found", + (parser->fname == NULL) ? "function" : parser->fname, + (parser->fname == NULL) ? "" : "()", keyword, i+1); } return cleanreturn(0, &freelist); @@ -2163,8 +2169,10 @@ vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs, if (current_arg) { /* arg present in tuple and in dict */ PyErr_Format(PyExc_TypeError, - "Argument given by name ('%U') " + "argument for %.200s%s given by name ('%U') " "and position (%d)", + (parser->fname == NULL) ? "function" : parser->fname, + (parser->fname == NULL) ? "" : "()", keyword, i+1); return cleanreturn(0, &freelist); } @@ -2186,7 +2194,7 @@ vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs, if (!PyUnicode_Check(keyword)) { PyErr_SetString(PyExc_TypeError, - "keywords must be strings"); + "keyword arguments must be strings"); return cleanreturn(0, &freelist); } match = PySequence_Contains(kwtuple, keyword); @@ -2194,7 +2202,7 @@ vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs, if (!match) { PyErr_Format(PyExc_TypeError, "'%U' is an invalid keyword " - "argument for %s%s", + "argument for %.200s%s", keyword, (parser->fname == NULL) ? "this function" : parser->fname, (parser->fname == NULL) ? "" : "()"); @@ -2377,7 +2385,7 @@ unpack_stack(PyObject **args, Py_ssize_t nargs, const char *name, if (name != NULL) PyErr_Format( PyExc_TypeError, - "%s expected %s%zd arguments, got %zd", + "%.200s expected %s%zd arguments, got %zd", name, (min == max ? "" : "at least "), min, nargs); else PyErr_Format( @@ -2396,7 +2404,7 @@ unpack_stack(PyObject **args, Py_ssize_t nargs, const char *name, if (name != NULL) PyErr_Format( PyExc_TypeError, - "%s expected %s%zd arguments, got %zd", + "%.200s expected %s%zd arguments, got %zd", name, (min == max ? "" : "at most "), max, nargs); else PyErr_Format( @@ -2481,7 +2489,7 @@ _PyArg_NoKeywords(const char *funcname, PyObject *kwargs) return 1; } - PyErr_Format(PyExc_TypeError, "%s does not take keyword arguments", + PyErr_Format(PyExc_TypeError, "%.200s does not take keyword arguments", funcname); return 0; } @@ -2498,7 +2506,7 @@ _PyArg_NoStackKeywords(const char *funcname, PyObject *kwnames) return 1; } - PyErr_Format(PyExc_TypeError, "%s does not take keyword arguments", + PyErr_Format(PyExc_TypeError, "%.200s does not take keyword arguments", funcname); return 0; } @@ -2516,7 +2524,7 @@ _PyArg_NoPositional(const char *funcname, PyObject *args) if (PyTuple_GET_SIZE(args) == 0) return 1; - PyErr_Format(PyExc_TypeError, "%s does not take positional arguments", + PyErr_Format(PyExc_TypeError, "%.200s does not take positional arguments", funcname); return 0; } From e4f4c24159c580b3d999d49eb450fbdd18b43096 Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Fri, 31 Mar 2017 12:34:46 +0200 Subject: [PATCH 3/4] bpo-29951: Changed exception message for missing required argument --- Lib/test/test_exceptions.py | 2 +- Lib/test/test_getargs2.py | 7 ++++--- Python/getargs.c | 8 ++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 90535daec16ada..6a1a7828af13bd 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -1089,7 +1089,7 @@ def test_attributes(self): self.assertEqual(exc.name, 'somename') self.assertEqual(exc.path, 'somepath') - msg = "'invalid' is an invalid keyword argument for ImportError()" + msg = "'invalid' is an invalid keyword argument for ImportError" with self.assertRaisesRegex(TypeError, msg): ImportError('test', invalid='keyword') diff --git a/Lib/test/test_getargs2.py b/Lib/test/test_getargs2.py index c58d12466246b2..86df3a475042ca 100644 --- a/Lib/test/test_getargs2.py +++ b/Lib/test/test_getargs2.py @@ -553,7 +553,8 @@ def test_required_args(self): try: getargs_keywords(arg1=(1,2)) except TypeError as err: - self.assertEqual(str(err), "required argument for function 'arg2' (pos 2) not found") + self.assertEqual( + str(err), "function missing required argument 'arg2' (pos 2)") else: self.fail('TypeError should have been raised') @@ -626,11 +627,11 @@ def test_required_args(self): ) # required arg missing with self.assertRaisesRegex(TypeError, - r"required argument for function 'required' \(pos 1\) not found"): + r"function missing required argument 'required' \(pos 1\)"): getargs_keyword_only(optional=2) with self.assertRaisesRegex(TypeError, - r"required argument for function 'required' \(pos 1\) not found"): + r"function missing required argument 'required' \(pos 1\)"): getargs_keyword_only(keyword_only=3) def test_too_many_args(self): diff --git a/Python/getargs.c b/Python/getargs.c index c8a807791d0307..a1ba1b8b1f0422 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -1754,8 +1754,8 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, * or the end of the format. */ } else { - PyErr_Format(PyExc_TypeError, "required argument " - "for %.200s%s '%s' (pos %d) not found", + PyErr_Format(PyExc_TypeError, "%.200s%s missing required " + "argument '%s' (pos %d)", (fname == NULL) ? "function" : fname, (fname == NULL) ? "" : "()", kwlist[i], i+1); @@ -2136,8 +2136,8 @@ vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs, } else { keyword = PyTuple_GET_ITEM(kwtuple, i - pos); - PyErr_Format(PyExc_TypeError, "required argument " - "for %.200s%s '%U' (pos %d) not found", + PyErr_Format(PyExc_TypeError, "%.200s%s missing required " + "argument '%U' (pos %d)", (parser->fname == NULL) ? "function" : parser->fname, (parser->fname == NULL) ? "" : "()", keyword, i+1); From 4da0e03de7b3b56cfcf78dbdd2c56d0281013a3d Mon Sep 17 00:00:00 2001 From: Michael Seifert Date: Fri, 31 Mar 2017 18:10:17 +0200 Subject: [PATCH 4/4] bpo-29551: undo the "keyword argument" part of the change --- Lib/test/test_extcall.py | 2 +- Lib/test/test_unpack_ex.py | 2 +- Python/ceval.c | 4 ++-- Python/getargs.c | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_extcall.py b/Lib/test/test_extcall.py index 1f77972c5aac14..043df013112517 100644 --- a/Lib/test/test_extcall.py +++ b/Lib/test/test_extcall.py @@ -221,7 +221,7 @@ >>> f(**{1:2}) Traceback (most recent call last): ... - TypeError: f() keyword arguments must be strings + TypeError: f() keywords must be strings >>> h(**{'e': 2}) Traceback (most recent call last): diff --git a/Lib/test/test_unpack_ex.py b/Lib/test/test_unpack_ex.py index d0899f5d8608f6..6be8f551fcc7ff 100644 --- a/Lib/test/test_unpack_ex.py +++ b/Lib/test/test_unpack_ex.py @@ -256,7 +256,7 @@ >>> f(**{1: 3}, **{1: 5}) Traceback (most recent call last): ... - TypeError: f() keyword arguments must be strings + TypeError: f() keywords must be strings Unpacking non-sequence diff --git a/Python/ceval.c b/Python/ceval.c index 286b49b45c7ca3..afd305cf9af44e 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2721,7 +2721,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) PyObject *key = PyTuple_GET_ITEM(val, 0); if (!PyUnicode_Check(key)) { PyErr_Format(PyExc_TypeError, - "%.200s%.200s keyword arguments must be strings", + "%.200s%.200s keywords must be strings", PyEval_GetFuncName(func), PyEval_GetFuncDesc(func)); } else { @@ -3990,7 +3990,7 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, if (keyword == NULL || !PyUnicode_Check(keyword)) { PyErr_Format(PyExc_TypeError, - "%U() keyword arguments must be strings", + "%U() keywords must be strings", co->co_name); goto fail; } diff --git a/Python/getargs.c b/Python/getargs.c index a1ba1b8b1f0422..58c9a998ff8dff 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -1584,7 +1584,7 @@ PyArg_ValidateKeywordArguments(PyObject *kwargs) } if (!_PyDict_HasOnlyStringKeys(kwargs)) { PyErr_SetString(PyExc_TypeError, - "keyword arguments must be strings"); + "keywords must be strings"); return 0; } return 1; @@ -1822,7 +1822,7 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format, int match = 0; if (!PyUnicode_Check(key)) { PyErr_SetString(PyExc_TypeError, - "keyword arguments must be strings"); + "keywords must be strings"); return cleanreturn(0, &freelist); } for (i = pos; i < len; i++) { @@ -2194,7 +2194,7 @@ vgetargskeywordsfast_impl(PyObject **args, Py_ssize_t nargs, if (!PyUnicode_Check(keyword)) { PyErr_SetString(PyExc_TypeError, - "keyword arguments must be strings"); + "keywords must be strings"); return cleanreturn(0, &freelist); } match = PySequence_Contains(kwtuple, keyword);