From 086f409864ee0cc14c87ff9175f00695ac69315a Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Fri, 22 Aug 2008 09:12:17 +0200 Subject: [PATCH] almost complete in-place implementation of Python's ParseTupleAndKeywords() use argument exception messages from Python 2.6 TODO: handling of keyword-only arguments seems to require more work TODO: clean up helper functions (currently somewhat redundant) --- Cython/Compiler/Nodes.py | 351 +++++++++++++++++++++++++++++----- tests/errors/callargs.pyx | 20 ++ tests/run/callargs.pyx | 31 +++ tests/run/classkwonlyargs.pyx | 12 +- tests/run/extkwonlyargs.pyx | 16 +- tests/run/extstarargs.pyx | 18 +- tests/run/kwonlyargs.pyx | 16 +- tests/run/kwonlyargscall.pyx | 16 +- tests/run/starargs.pyx | 18 +- 9 files changed, 393 insertions(+), 105 deletions(-) create mode 100644 tests/errors/callargs.pyx create mode 100644 tests/run/callargs.pyx diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 7eb3ede8..77fb89cd 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -1336,17 +1336,18 @@ class DefNode(FuncDefNode): self.declare_pyfunction(env) self.analyse_signature(env) self.return_type = self.entry.signature.return_type() + env.use_utility_code(raise_argtuple_too_long_utility_code) + env.use_utility_code(keyword_check_utility_code) + env.use_utility_code(check_double_keywords_utility_code) if self.signature_has_generic_args(): if self.star_arg: env.use_utility_code(get_stararg_utility_code) - elif self.signature_has_generic_args(): - env.use_utility_code(raise_argtuple_too_long_utility_code) if not self.signature_has_nongeneric_args(): - env.use_utility_code(get_keyword_string_check_utility_code) + env.use_utility_code(keyword_string_check_utility_code) elif self.starstar_arg: - env.use_utility_code(get_splitkeywords_utility_code) + env.use_utility_code(split_keywords_utility_code) if self.num_required_kw_args: - env.use_utility_code(get_checkkeywords_utility_code) + env.use_utility_code(check_keywords_utility_code) def analyse_signature(self, env): any_type_tests_needed = 0 @@ -1644,7 +1645,7 @@ class DefNode(FuncDefNode): if has_star_or_kw_args: self.generate_stararg_getting_code(code) - self.generate_argument_tuple_parsing_code( + self.generate_tuple_and_keyword_parsing_code( positional_args, arg_formats, arg_addrs, code) code.error_label = old_error_label @@ -1663,7 +1664,7 @@ class DefNode(FuncDefNode): code.putln("return %s;" % self.error_value()) code.put_label(end_label) - def generate_argument_tuple_parsing_code(self, positional_args, + def _generate_argument_tuple_parsing_code(self, positional_args, arg_formats, arg_addrs, code): # Unpack inplace if it's simple if not self.num_required_kw_args: @@ -1689,20 +1690,7 @@ class DefNode(FuncDefNode): code.putln('if (PyTuple_GET_SIZE(%s) > %s) {' % (Naming.args_cname, i)) closing += 1 item = "PyTuple_GET_ITEM(%s, %s)" % (Naming.args_cname, i) - if arg.type.is_pyobject: - if arg.is_generic: - item = PyrexTypes.typecast(arg.type, PyrexTypes.py_object_type, item) - code.putln("%s = %s;" % (arg.entry.cname, item)) - else: - func = arg.type.from_py_function - if func: - code.putln("%s = %s(%s); %s" % ( - arg.entry.cname, - func, - item, - code.error_goto_if(arg.type.error_condition(arg.entry.cname), arg.pos))) - else: - error(arg.pos, "Cannot convert Python object argument to type '%s'" % arg.type) + self.generate_arg_assignment(arg, item, code) i += 1 for _ in range(closing): code.putln('}') @@ -1722,6 +1710,22 @@ class DefNode(FuncDefNode): if not self.num_required_kw_args: code.putln('}') + def generate_arg_assignment(self, arg, item, code): + if arg.type.is_pyobject: + if arg.is_generic: + item = PyrexTypes.typecast(arg.type, PyrexTypes.py_object_type, item) + code.putln("%s = %s;" % (arg.entry.cname, item)) + else: + func = arg.type.from_py_function + if func: + code.putln("%s = %s(%s); %s" % ( + arg.entry.cname, + func, + item, + code.error_goto_if(arg.type.error_condition(arg.entry.cname), arg.pos))) + else: + error(arg.pos, "Cannot convert Python object argument to type '%s'" % arg.type) + def put_stararg_decrefs(self, code): if self.star_arg: code.put_decref(Naming.args_cname, py_object_type) @@ -1778,8 +1782,7 @@ class DefNode(FuncDefNode): code.put_incref(Naming.args_cname, py_object_type) code.put("%s = %s; " % (star_arg_cname, Naming.empty_tuple)) code.put_incref(Naming.empty_tuple, py_object_type) - code.putln("}") - code.putln("else {") + code.putln("} else {") code.putln( "if (unlikely(__Pyx_SplitStarArg(&%s, %d, &%s) < 0)) return %s;" % ( Naming.args_cname, @@ -1788,7 +1791,7 @@ class DefNode(FuncDefNode): self.error_value())) code.putln("}") self.star_arg.entry.xdecref_cleanup = 0 - elif self.signature_has_generic_args(): + else: # make sure supernumerous positional arguments do not run # into keyword-only arguments and provide a more helpful # message than PyArg_ParseTupelAndKeywords() @@ -1798,18 +1801,31 @@ class DefNode(FuncDefNode): if self.starstar_arg: handle_error = 1 code.put( - "if (unlikely(__Pyx_SplitKeywords(&%s, %s, &%s, %s) < 0)) " % ( + 'if (unlikely(__Pyx_SplitKeywords(&%s, %s, &%s, %s, PyTuple_GET_SIZE(%s), "%s") < 0)) ' % ( Naming.kwds_cname, Naming.kwdlist_cname, self.starstar_arg.entry.cname, - self.reqd_kw_flags_cname)) + self.reqd_kw_flags_cname, + Naming.args_cname, + self.name.utf8encode())) self.starstar_arg.entry.xdecref_cleanup = 0 elif self.num_required_kw_args: handle_error = 1 - code.put("if (unlikely(__Pyx_CheckRequiredKeywords(%s, %s, %s) < 0)) " % ( + code.put('if (unlikely(__Pyx_CheckRequiredKeywords(%s, %s, %s, PyTuple_GET_SIZE(%s), "%s") < 0)) ' % ( + Naming.kwds_cname, + Naming.kwdlist_cname, + self.reqd_kw_flags_cname, + Naming.args_cname, + self.name.utf8encode())) + else: + # check that positional arguments are not passed as keywords also + handle_error = 1 + code.put('if (unlikely(%s) && unlikely(__Pyx_CheckDoubleKeywords(%s, %s, PyTuple_GET_SIZE(%s), "%s") < 0)) ' % ( + Naming.kwds_cname, Naming.kwds_cname, Naming.kwdlist_cname, - self.reqd_kw_flags_cname)) + Naming.args_cname, + self.name.utf8encode())) if handle_error: if self.star_arg: @@ -1821,11 +1837,109 @@ class DefNode(FuncDefNode): else: code.putln(error_return) + def generate_tuple_and_keyword_parsing_code(self, positional_args, + arg_formats, arg_addrs, code): + min_positional_args = self.num_required_args - self.num_required_kw_args + if len(self.args) > 0 and self.args[0].is_self_arg: + min_positional_args -= 1 + max_positional_args = len(positional_args) + + # just a quick check to start with + if not self.star_arg: + code.putln("if (unlikely(PyTuple_GET_SIZE(%s) > %d)) {" % ( + Naming.args_cname, max_positional_args)) + code.put('__Pyx_RaiseArgtupleInvalid("%s", %d, %d, PyTuple_GET_SIZE(%s)); ' % ( + self.name.utf8encode(), min_positional_args, + max_positional_args, Naming.args_cname)) + code.putln(code.error_goto(self.pos)) + code.putln("}") + + # --- optimised code when we receive keyword arguments + code.putln("if (unlikely(%s) && (PyDict_Size(%s) > 0)) {" % ( + Naming.kwds_cname, Naming.kwds_cname)) + code.putln("PyObject* values[%d];" % max_positional_args) + code.putln("Py_ssize_t kw_args = PyDict_Size(%s), arg;" % + Naming.kwds_cname) + + code.putln("for (arg=0; arg < PyTuple_GET_SIZE(%s); arg++) {" % + Naming.args_cname) + code.putln("values[arg] = PyTuple_GET_ITEM(%s, arg);" % + Naming.args_cname) + code.putln("if (unlikely(PyDict_GetItemString(%s, %s[arg]))) {" % ( + Naming.kwds_cname, Naming.kwdlist_cname)) + code.put( '__Pyx_RaiseDoubleKeywordsError("%s", %s[arg]); ' % ( + self.name.utf8encode(), Naming.kwdlist_cname)) + code.putln(code.error_goto(self.pos)) + code.putln('}') + code.putln('}') + + code.putln("for (arg=PyTuple_GET_SIZE(%s); arg < %d; arg++) {" % ( + Naming.args_cname, max_positional_args)) + code.putln('values[arg] = PyDict_GetItemString(%s, %s[arg]);' % ( + Naming.kwds_cname, Naming.kwdlist_cname)) + code.putln('if (values[arg]) kw_args--;'); + code.putln('}') + + code.putln('if (unlikely(kw_args > 0)) {') + code.put('if (!__Pyx_CheckKeywords(%s, "%s", %s)) ' % ( + Naming.kwds_cname, self.name.utf8encode(), Naming.kwdlist_cname)) + code.putln(code.error_goto(self.pos)) + code.putln('}') + + default_seen = False + for i, arg in enumerate(positional_args): + if arg.default: + default_seen = True + if default_seen: + code.putln("if (values[%d]) {" % i) + self.generate_arg_assignment(arg, "values[%d]" % i, code) + if default_seen: + if not arg.default: + code.putln('} else {') + code.put('__Pyx_RaiseArgtupleInvalid("%s", %d, %d, %d); ' % ( + self.name.utf8encode(), min_positional_args, + max_positional_args, i)) + code.putln(code.error_goto(self.pos)) + code.putln('}') + + # --- optimised code when we did not receive any keyword arguments + code.putln('} else if (unlikely(PyTuple_GET_SIZE(%s) < %d)) {' % ( + Naming.args_cname, min_positional_args)) + code.put('__Pyx_RaiseArgtupleInvalid("%s", %d, %d, PyTuple_GET_SIZE(%s)); ' % ( + self.name.utf8encode(), min_positional_args, + max_positional_args, Naming.args_cname)) + code.putln(code.error_goto(self.pos)) + + code.putln('} else {') + closing = 0 + for i, arg in enumerate(positional_args): + if arg.default: + code.putln('if (PyTuple_GET_SIZE(%s) > %s) {' % (Naming.args_cname, i)) + closing += 1 + item = "PyTuple_GET_ITEM(%s, %s)" % (Naming.args_cname, i) + self.generate_arg_assignment(arg, item, code) + for _ in range(closing): + code.putln('}') + + code.putln('}') + return + + argformat = '"%s"' % string.join(arg_formats, "") + pt_arglist = [Naming.args_cname, Naming.kwds_cname, argformat, Naming.kwdlist_cname] + arg_addrs + pt_argstring = string.join(pt_arglist, ", ") + code.putln( + 'if (unlikely(!PyArg_ParseTupleAndKeywords(%s))) %s' % ( + pt_argstring, + code.error_goto(self.pos))) + self.generate_argument_conversion_code(code) + + code.putln('}') + def generate_positional_args_check(self, code, nargs): code.putln("if (unlikely(PyTuple_GET_SIZE(%s) > %d)) {" % ( Naming.args_cname, nargs)) - code.putln("__Pyx_RaiseArgtupleTooLong(%d, PyTuple_GET_SIZE(%s));" % ( - nargs, Naming.args_cname)) + code.putln('__Pyx_RaiseArgtupleInvalid("%s", 0, %d, PyTuple_GET_SIZE(%s));' % ( + self.name.utf8encode(), nargs, Naming.args_cname)) code.putln("return %s;" % self.error_value()) code.putln("}") @@ -4267,25 +4381,42 @@ static INLINE int __Pyx_SplitStarArg( #------------------------------------------------------------------------------------ # -# __Pyx_RaiseArgtupleTooLong raises the correct exception when too -# many positional arguments were found. This handles Py_ssize_t -# formatting correctly. +# __Pyx_RaiseArgtupleInvalid raises the correct exception when too +# many or too few positional arguments were found. This handles +# Py_ssize_t formatting correctly. raise_argtuple_too_long_utility_code = [ """ -static INLINE void __Pyx_RaiseArgtupleTooLong(Py_ssize_t num_expected, Py_ssize_t num_found); /*proto*/ +static INLINE void __Pyx_RaiseArgtupleInvalid(char* func_name, + Py_ssize_t num_min, Py_ssize_t num_max, Py_ssize_t num_found); /*proto*/ """,""" -static INLINE void __Pyx_RaiseArgtupleTooLong( - Py_ssize_t num_expected, +static INLINE void __Pyx_RaiseArgtupleInvalid( + char* func_name, + Py_ssize_t num_min, + Py_ssize_t num_max, Py_ssize_t num_found) { - const char* error_message = - #if PY_VERSION_HEX < 0x02050000 - "function takes at most %d positional arguments (%d given)"; - #else - "function takes at most %zd positional arguments (%zd given)"; - #endif - PyErr_Format(PyExc_TypeError, error_message, num_expected, num_found); + if (num_found < num_min) { + PyErr_Format(PyExc_TypeError, + #if PY_VERSION_HEX < 0x02050000 + "%s() takes at least %d positional arguments (%d given)", + #elif PY_MAJOR_VERSION >= 3 + "%U() takes at most %zd positional arguments (%zd given)", + #else + "%s() takes at least %zd positional arguments (%zd given)", + #endif + func_name, num_min, num_found); + } else { + PyErr_Format(PyExc_TypeError, + #if PY_VERSION_HEX < 0x02050000 + "%s() takes at most %d positional arguments (%d given)", + #elif PY_MAJOR_VERSION >= 3 + "%U() takes at most %zd positional arguments (%zd given)", + #else + "%s() takes at most %zd positional arguments (%zd given)", + #endif + func_name, num_max, num_found); + } } """] @@ -4295,7 +4426,7 @@ static INLINE void __Pyx_RaiseArgtupleTooLong( # were passed to a function, or if any keywords were passed to a # function that does not accept them. -get_keyword_string_check_utility_code = [ +keyword_string_check_utility_code = [ """ static int __Pyx_CheckKeywordStrings(PyObject *kwdict, const char* function_name, int kw_allowed); /*proto*/ """,""" @@ -4332,6 +4463,68 @@ static int __Pyx_CheckKeywordStrings( } """] +#------------------------------------------------------------------------------------ +# +# __Pyx_CheckKeywords raises an error if non-string keywords were +# passed to a function, or if any unsupported keywords were passed to +# a function. + +keyword_check_utility_code = [ +""" +static int __Pyx_CheckKeywords(PyObject *kwdict, const char* function_name, + char** argnames); /*proto*/ +""",""" +static int __Pyx_CheckKeywords( + PyObject *kwdict, + const char* function_name, + char** argnames) +{ + PyObject* key = 0; + Py_ssize_t pos = 0; + char** name; + while (PyDict_Next(kwdict, &pos, &key, 0)) { + #if PY_MAJOR_VERSION < 3 + if (unlikely(!PyString_Check(key))) { + #else + if (unlikely(!PyUnicode_Check(key))) { + #endif + PyErr_Format(PyExc_TypeError, + "%s() keywords must be strings", function_name); + return 0; + } else if (argnames) { + name = argnames; + while (*name) { + #if PY_MAJOR_VERSION >= 3 + PyObject* utf8_key = PyUnicode_AsUTF8String(key); + if (!utf8_key) return -1; + if (strcmp(*name, PyString_AS_STRING(utf8_key)) == 0) { + Py_DECREF(utf8_key); + break; + } + Py_DECREF(utf8_key); + #else + if (strcmp(*name, PyString_AS_STRING(key)) == 0) + break; + #endif + name++; + } + if (!*name) { + PyErr_Format(PyExc_TypeError, + #if PY_MAJOR_VERSION < 3 + "'%s' is an invalid keyword argument for this function", + PyString_AS_STRING(key)); + #else + "'%U' is an invalid keyword argument for this function", + key); + #endif + return 0; + } + } + } + return 1; +} +"""] + #------------------------------------------------------------------------------------ # # __Pyx_SplitKeywords splits the kwds dict into two parts one part @@ -4349,17 +4542,24 @@ static int __Pyx_CheckKeywordStrings( # the names in kwd_list, indicating required keyword arguments. If # any of these are not present in kwds, an exception is raised. # +# If num_posargs is greater 0, it denotes the number of positional +# arguments that were passed and that must therefore not get passed +# as keyword arguments as well. +# -get_splitkeywords_utility_code = [ +split_keywords_utility_code = [ """ static int __Pyx_SplitKeywords(PyObject **kwds, char *kwd_list[], \ - PyObject **kwds2, char rqd_kwds[]); /*proto*/ + PyObject **kwds2, char rqd_kwds[], + Py_ssize_t num_posargs, char* func_name); /*proto*/ """,""" static int __Pyx_SplitKeywords( PyObject **kwds, char *kwd_list[], PyObject **kwds2, - char rqd_kwds[]) + char rqd_kwds[], + Py_ssize_t num_posargs, + char* func_name) { PyObject *s = 0, *x = 0, *kwds1 = 0; int i; @@ -4380,6 +4580,8 @@ static int __Pyx_SplitKeywords( #endif x = PyDict_GetItem(*kwds, s); if (x) { + if (i < num_posargs) + goto arg_passed_twice; if (PyDict_SetItem(kwds1, s, x) < 0) goto bad; if (PyDict_DelItem(*kwds2, s) < 0) @@ -4404,6 +4606,10 @@ static int __Pyx_SplitKeywords( *kwds = kwds1; return 0; +arg_passed_twice: + PyErr_Format(PyExc_TypeError, + "%s() got multiple values for keyword argument '%s'", func_name, *p); + goto bad; missing_kwarg: PyErr_Format(PyExc_TypeError, "required keyword argument '%s' is missing", *p); @@ -4415,23 +4621,32 @@ bad: } """] -get_checkkeywords_utility_code = [ +check_keywords_utility_code = [ """ static INLINE int __Pyx_CheckRequiredKeywords(PyObject *kwds, char *kwd_list[], - char rqd_kwds[]); /*proto*/ + char rqd_kwds[], Py_ssize_t num_posargs, char* func_name); /*proto*/ """,""" static INLINE int __Pyx_CheckRequiredKeywords( PyObject *kwds, char *kwd_list[], - char rqd_kwds[]) + char rqd_kwds[], + Py_ssize_t num_posargs, + char* func_name) { int i; char **p; if (kwds) { - for (i = 0, p = kwd_list; *p; i++, p++) + p = kwd_list; + for (i=0; i < num_posargs && *p; i++, p++) { + if (PyDict_GetItemString(kwds, *p)) + goto arg_passed_twice; + } + while (*p) { if (rqd_kwds[i] && !PyDict_GetItemString(kwds, *p)) goto missing_kwarg; + i++; p++; + } } else { for (i = 0, p = kwd_list; *p; i++, p++) @@ -4440,6 +4655,10 @@ static INLINE int __Pyx_CheckRequiredKeywords( } return 0; +arg_passed_twice: + PyErr_Format(PyExc_TypeError, + "%s() got multiple values for keyword argument '%s'", func_name, *p); + return -1; missing_kwarg: PyErr_Format(PyExc_TypeError, "required keyword argument '%s' is missing", *p); @@ -4447,6 +4666,36 @@ missing_kwarg: } """] +check_double_keywords_utility_code = [ +""" +static INLINE int __Pyx_CheckDoubleKeywords(PyObject *kwds, char *kwd_list[], + Py_ssize_t num_posargs, const char* func_name); /*proto*/ +static int __Pyx_RaiseDoubleKeywordsError(const char* func_name, + const char* kw_name); /*proto*/ +""",""" +static INLINE int __Pyx_CheckDoubleKeywords( + PyObject *kwds, + char *kwd_list[], + Py_ssize_t num_posargs, + const char* func_name) +{ + int i; + char** p = kwd_list; + for (i=0; i < num_posargs && *p; i++, p++) { + if (PyDict_GetItemString(kwds, *p)) + return __Pyx_RaiseDoubleKeywordsError(func_name, *p); + } + + return 0; +} + +static int __Pyx_RaiseDoubleKeywordsError(const char* func_name, const char* kw_name) { + PyErr_Format(PyExc_TypeError, + "%s() got multiple values for keyword argument '%s'", func_name, kw_name); + return -1; +} +"""] + #------------------------------------------------------------------------------------ unraisable_exception_utility_code = [ diff --git a/tests/errors/callargs.pyx b/tests/errors/callargs.pyx new file mode 100644 index 00000000..e9c1b369 --- /dev/null +++ b/tests/errors/callargs.pyx @@ -0,0 +1,20 @@ +def f(*args, **kwargs): + pass + +args = (1,2,3) +kwargs = {u"test" : "toast"} + +def test(): + f(*args, 1, 2, 3) + f(**kwargs, 1, 2, c=3) + f(*args, **kwargs, *args) + f(1, 2, c=3, *args, **kwargs, *args) + f(1, 2, c=3, *args, d=5, **kwargs, **kwargs) + f(1, 2, c=3, *args, d=5, **kwargs, x=6) + f(1=2) + +# too bad we don't get more errors here ... + +_ERRORS = u""" +8:13: Non-keyword arg following star-arg +""" diff --git a/tests/run/callargs.pyx b/tests/run/callargs.pyx new file mode 100644 index 00000000..27d46cab --- /dev/null +++ b/tests/run/callargs.pyx @@ -0,0 +1,31 @@ +__doc__ = u""" + >>> test() + 1 2 3 * 0 0 + 1 2 3 * 0 0 + 1 2 9 * 2 1 + 1 2 7 * 2 1 + 1 2 9 * 2 2 + 1 2 9 * 2 2 + 1 2 9 * 2 3 +""" + +def f(a, b, c, *args, **kwargs): + print a, b, c, u'*', len(args), len(kwargs) + +args = (9,8,7) + +import sys +if sys.version_info[0] >= 3: + kwargs = {u"test" : u"toast"} +else: + kwargs = {"test" : u"toast"} + + +def test(): + f(1,2,3) + f(1,2, c=3) + f(1,2, d=3, *args) + f(1,2, d=3, *(7,8,9)) + f(1,2, d=3, *args, **kwargs) + f(1,2, d=3, *args, e=5) + f(1,2, d=3, *args, e=5, **kwargs) diff --git a/tests/run/classkwonlyargs.pyx b/tests/run/classkwonlyargs.pyx index 961c78ec..2c2b1e2b 100644 --- a/tests/run/classkwonlyargs.pyx +++ b/tests/run/classkwonlyargs.pyx @@ -5,20 +5,20 @@ __doc__ = u""" >>> b(1,2,3) >>> b(1,2,3,4) Traceback (most recent call last): - TypeError: function takes exactly 4 arguments (5 given) + TypeError: b() takes at most 4 positional arguments (5 given) >>> c(1,2) >>> c(1,2,3) >>> c(1,2,3,4) Traceback (most recent call last): - TypeError: function takes at most 4 arguments (5 given) + TypeError: c() takes at most 4 positional arguments (5 given) >>> d(1,2) >>> d(1,2, c=1) >>> d(1,2,3) Traceback (most recent call last): - TypeError: function takes at most 3 positional arguments (4 given) + TypeError: d() takes at most 3 positional arguments (4 given) >>> d(1,2, d=1) Traceback (most recent call last): TypeError: 'd' is an invalid keyword argument for this function @@ -30,14 +30,14 @@ __doc__ = u""" >>> e(1,2,3) >>> e(1,2,3,4) Traceback (most recent call last): - TypeError: function takes at most 4 positional arguments (5 given) + TypeError: e() takes at most 4 positional arguments (5 given) >>> f(1,2, c=1) >>> f(1,2, c=1, d=2) >>> f(1,2,3) Traceback (most recent call last): - TypeError: function takes at most 3 positional arguments (4 given) + TypeError: f() takes at most 3 positional arguments (4 given) >>> f(1,2) Traceback (most recent call last): TypeError: required keyword argument 'c' is missing @@ -51,7 +51,7 @@ __doc__ = u""" >>> g(1,2,3) #doctest: +ELLIPSIS Traceback (most recent call last): - TypeError: function takes at most 3 positional arguments (4 given) + TypeError: g() takes at most 3 positional arguments (4 given) >>> g(1,2) Traceback (most recent call last): TypeError: required keyword argument 'c' is missing diff --git a/tests/run/extkwonlyargs.pyx b/tests/run/extkwonlyargs.pyx index 5dc8306c..b2d343ee 100644 --- a/tests/run/extkwonlyargs.pyx +++ b/tests/run/extkwonlyargs.pyx @@ -5,20 +5,20 @@ __doc__ = u""" >>> b(1,2,3) >>> b(1,2,3,4) Traceback (most recent call last): - TypeError: function takes exactly 3 arguments (4 given) + TypeError: b() takes at most 3 positional arguments (4 given) >>> c(1,2) >>> c(1,2,3) >>> c(1,2,3,4) Traceback (most recent call last): - TypeError: function takes at most 3 arguments (4 given) + TypeError: c() takes at most 3 positional arguments (4 given) >>> d(1,2) >>> d(1,2, c=1) >>> d(1,2,3) Traceback (most recent call last): - TypeError: function takes at most 2 positional arguments (3 given) + TypeError: d() takes at most 2 positional arguments (3 given) >>> d(1,2, d=1) Traceback (most recent call last): TypeError: 'd' is an invalid keyword argument for this function @@ -30,14 +30,14 @@ __doc__ = u""" >>> e(1,2,3) >>> e(1,2,3,4) Traceback (most recent call last): - TypeError: function takes at most 3 positional arguments (4 given) + TypeError: e() takes at most 3 positional arguments (4 given) >>> f(1,2, c=1) >>> f(1,2, c=1, d=2) >>> f(1,2,3) Traceback (most recent call last): - TypeError: function takes at most 2 positional arguments (3 given) + TypeError: f() takes at most 2 positional arguments (3 given) >>> f(1,2) Traceback (most recent call last): TypeError: required keyword argument 'c' is missing @@ -51,7 +51,7 @@ __doc__ = u""" >>> g(1,2,3) Traceback (most recent call last): - TypeError: function takes at most 2 positional arguments (3 given) + TypeError: g() takes at most 2 positional arguments (3 given) >>> g(1,2) Traceback (most recent call last): TypeError: required keyword argument 'c' is missing @@ -84,10 +84,6 @@ __doc__ = u""" TypeError: required keyword argument 'f' is missing """ -import sys, re -if sys.version_info >= (2,6): - __doc__ = re.sub(u"Error: (.*)exactly(.*)", u"Error: \\1at most\\2", __doc__) - cdef class Ext: def b(self, a, b, c): pass diff --git a/tests/run/extstarargs.pyx b/tests/run/extstarargs.pyx index 454e6bde..0b785999 100644 --- a/tests/run/extstarargs.pyx +++ b/tests/run/extstarargs.pyx @@ -7,10 +7,10 @@ __doc__ = u""" (1, 2, 3) >>> spam(1,2) #doctest: +ELLIPSIS Traceback (most recent call last): - TypeError: function takes exactly 3 arguments (2 given) + TypeError: spam() takes at least 3 positional arguments (2 given) >>> spam(1,2,3,4) Traceback (most recent call last): - TypeError: function takes exactly 3 arguments (4 given) + TypeError: spam() takes at most 3 positional arguments (4 given) >>> spam(1,2,3, a=1) #doctest: +ELLIPSIS Traceback (most recent call last): TypeError: 'a' is an invalid keyword argument for this function @@ -23,7 +23,7 @@ __doc__ = u""" (1, 2, 3, (4, 5, 6, 7, 8, 9)) >>> grail(1,2) #doctest: +ELLIPSIS Traceback (most recent call last): - TypeError: function takes exactly 3 arguments (2 given) + TypeError: grail() takes exactly 3 arguments (2 given) >>> grail(1,2,3, a=1) #doctest: +ELLIPSIS Traceback (most recent call last): TypeError: 'a' is an invalid keyword argument for this function @@ -32,7 +32,7 @@ __doc__ = u""" (1, 2, 3, ()) >>> swallow(1,2,3,4) Traceback (most recent call last): - TypeError: function takes at most 3 positional arguments (4 given) + TypeError: swallow() takes at most 3 positional arguments (4 given) >>> swallow(1,2,3, a=1, b=2) (1, 2, 3, (('a', 1), ('b', 2))) >>> swallow(1,2,3, x=1) #doctest: +ELLIPSIS @@ -68,13 +68,13 @@ __doc__ = u""" (('a', 1), ('b', 2)) >>> onlyk(1) Traceback (most recent call last): - TypeError: function takes at most 0 positional arguments (1 given) + TypeError: onlyk() takes at most 0 positional arguments (1 given) >>> onlyk(1, 2) Traceback (most recent call last): - TypeError: function takes at most 0 positional arguments (2 given) + TypeError: onlyk() takes at most 0 positional arguments (2 given) >>> onlyk(1, a=1, b=2) Traceback (most recent call last): - TypeError: function takes at most 0 positional arguments (1 given) + TypeError: onlyk() takes at most 0 positional arguments (1 given) >>> tk(a=1) (('a', 1),) @@ -88,10 +88,6 @@ __doc__ = u""" (1, ('a', 1), ('b', 2)) """ -import sys, re -if sys.version_info >= (2,6): - __doc__ = re.sub(u"Error: (.*)exactly(.*)", u"Error: \\1at most\\2", __doc__) - import sys, re if sys.version_info >= (2,6): __doc__ = re.sub(u"(ELLIPSIS[^>]*Error: )[^\n]*\n", u"\\1...\n", __doc__, re.M) diff --git a/tests/run/kwonlyargs.pyx b/tests/run/kwonlyargs.pyx index 3bfd6e0f..710dc476 100644 --- a/tests/run/kwonlyargs.pyx +++ b/tests/run/kwonlyargs.pyx @@ -2,20 +2,20 @@ __doc__ = u""" >>> b(1,2,3) >>> b(1,2,3,4) Traceback (most recent call last): - TypeError: function takes exactly 3 arguments (4 given) + TypeError: b() takes at most 3 positional arguments (4 given) >>> c(1,2) >>> c(1,2,3) >>> c(1,2,3,4) Traceback (most recent call last): - TypeError: function takes at most 3 arguments (4 given) + TypeError: c() takes at most 3 positional arguments (4 given) >>> d(1,2) >>> d(1,2, c=1) >>> d(1,2,3) Traceback (most recent call last): - TypeError: function takes at most 2 positional arguments (3 given) + TypeError: d() takes at most 2 positional arguments (3 given) >>> d(1,2, d=1) Traceback (most recent call last): TypeError: 'd' is an invalid keyword argument for this function @@ -27,14 +27,14 @@ __doc__ = u""" >>> e(1,2,3) >>> e(1,2,3,4) Traceback (most recent call last): - TypeError: function takes at most 3 positional arguments (4 given) + TypeError: e() takes at most 3 positional arguments (4 given) >>> f(1,2, c=1) >>> f(1,2, c=1, d=2) >>> f(1,2,3) Traceback (most recent call last): - TypeError: function takes at most 2 positional arguments (3 given) + TypeError: f() takes at most 2 positional arguments (3 given) >>> f(1,2) Traceback (most recent call last): TypeError: required keyword argument 'c' is missing @@ -48,7 +48,7 @@ __doc__ = u""" >>> g(1,2,3) Traceback (most recent call last): - TypeError: function takes at most 2 positional arguments (3 given) + TypeError: g() takes at most 2 positional arguments (3 given) >>> g(1,2) Traceback (most recent call last): TypeError: required keyword argument 'c' is missing @@ -81,10 +81,6 @@ __doc__ = u""" TypeError: required keyword argument 'f' is missing """ -import sys, re -if sys.version_info >= (2,6): - __doc__ = re.sub(u"Error: (.*)exactly(.*)", u"Error: \\1at most\\2", __doc__) - def b(a, b, c): pass diff --git a/tests/run/kwonlyargscall.pyx b/tests/run/kwonlyargscall.pyx index b7da7930..415ee88a 100644 --- a/tests/run/kwonlyargscall.pyx +++ b/tests/run/kwonlyargscall.pyx @@ -2,20 +2,20 @@ __doc__ = u""" >>> call3(b) >>> call4(b) Traceback (most recent call last): - TypeError: function takes exactly 3 arguments (4 given) + TypeError: b() takes at most 3 positional arguments (4 given) >>> call2(c) >>> call3(c) >>> call4(c) Traceback (most recent call last): - TypeError: function takes at most 3 arguments (4 given) + TypeError: c() takes at most 3 positional arguments (4 given) >>> call2(d) >>> call2c(d) >>> call3(d) Traceback (most recent call last): - TypeError: function takes at most 2 positional arguments (3 given) + TypeError: d() takes at most 2 positional arguments (3 given) >>> call2d(d) Traceback (most recent call last): TypeError: 'd' is an invalid keyword argument for this function @@ -27,14 +27,14 @@ __doc__ = u""" >>> call3(e) >>> call4(e) Traceback (most recent call last): - TypeError: function takes at most 3 positional arguments (4 given) + TypeError: e() takes at most 3 positional arguments (4 given) >>> call2c(f) >>> call2cd(f) >>> call3(f) Traceback (most recent call last): - TypeError: function takes at most 2 positional arguments (3 given) + TypeError: f() takes at most 2 positional arguments (3 given) >>> call2(f) Traceback (most recent call last): TypeError: required keyword argument 'c' is missing @@ -48,7 +48,7 @@ __doc__ = u""" >>> call3(g) Traceback (most recent call last): - TypeError: function takes at most 2 positional arguments (3 given) + TypeError: g() takes at most 2 positional arguments (3 given) >>> call2(g) Traceback (most recent call last): TypeError: required keyword argument 'c' is missing @@ -132,6 +132,10 @@ def call2cefd(f): def call2cfex(f): f(1,2, c=1, f=2, e=0, x=25) +def call6argscfexy(f): + args = (1,2,3,4,5,6) + f(*args, c=1, f=2, e=3, x=25, y=11) + def call6cfexy(f): f(1,2,3,4,5,6, c=1, f=2, e=3, x=25, y=11) diff --git a/tests/run/starargs.pyx b/tests/run/starargs.pyx index 487d6b2f..d16a2d97 100644 --- a/tests/run/starargs.pyx +++ b/tests/run/starargs.pyx @@ -3,10 +3,10 @@ __doc__ = u""" (1, 2, 3) >>> spam(1,2) #doctest: +ELLIPSIS Traceback (most recent call last): - TypeError: function takes exactly 3 arguments (2 given) + TypeError: spam() takes at least 3 positional arguments (2 given) >>> spam(1,2,3,4) Traceback (most recent call last): - TypeError: function takes exactly 3 arguments (4 given) + TypeError: spam() takes at most 3 positional arguments (4 given) >>> spam(1,2,3, a=1) #doctest: +ELLIPSIS Traceback (most recent call last): TypeError: 'a' is an invalid keyword argument for this function @@ -19,7 +19,7 @@ __doc__ = u""" (1, 2, 3, (4, 5, 6, 7, 8, 9)) >>> grail(1,2) #doctest: +ELLIPSIS Traceback (most recent call last): - TypeError: function takes exactly 3 arguments (2 given) + TypeError: grail() takes at least 3 positional arguments (2 given) >>> grail(1,2,3, a=1) #doctest: +ELLIPSIS Traceback (most recent call last): TypeError: 'a' is an invalid keyword argument for this function @@ -28,7 +28,7 @@ __doc__ = u""" (1, 2, 3, ()) >>> swallow(1,2,3,4) Traceback (most recent call last): - TypeError: function takes at most 3 positional arguments (4 given) + TypeError: swallow() takes at most 3 positional arguments (4 given) >>> swallow(1,2,3, a=1, b=2) (1, 2, 3, (('a', 1), ('b', 2))) >>> swallow(1,2,3, x=1) #doctest: +ELLIPSIS @@ -64,13 +64,13 @@ __doc__ = u""" (('a', 1), ('b', 2)) >>> onlyk(1) Traceback (most recent call last): - TypeError: function takes at most 0 positional arguments (1 given) + TypeError: onlyk() takes at most 0 positional arguments (1 given) >>> onlyk(1, 2) Traceback (most recent call last): - TypeError: function takes at most 0 positional arguments (2 given) + TypeError: onlyk() takes at most 0 positional arguments (2 given) >>> onlyk(1, a=1, b=2) Traceback (most recent call last): - TypeError: function takes at most 0 positional arguments (1 given) + TypeError: onlyk() takes at most 0 positional arguments (1 given) >>> tk(a=1) (('a', 1),) @@ -84,10 +84,6 @@ __doc__ = u""" (1, ('a', 1), ('b', 2)) """ -import sys, re -if sys.version_info >= (2,6): - __doc__ = re.sub(u"Error: (.*)exactly(.*)", u"Error: \\1at most\\2", __doc__) - import sys, re if sys.version_info >= (2,6): __doc__ = re.sub(u"(ELLIPSIS[^>]*Error: )[^\n]*\n", u"\\1...\n", __doc__, re.M) -- 2.26.2