Commit 086f4098 authored by Stefan Behnel's avatar Stefan Behnel

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)
parent 5091aefd
...@@ -1336,17 +1336,18 @@ class DefNode(FuncDefNode): ...@@ -1336,17 +1336,18 @@ class DefNode(FuncDefNode):
self.declare_pyfunction(env) self.declare_pyfunction(env)
self.analyse_signature(env) self.analyse_signature(env)
self.return_type = self.entry.signature.return_type() 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.signature_has_generic_args():
if self.star_arg: if self.star_arg:
env.use_utility_code(get_stararg_utility_code) 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(): 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: 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: 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): def analyse_signature(self, env):
any_type_tests_needed = 0 any_type_tests_needed = 0
...@@ -1644,7 +1645,7 @@ class DefNode(FuncDefNode): ...@@ -1644,7 +1645,7 @@ class DefNode(FuncDefNode):
if has_star_or_kw_args: if has_star_or_kw_args:
self.generate_stararg_getting_code(code) 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) positional_args, arg_formats, arg_addrs, code)
code.error_label = old_error_label code.error_label = old_error_label
...@@ -1663,7 +1664,7 @@ class DefNode(FuncDefNode): ...@@ -1663,7 +1664,7 @@ class DefNode(FuncDefNode):
code.putln("return %s;" % self.error_value()) code.putln("return %s;" % self.error_value())
code.put_label(end_label) 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): arg_formats, arg_addrs, code):
# Unpack inplace if it's simple # Unpack inplace if it's simple
if not self.num_required_kw_args: if not self.num_required_kw_args:
...@@ -1689,20 +1690,7 @@ class DefNode(FuncDefNode): ...@@ -1689,20 +1690,7 @@ class DefNode(FuncDefNode):
code.putln('if (PyTuple_GET_SIZE(%s) > %s) {' % (Naming.args_cname, i)) code.putln('if (PyTuple_GET_SIZE(%s) > %s) {' % (Naming.args_cname, i))
closing += 1 closing += 1
item = "PyTuple_GET_ITEM(%s, %s)" % (Naming.args_cname, i) item = "PyTuple_GET_ITEM(%s, %s)" % (Naming.args_cname, i)
if arg.type.is_pyobject: self.generate_arg_assignment(arg, item, code)
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)
i += 1 i += 1
for _ in range(closing): for _ in range(closing):
code.putln('}') code.putln('}')
...@@ -1722,6 +1710,22 @@ class DefNode(FuncDefNode): ...@@ -1722,6 +1710,22 @@ class DefNode(FuncDefNode):
if not self.num_required_kw_args: if not self.num_required_kw_args:
code.putln('}') 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): def put_stararg_decrefs(self, code):
if self.star_arg: if self.star_arg:
code.put_decref(Naming.args_cname, py_object_type) code.put_decref(Naming.args_cname, py_object_type)
...@@ -1778,8 +1782,7 @@ class DefNode(FuncDefNode): ...@@ -1778,8 +1782,7 @@ class DefNode(FuncDefNode):
code.put_incref(Naming.args_cname, py_object_type) code.put_incref(Naming.args_cname, py_object_type)
code.put("%s = %s; " % (star_arg_cname, Naming.empty_tuple)) code.put("%s = %s; " % (star_arg_cname, Naming.empty_tuple))
code.put_incref(Naming.empty_tuple, py_object_type) code.put_incref(Naming.empty_tuple, py_object_type)
code.putln("}") code.putln("} else {")
code.putln("else {")
code.putln( code.putln(
"if (unlikely(__Pyx_SplitStarArg(&%s, %d, &%s) < 0)) return %s;" % ( "if (unlikely(__Pyx_SplitStarArg(&%s, %d, &%s) < 0)) return %s;" % (
Naming.args_cname, Naming.args_cname,
...@@ -1788,7 +1791,7 @@ class DefNode(FuncDefNode): ...@@ -1788,7 +1791,7 @@ class DefNode(FuncDefNode):
self.error_value())) self.error_value()))
code.putln("}") code.putln("}")
self.star_arg.entry.xdecref_cleanup = 0 self.star_arg.entry.xdecref_cleanup = 0
elif self.signature_has_generic_args(): else:
# make sure supernumerous positional arguments do not run # make sure supernumerous positional arguments do not run
# into keyword-only arguments and provide a more helpful # into keyword-only arguments and provide a more helpful
# message than PyArg_ParseTupelAndKeywords() # message than PyArg_ParseTupelAndKeywords()
...@@ -1798,18 +1801,31 @@ class DefNode(FuncDefNode): ...@@ -1798,18 +1801,31 @@ class DefNode(FuncDefNode):
if self.starstar_arg: if self.starstar_arg:
handle_error = 1 handle_error = 1
code.put( 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.kwds_cname,
Naming.kwdlist_cname, Naming.kwdlist_cname,
self.starstar_arg.entry.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 self.starstar_arg.entry.xdecref_cleanup = 0
elif self.num_required_kw_args: elif self.num_required_kw_args:
handle_error = 1 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.kwds_cname,
Naming.kwdlist_cname, Naming.kwdlist_cname,
self.reqd_kw_flags_cname)) Naming.args_cname,
self.name.utf8encode()))
if handle_error: if handle_error:
if self.star_arg: if self.star_arg:
...@@ -1821,11 +1837,109 @@ class DefNode(FuncDefNode): ...@@ -1821,11 +1837,109 @@ class DefNode(FuncDefNode):
else: else:
code.putln(error_return) 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): def generate_positional_args_check(self, code, nargs):
code.putln("if (unlikely(PyTuple_GET_SIZE(%s) > %d)) {" % ( code.putln("if (unlikely(PyTuple_GET_SIZE(%s) > %d)) {" % (
Naming.args_cname, nargs)) Naming.args_cname, nargs))
code.putln("__Pyx_RaiseArgtupleTooLong(%d, PyTuple_GET_SIZE(%s));" % ( code.putln('__Pyx_RaiseArgtupleInvalid("%s", 0, %d, PyTuple_GET_SIZE(%s));' % (
nargs, Naming.args_cname)) self.name.utf8encode(), nargs, Naming.args_cname))
code.putln("return %s;" % self.error_value()) code.putln("return %s;" % self.error_value())
code.putln("}") code.putln("}")
...@@ -4267,25 +4381,42 @@ static INLINE int __Pyx_SplitStarArg( ...@@ -4267,25 +4381,42 @@ static INLINE int __Pyx_SplitStarArg(
#------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------
# #
# __Pyx_RaiseArgtupleTooLong raises the correct exception when too # __Pyx_RaiseArgtupleInvalid raises the correct exception when too
# many positional arguments were found. This handles Py_ssize_t # many or too few positional arguments were found. This handles
# formatting correctly. # Py_ssize_t formatting correctly.
raise_argtuple_too_long_utility_code = [ 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( static INLINE void __Pyx_RaiseArgtupleInvalid(
Py_ssize_t num_expected, char* func_name,
Py_ssize_t num_min,
Py_ssize_t num_max,
Py_ssize_t num_found) Py_ssize_t num_found)
{ {
const char* error_message = if (num_found < num_min) {
#if PY_VERSION_HEX < 0x02050000 PyErr_Format(PyExc_TypeError,
"function takes at most %d positional arguments (%d given)"; #if PY_VERSION_HEX < 0x02050000
#else "%s() takes at least %d positional arguments (%d given)",
"function takes at most %zd positional arguments (%zd given)"; #elif PY_MAJOR_VERSION >= 3
#endif "%U() takes at most %zd positional arguments (%zd given)",
PyErr_Format(PyExc_TypeError, error_message, num_expected, num_found); #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( ...@@ -4295,7 +4426,7 @@ static INLINE void __Pyx_RaiseArgtupleTooLong(
# were passed to a function, or if any keywords were passed to a # were passed to a function, or if any keywords were passed to a
# function that does not accept them. # 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*/ static int __Pyx_CheckKeywordStrings(PyObject *kwdict, const char* function_name, int kw_allowed); /*proto*/
""",""" ""","""
...@@ -4332,6 +4463,68 @@ static int __Pyx_CheckKeywordStrings( ...@@ -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 # __Pyx_SplitKeywords splits the kwds dict into two parts one part
...@@ -4349,17 +4542,24 @@ static int __Pyx_CheckKeywordStrings( ...@@ -4349,17 +4542,24 @@ static int __Pyx_CheckKeywordStrings(
# the names in kwd_list, indicating required keyword arguments. If # the names in kwd_list, indicating required keyword arguments. If
# any of these are not present in kwds, an exception is raised. # 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[], \ 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( static int __Pyx_SplitKeywords(
PyObject **kwds, PyObject **kwds,
char *kwd_list[], char *kwd_list[],
PyObject **kwds2, PyObject **kwds2,
char rqd_kwds[]) char rqd_kwds[],
Py_ssize_t num_posargs,
char* func_name)
{ {
PyObject *s = 0, *x = 0, *kwds1 = 0; PyObject *s = 0, *x = 0, *kwds1 = 0;
int i; int i;
...@@ -4380,6 +4580,8 @@ static int __Pyx_SplitKeywords( ...@@ -4380,6 +4580,8 @@ static int __Pyx_SplitKeywords(
#endif #endif
x = PyDict_GetItem(*kwds, s); x = PyDict_GetItem(*kwds, s);
if (x) { if (x) {
if (i < num_posargs)
goto arg_passed_twice;
if (PyDict_SetItem(kwds1, s, x) < 0) if (PyDict_SetItem(kwds1, s, x) < 0)
goto bad; goto bad;
if (PyDict_DelItem(*kwds2, s) < 0) if (PyDict_DelItem(*kwds2, s) < 0)
...@@ -4404,6 +4606,10 @@ static int __Pyx_SplitKeywords( ...@@ -4404,6 +4606,10 @@ static int __Pyx_SplitKeywords(
*kwds = kwds1; *kwds = kwds1;
return 0; return 0;
arg_passed_twice:
PyErr_Format(PyExc_TypeError,
"%s() got multiple values for keyword argument '%s'", func_name, *p);
goto bad;
missing_kwarg: missing_kwarg:
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"required keyword argument '%s' is missing", *p); "required keyword argument '%s' is missing", *p);
...@@ -4415,23 +4621,32 @@ bad: ...@@ -4415,23 +4621,32 @@ bad:
} }
"""] """]
get_checkkeywords_utility_code = [ check_keywords_utility_code = [
""" """
static INLINE int __Pyx_CheckRequiredKeywords(PyObject *kwds, char *kwd_list[], 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( static INLINE int __Pyx_CheckRequiredKeywords(
PyObject *kwds, PyObject *kwds,
char *kwd_list[], char *kwd_list[],
char rqd_kwds[]) char rqd_kwds[],
Py_ssize_t num_posargs,
char* func_name)
{ {
int i; int i;
char **p; char **p;
if (kwds) { 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)) if (rqd_kwds[i] && !PyDict_GetItemString(kwds, *p))
goto missing_kwarg; goto missing_kwarg;
i++; p++;
}
} }
else { else {
for (i = 0, p = kwd_list; *p; i++, p++) for (i = 0, p = kwd_list; *p; i++, p++)
...@@ -4440,6 +4655,10 @@ static INLINE int __Pyx_CheckRequiredKeywords( ...@@ -4440,6 +4655,10 @@ static INLINE int __Pyx_CheckRequiredKeywords(
} }
return 0; return 0;
arg_passed_twice:
PyErr_Format(PyExc_TypeError,
"%s() got multiple values for keyword argument '%s'", func_name, *p);
return -1;
missing_kwarg: missing_kwarg:
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"required keyword argument '%s' is missing", *p); "required keyword argument '%s' is missing", *p);
...@@ -4447,6 +4666,36 @@ missing_kwarg: ...@@ -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 = [ unraisable_exception_utility_code = [
......
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
"""
__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)
...@@ -5,20 +5,20 @@ __doc__ = u""" ...@@ -5,20 +5,20 @@ __doc__ = u"""
>>> b(1,2,3) >>> b(1,2,3)
>>> b(1,2,3,4) >>> b(1,2,3,4)
Traceback (most recent call last): 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)
>>> c(1,2,3) >>> c(1,2,3)
>>> c(1,2,3,4) >>> c(1,2,3,4)
Traceback (most recent call last): 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)
>>> d(1,2, c=1) >>> d(1,2, c=1)
>>> d(1,2,3) >>> d(1,2,3)
Traceback (most recent call last): 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) >>> d(1,2, d=1)
Traceback (most recent call last): Traceback (most recent call last):
TypeError: 'd' is an invalid keyword argument for this function TypeError: 'd' is an invalid keyword argument for this function
...@@ -30,14 +30,14 @@ __doc__ = u""" ...@@ -30,14 +30,14 @@ __doc__ = u"""
>>> e(1,2,3) >>> e(1,2,3)
>>> e(1,2,3,4) >>> e(1,2,3,4)
Traceback (most recent call last): 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)
>>> f(1,2, c=1, d=2) >>> f(1,2, c=1, d=2)
>>> f(1,2,3) >>> f(1,2,3)
Traceback (most recent call last): 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) >>> f(1,2)
Traceback (most recent call last): Traceback (most recent call last):
TypeError: required keyword argument 'c' is missing TypeError: required keyword argument 'c' is missing
...@@ -51,7 +51,7 @@ __doc__ = u""" ...@@ -51,7 +51,7 @@ __doc__ = u"""
>>> g(1,2,3) #doctest: +ELLIPSIS >>> g(1,2,3) #doctest: +ELLIPSIS
Traceback (most recent call last): 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) >>> g(1,2)
Traceback (most recent call last): Traceback (most recent call last):
TypeError: required keyword argument 'c' is missing TypeError: required keyword argument 'c' is missing
......
...@@ -5,20 +5,20 @@ __doc__ = u""" ...@@ -5,20 +5,20 @@ __doc__ = u"""
>>> b(1,2,3) >>> b(1,2,3)
>>> b(1,2,3,4) >>> b(1,2,3,4)
Traceback (most recent call last): 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)
>>> c(1,2,3) >>> c(1,2,3)
>>> c(1,2,3,4) >>> c(1,2,3,4)
Traceback (most recent call last): 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)
>>> d(1,2, c=1) >>> d(1,2, c=1)
>>> d(1,2,3) >>> d(1,2,3)
Traceback (most recent call last): 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) >>> d(1,2, d=1)
Traceback (most recent call last): Traceback (most recent call last):
TypeError: 'd' is an invalid keyword argument for this function TypeError: 'd' is an invalid keyword argument for this function
...@@ -30,14 +30,14 @@ __doc__ = u""" ...@@ -30,14 +30,14 @@ __doc__ = u"""
>>> e(1,2,3) >>> e(1,2,3)
>>> e(1,2,3,4) >>> e(1,2,3,4)
Traceback (most recent call last): 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)
>>> f(1,2, c=1, d=2) >>> f(1,2, c=1, d=2)
>>> f(1,2,3) >>> f(1,2,3)
Traceback (most recent call last): 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) >>> f(1,2)
Traceback (most recent call last): Traceback (most recent call last):
TypeError: required keyword argument 'c' is missing TypeError: required keyword argument 'c' is missing
...@@ -51,7 +51,7 @@ __doc__ = u""" ...@@ -51,7 +51,7 @@ __doc__ = u"""
>>> g(1,2,3) >>> g(1,2,3)
Traceback (most recent call last): 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) >>> g(1,2)
Traceback (most recent call last): Traceback (most recent call last):
TypeError: required keyword argument 'c' is missing TypeError: required keyword argument 'c' is missing
...@@ -84,10 +84,6 @@ __doc__ = u""" ...@@ -84,10 +84,6 @@ __doc__ = u"""
TypeError: required keyword argument 'f' is missing 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: cdef class Ext:
def b(self, a, b, c): def b(self, a, b, c):
pass pass
......
...@@ -7,10 +7,10 @@ __doc__ = u""" ...@@ -7,10 +7,10 @@ __doc__ = u"""
(1, 2, 3) (1, 2, 3)
>>> spam(1,2) #doctest: +ELLIPSIS >>> spam(1,2) #doctest: +ELLIPSIS
Traceback (most recent call last): 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) >>> spam(1,2,3,4)
Traceback (most recent call last): 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 >>> spam(1,2,3, a=1) #doctest: +ELLIPSIS
Traceback (most recent call last): Traceback (most recent call last):
TypeError: 'a' is an invalid keyword argument for this function TypeError: 'a' is an invalid keyword argument for this function
...@@ -23,7 +23,7 @@ __doc__ = u""" ...@@ -23,7 +23,7 @@ __doc__ = u"""
(1, 2, 3, (4, 5, 6, 7, 8, 9)) (1, 2, 3, (4, 5, 6, 7, 8, 9))
>>> grail(1,2) #doctest: +ELLIPSIS >>> grail(1,2) #doctest: +ELLIPSIS
Traceback (most recent call last): 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 >>> grail(1,2,3, a=1) #doctest: +ELLIPSIS
Traceback (most recent call last): Traceback (most recent call last):
TypeError: 'a' is an invalid keyword argument for this function TypeError: 'a' is an invalid keyword argument for this function
...@@ -32,7 +32,7 @@ __doc__ = u""" ...@@ -32,7 +32,7 @@ __doc__ = u"""
(1, 2, 3, ()) (1, 2, 3, ())
>>> swallow(1,2,3,4) >>> swallow(1,2,3,4)
Traceback (most recent call last): 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) >>> swallow(1,2,3, a=1, b=2)
(1, 2, 3, (('a', 1), ('b', 2))) (1, 2, 3, (('a', 1), ('b', 2)))
>>> swallow(1,2,3, x=1) #doctest: +ELLIPSIS >>> swallow(1,2,3, x=1) #doctest: +ELLIPSIS
...@@ -68,13 +68,13 @@ __doc__ = u""" ...@@ -68,13 +68,13 @@ __doc__ = u"""
(('a', 1), ('b', 2)) (('a', 1), ('b', 2))
>>> onlyk(1) >>> onlyk(1)
Traceback (most recent call last): 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) >>> onlyk(1, 2)
Traceback (most recent call last): 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) >>> onlyk(1, a=1, b=2)
Traceback (most recent call last): 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) >>> tk(a=1)
(('a', 1),) (('a', 1),)
...@@ -88,10 +88,6 @@ __doc__ = u""" ...@@ -88,10 +88,6 @@ __doc__ = u"""
(1, ('a', 1), ('b', 2)) (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 import sys, re
if sys.version_info >= (2,6): if sys.version_info >= (2,6):
__doc__ = re.sub(u"(ELLIPSIS[^>]*Error: )[^\n]*\n", u"\\1...\n", __doc__, re.M) __doc__ = re.sub(u"(ELLIPSIS[^>]*Error: )[^\n]*\n", u"\\1...\n", __doc__, re.M)
......
...@@ -2,20 +2,20 @@ __doc__ = u""" ...@@ -2,20 +2,20 @@ __doc__ = u"""
>>> b(1,2,3) >>> b(1,2,3)
>>> b(1,2,3,4) >>> b(1,2,3,4)
Traceback (most recent call last): 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)
>>> c(1,2,3) >>> c(1,2,3)
>>> c(1,2,3,4) >>> c(1,2,3,4)
Traceback (most recent call last): 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)
>>> d(1,2, c=1) >>> d(1,2, c=1)
>>> d(1,2,3) >>> d(1,2,3)
Traceback (most recent call last): 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) >>> d(1,2, d=1)
Traceback (most recent call last): Traceback (most recent call last):
TypeError: 'd' is an invalid keyword argument for this function TypeError: 'd' is an invalid keyword argument for this function
...@@ -27,14 +27,14 @@ __doc__ = u""" ...@@ -27,14 +27,14 @@ __doc__ = u"""
>>> e(1,2,3) >>> e(1,2,3)
>>> e(1,2,3,4) >>> e(1,2,3,4)
Traceback (most recent call last): 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)
>>> f(1,2, c=1, d=2) >>> f(1,2, c=1, d=2)
>>> f(1,2,3) >>> f(1,2,3)
Traceback (most recent call last): 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) >>> f(1,2)
Traceback (most recent call last): Traceback (most recent call last):
TypeError: required keyword argument 'c' is missing TypeError: required keyword argument 'c' is missing
...@@ -48,7 +48,7 @@ __doc__ = u""" ...@@ -48,7 +48,7 @@ __doc__ = u"""
>>> g(1,2,3) >>> g(1,2,3)
Traceback (most recent call last): 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) >>> g(1,2)
Traceback (most recent call last): Traceback (most recent call last):
TypeError: required keyword argument 'c' is missing TypeError: required keyword argument 'c' is missing
...@@ -81,10 +81,6 @@ __doc__ = u""" ...@@ -81,10 +81,6 @@ __doc__ = u"""
TypeError: required keyword argument 'f' is missing 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): def b(a, b, c):
pass pass
......
...@@ -2,20 +2,20 @@ __doc__ = u""" ...@@ -2,20 +2,20 @@ __doc__ = u"""
>>> call3(b) >>> call3(b)
>>> call4(b) >>> call4(b)
Traceback (most recent call last): 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) >>> call2(c)
>>> call3(c) >>> call3(c)
>>> call4(c) >>> call4(c)
Traceback (most recent call last): 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) >>> call2(d)
>>> call2c(d) >>> call2c(d)
>>> call3(d) >>> call3(d)
Traceback (most recent call last): 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) >>> call2d(d)
Traceback (most recent call last): Traceback (most recent call last):
TypeError: 'd' is an invalid keyword argument for this function TypeError: 'd' is an invalid keyword argument for this function
...@@ -27,14 +27,14 @@ __doc__ = u""" ...@@ -27,14 +27,14 @@ __doc__ = u"""
>>> call3(e) >>> call3(e)
>>> call4(e) >>> call4(e)
Traceback (most recent call last): 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) >>> call2c(f)
>>> call2cd(f) >>> call2cd(f)
>>> call3(f) >>> call3(f)
Traceback (most recent call last): 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) >>> call2(f)
Traceback (most recent call last): Traceback (most recent call last):
TypeError: required keyword argument 'c' is missing TypeError: required keyword argument 'c' is missing
...@@ -48,7 +48,7 @@ __doc__ = u""" ...@@ -48,7 +48,7 @@ __doc__ = u"""
>>> call3(g) >>> call3(g)
Traceback (most recent call last): 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) >>> call2(g)
Traceback (most recent call last): Traceback (most recent call last):
TypeError: required keyword argument 'c' is missing TypeError: required keyword argument 'c' is missing
...@@ -132,6 +132,10 @@ def call2cefd(f): ...@@ -132,6 +132,10 @@ def call2cefd(f):
def call2cfex(f): def call2cfex(f):
f(1,2, c=1, f=2, e=0, x=25) 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): def call6cfexy(f):
f(1,2,3,4,5,6, c=1, f=2, e=3, x=25, y=11) f(1,2,3,4,5,6, c=1, f=2, e=3, x=25, y=11)
......
...@@ -3,10 +3,10 @@ __doc__ = u""" ...@@ -3,10 +3,10 @@ __doc__ = u"""
(1, 2, 3) (1, 2, 3)
>>> spam(1,2) #doctest: +ELLIPSIS >>> spam(1,2) #doctest: +ELLIPSIS
Traceback (most recent call last): 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) >>> spam(1,2,3,4)
Traceback (most recent call last): 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 >>> spam(1,2,3, a=1) #doctest: +ELLIPSIS
Traceback (most recent call last): Traceback (most recent call last):
TypeError: 'a' is an invalid keyword argument for this function TypeError: 'a' is an invalid keyword argument for this function
...@@ -19,7 +19,7 @@ __doc__ = u""" ...@@ -19,7 +19,7 @@ __doc__ = u"""
(1, 2, 3, (4, 5, 6, 7, 8, 9)) (1, 2, 3, (4, 5, 6, 7, 8, 9))
>>> grail(1,2) #doctest: +ELLIPSIS >>> grail(1,2) #doctest: +ELLIPSIS
Traceback (most recent call last): 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 >>> grail(1,2,3, a=1) #doctest: +ELLIPSIS
Traceback (most recent call last): Traceback (most recent call last):
TypeError: 'a' is an invalid keyword argument for this function TypeError: 'a' is an invalid keyword argument for this function
...@@ -28,7 +28,7 @@ __doc__ = u""" ...@@ -28,7 +28,7 @@ __doc__ = u"""
(1, 2, 3, ()) (1, 2, 3, ())
>>> swallow(1,2,3,4) >>> swallow(1,2,3,4)
Traceback (most recent call last): 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) >>> swallow(1,2,3, a=1, b=2)
(1, 2, 3, (('a', 1), ('b', 2))) (1, 2, 3, (('a', 1), ('b', 2)))
>>> swallow(1,2,3, x=1) #doctest: +ELLIPSIS >>> swallow(1,2,3, x=1) #doctest: +ELLIPSIS
...@@ -64,13 +64,13 @@ __doc__ = u""" ...@@ -64,13 +64,13 @@ __doc__ = u"""
(('a', 1), ('b', 2)) (('a', 1), ('b', 2))
>>> onlyk(1) >>> onlyk(1)
Traceback (most recent call last): 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) >>> onlyk(1, 2)
Traceback (most recent call last): 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) >>> onlyk(1, a=1, b=2)
Traceback (most recent call last): 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) >>> tk(a=1)
(('a', 1),) (('a', 1),)
...@@ -84,10 +84,6 @@ __doc__ = u""" ...@@ -84,10 +84,6 @@ __doc__ = u"""
(1, ('a', 1), ('b', 2)) (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 import sys, re
if sys.version_info >= (2,6): if sys.version_info >= (2,6):
__doc__ = re.sub(u"(ELLIPSIS[^>]*Error: )[^\n]*\n", u"\\1...\n", __doc__, re.M) __doc__ = re.sub(u"(ELLIPSIS[^>]*Error: )[^\n]*\n", u"\\1...\n", __doc__, re.M)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment