Commit dfaa72eb authored by Stefan Behnel's avatar Stefan Behnel

fixed error handling for required keyword arguments

cosolidate exception messages
cleanup of utility code
parent b6b0f9a0
...@@ -1336,18 +1336,10 @@ class DefNode(FuncDefNode): ...@@ -1336,18 +1336,10 @@ 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(raise_argtuple_invalid_utility_code)
env.use_utility_code(keyword_check_utility_code) env.use_utility_code(raise_keyword_required_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)
if not self.signature_has_nongeneric_args():
env.use_utility_code(keyword_string_check_utility_code)
elif self.starstar_arg:
env.use_utility_code(split_keywords_utility_code)
if self.num_required_kw_args: if self.num_required_kw_args:
env.use_utility_code(check_keywords_utility_code) env.use_utility_code(check_required_keywords_utility_code)
def analyse_signature(self, env): def analyse_signature(self, env):
any_type_tests_needed = 0 any_type_tests_needed = 0
...@@ -1560,17 +1552,14 @@ class DefNode(FuncDefNode): ...@@ -1560,17 +1552,14 @@ class DefNode(FuncDefNode):
Naming.kwdlist_cname) Naming.kwdlist_cname)
for arg in self.args: for arg in self.args:
if arg.is_generic: if arg.is_generic:
code.put( code.put('"%s",' % arg.name.utf8encode())
'"%s",' %
arg.name.utf8encode())
if arg.kw_only and not arg.default: if arg.kw_only and not arg.default:
has_reqd_kwds = 1 has_reqd_kwds = 1
flag = "1" flag = "1"
else: else:
flag = "0" flag = "0"
reqd_kw_flags.append(flag) reqd_kw_flags.append(flag)
code.putln( code.putln("0};")
"0};")
if has_reqd_kwds: if has_reqd_kwds:
flags_name = Naming.reqd_kwds_cname flags_name = Naming.reqd_kwds_cname
self.reqd_kw_flags_cname = flags_name self.reqd_kw_flags_cname = flags_name
...@@ -1696,6 +1685,8 @@ class DefNode(FuncDefNode): ...@@ -1696,6 +1685,8 @@ class DefNode(FuncDefNode):
if not self.star_arg: if not self.star_arg:
self.generate_positional_args_check(0, code) self.generate_positional_args_check(0, code)
code.globalstate.use_utility_code(keyword_string_check_utility_code)
code.putln( code.putln(
"if (unlikely(%s) && unlikely(!__Pyx_CheckKeywordStrings(%s, \"%s\", %d))) return %s;" % ( "if (unlikely(%s) && unlikely(!__Pyx_CheckKeywordStrings(%s, \"%s\", %d))) return %s;" % (
Naming.kwds_cname, Naming.kwds_cname, self.name, Naming.kwds_cname, Naming.kwds_cname, self.name,
...@@ -1721,6 +1712,7 @@ class DefNode(FuncDefNode): ...@@ -1721,6 +1712,7 @@ class DefNode(FuncDefNode):
def generate_stararg_getting_code(self, max_positional_args, code): def generate_stararg_getting_code(self, max_positional_args, code):
if self.star_arg: if self.star_arg:
code.globalstate.use_utility_code(split_stararg_utility_code)
star_arg_cname = self.star_arg.entry.cname star_arg_cname = self.star_arg.entry.cname
code.putln("if (likely(PyTuple_GET_SIZE(%s) <= %d)) {" % ( code.putln("if (likely(PyTuple_GET_SIZE(%s) <= %d)) {" % (
Naming.args_cname, max_positional_args)) Naming.args_cname, max_positional_args))
...@@ -1738,6 +1730,7 @@ class DefNode(FuncDefNode): ...@@ -1738,6 +1730,7 @@ class DefNode(FuncDefNode):
self.star_arg.entry.xdecref_cleanup = 0 self.star_arg.entry.xdecref_cleanup = 0
if self.starstar_arg: if self.starstar_arg:
code.globalstate.use_utility_code(split_keywords_utility_code)
handle_error = 1 handle_error = 1
code.put( code.put(
'if (unlikely(__Pyx_SplitKeywords(&%s, %s, &%s, %s, PyTuple_GET_SIZE(%s), "%s") < 0)) ' % ( 'if (unlikely(__Pyx_SplitKeywords(&%s, %s, &%s, %s, PyTuple_GET_SIZE(%s), "%s") < 0)) ' % (
...@@ -1772,6 +1765,9 @@ class DefNode(FuncDefNode): ...@@ -1772,6 +1765,9 @@ class DefNode(FuncDefNode):
if self.star_arg or self.starstar_arg: if self.star_arg or self.starstar_arg:
self.generate_stararg_getting_code(max_positional_args, code) self.generate_stararg_getting_code(max_positional_args, code)
code.globalstate.use_utility_code(raise_double_keywords_utility_code)
code.globalstate.use_utility_code(keyword_check_utility_code)
# --- optimised code when we receive keyword arguments # --- optimised code when we receive keyword arguments
code.putln("if (unlikely(%s) && (PyDict_Size(%s) > 0)) {" % ( code.putln("if (unlikely(%s) && (PyDict_Size(%s) > 0)) {" % (
Naming.kwds_cname, Naming.kwds_cname)) Naming.kwds_cname, Naming.kwds_cname))
...@@ -1787,7 +1783,7 @@ class DefNode(FuncDefNode): ...@@ -1787,7 +1783,7 @@ class DefNode(FuncDefNode):
Naming.args_cname) Naming.args_cname)
code.putln("if (unlikely(PyDict_GetItemString(%s, %s[arg]))) {" % ( code.putln("if (unlikely(PyDict_GetItemString(%s, %s[arg]))) {" % (
Naming.kwds_cname, Naming.kwdlist_cname)) Naming.kwds_cname, Naming.kwdlist_cname))
code.put( '__Pyx_RaiseDoubleKeywordsError("%s", %s[arg]); ' % ( code.put('__Pyx_RaiseDoubleKeywordsError("%s", %s[arg]); ' % (
self.name.utf8encode(), Naming.kwdlist_cname)) self.name.utf8encode(), Naming.kwdlist_cname))
code.putln(code.error_goto(self.pos)) code.putln(code.error_goto(self.pos))
code.putln('}') code.putln('}')
...@@ -1799,6 +1795,12 @@ class DefNode(FuncDefNode): ...@@ -1799,6 +1795,12 @@ class DefNode(FuncDefNode):
code.putln('values[arg] = PyDict_GetItemString(%s, %s[arg]);' % ( code.putln('values[arg] = PyDict_GetItemString(%s, %s[arg]);' % (
Naming.kwds_cname, Naming.kwdlist_cname)) Naming.kwds_cname, Naming.kwdlist_cname))
code.putln('if (values[arg]) kw_args--;'); code.putln('if (values[arg]) kw_args--;');
if self.num_required_kw_args:
code.putln('else if (%s[arg]) {' % Naming.reqd_kwds_cname)
code.put('__Pyx_RaiseKeywordRequired("%s", %s[arg]); ' %(
self.name.utf8encode(), Naming.kwdlist_cname))
code.putln(code.error_goto(self.pos))
code.putln('}')
code.putln('}') code.putln('}')
# raise an error if not all keywords were read # raise an error if not all keywords were read
...@@ -1810,28 +1812,12 @@ class DefNode(FuncDefNode): ...@@ -1810,28 +1812,12 @@ class DefNode(FuncDefNode):
# convert arg values to their final type and assign them # convert arg values to their final type and assign them
default_seen = False default_seen = False
i = 0 for i, arg in enumerate(tuple(positional_args) + tuple(kw_only_args)):
for arg in self.args:
if arg.is_self_arg:
continue
if arg.default: if arg.default:
default_seen = True
if default_seen:
code.putln("if (values[%d]) {" % i) code.putln("if (values[%d]) {" % i)
self.generate_arg_assignment(arg, "values[%d]" % i, code) self.generate_arg_assignment(arg, "values[%d]" % i, code)
if default_seen: if arg.default:
if not arg.default:
code.putln('} else {')
if arg.kw_only:
code.put('PyErr_Format(PyExc_TypeError, "%s() needs keyword-only argument %s"); ' % (
self.name.utf8encode(), arg.name.utf8encode()))
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('}') code.putln('}')
i += 1
# --- optimised code when we do not receive any keyword arguments # --- optimised code when we do not receive any keyword arguments
if self.num_required_kw_args: if self.num_required_kw_args:
...@@ -1839,9 +1825,10 @@ class DefNode(FuncDefNode): ...@@ -1839,9 +1825,10 @@ class DefNode(FuncDefNode):
for arg in self.args: for arg in self.args:
if arg.kw_only and not arg.default: if arg.kw_only and not arg.default:
required_arg = arg required_arg = arg
break
code.putln('} else {') code.putln('} else {')
code.put('PyErr_Format(PyExc_TypeError, "%s() needs keyword-only argument %s");' % ( code.putln('__Pyx_RaiseKeywordRequired("%s", "%s");' % (
self.name.utf8encode(), required_arg.name.utf8encode())) self.name.utf8encode(), required_arg.name.utf8encode()))
code.putln(code.error_goto(self.pos)) code.putln(code.error_goto(self.pos))
code.putln('}') code.putln('}')
else: else:
...@@ -4282,7 +4269,7 @@ static int __Pyx_ArgTypeTest(PyObject *obj, PyTypeObject *type, int none_allowed ...@@ -4282,7 +4269,7 @@ static int __Pyx_ArgTypeTest(PyObject *obj, PyTypeObject *type, int none_allowed
# new reference in *args2. Does not touch any of its arguments on # new reference in *args2. Does not touch any of its arguments on
# failure. # failure.
get_stararg_utility_code = [ split_stararg_utility_code = [
""" """
static INLINE int __Pyx_SplitStarArg(PyObject **args, Py_ssize_t nargs, PyObject **args2); /*proto*/ static INLINE int __Pyx_SplitStarArg(PyObject **args, Py_ssize_t nargs, PyObject **args2); /*proto*/
""",""" ""","""
...@@ -4313,7 +4300,7 @@ static INLINE int __Pyx_SplitStarArg( ...@@ -4313,7 +4300,7 @@ static INLINE int __Pyx_SplitStarArg(
# many or too few positional arguments were found. This handles # many or too few positional arguments were found. This handles
# Py_ssize_t formatting correctly. # Py_ssize_t formatting correctly.
raise_argtuple_too_long_utility_code = [ raise_argtuple_invalid_utility_code = [
""" """
static INLINE void __Pyx_RaiseArgtupleInvalid(char* func_name, static INLINE void __Pyx_RaiseArgtupleInvalid(char* func_name,
Py_ssize_t num_min, Py_ssize_t num_max, Py_ssize_t num_found); /*proto*/ Py_ssize_t num_min, Py_ssize_t num_max, Py_ssize_t num_found); /*proto*/
...@@ -4324,27 +4311,59 @@ static INLINE void __Pyx_RaiseArgtupleInvalid( ...@@ -4324,27 +4311,59 @@ static INLINE void __Pyx_RaiseArgtupleInvalid(
Py_ssize_t num_max, Py_ssize_t num_max,
Py_ssize_t num_found) Py_ssize_t num_found)
{ {
Py_ssize_t num_expected;
char* message;
if (num_found < num_min) { if (num_found < num_min) {
PyErr_Format(PyExc_TypeError, num_expected = num_min;
message =
#if PY_VERSION_HEX < 0x02050000 #if PY_VERSION_HEX < 0x02050000
"%s() takes at least %d positional arguments (%d given)", "%s() takes at least %d positional arguments (%d given)";
#elif PY_MAJOR_VERSION >= 3 #elif PY_MAJOR_VERSION >= 3
"%U() takes at most %zd positional arguments (%zd given)", "%U() takes at most %zd positional arguments (%zd given)";
#else #else
"%s() takes at least %zd positional arguments (%zd given)", "%s() takes at least %zd positional arguments (%zd given)";
#endif #endif
func_name, num_min, num_found);
} else { } else {
PyErr_Format(PyExc_TypeError, num_expected = num_max;
message =
#if PY_VERSION_HEX < 0x02050000 #if PY_VERSION_HEX < 0x02050000
"%s() takes at most %d positional arguments (%d given)", "%s() takes at most %d positional arguments (%d given)";
#elif PY_MAJOR_VERSION >= 3 #elif PY_MAJOR_VERSION >= 3
"%U() takes at most %zd positional arguments (%zd given)", "%U() takes at most %zd positional arguments (%zd given)";
#else #else
"%s() takes at most %zd positional arguments (%zd given)", "%s() takes at most %zd positional arguments (%zd given)";
#endif #endif
func_name, num_max, num_found);
} }
PyErr_Format(PyExc_TypeError, message, func_name,
num_expected, num_found);
}
"""]
raise_keyword_required_utility_code = [
"""
static INLINE void __Pyx_RaiseKeywordRequired(char* func_name, char* kw_name); /*proto*/
""","""
static INLINE void __Pyx_RaiseKeywordRequired(
char* func_name,
char* kw_name)
{
PyErr_Format(PyExc_TypeError,
"%s() needs keyword-only argument %s", func_name, kw_name);
}
"""]
raise_double_keywords_utility_code = [
"""
static INLINE void __Pyx_RaiseDoubleKeywordsError(
const char* func_name, const char* kw_name); /*proto*/
""","""
static INLINE void __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);
} }
"""] """]
...@@ -4539,8 +4558,7 @@ arg_passed_twice: ...@@ -4539,8 +4558,7 @@ arg_passed_twice:
"%s() got multiple values for keyword argument '%s'", func_name, *p); "%s() got multiple values for keyword argument '%s'", func_name, *p);
goto bad; goto bad;
missing_kwarg: missing_kwarg:
PyErr_Format(PyExc_TypeError, __Pyx_RaiseKeywordRequired(func_name, *p);
"%s() needs keyword-only argument %s", func_name, *p);
bad: bad:
Py_XDECREF(s); Py_XDECREF(s);
Py_XDECREF(kwds1); Py_XDECREF(kwds1);
...@@ -4549,7 +4567,7 @@ bad: ...@@ -4549,7 +4567,7 @@ bad:
} }
"""] """]
check_keywords_utility_code = [ check_required_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[], Py_ssize_t num_posargs, char* func_name); /*proto*/ char rqd_kwds[], Py_ssize_t num_posargs, char* func_name); /*proto*/
...@@ -4588,38 +4606,7 @@ arg_passed_twice: ...@@ -4588,38 +4606,7 @@ arg_passed_twice:
"%s() got multiple values for keyword argument '%s'", func_name, *p); "%s() got multiple values for keyword argument '%s'", func_name, *p);
return -1; return -1;
missing_kwarg: missing_kwarg:
PyErr_Format(PyExc_TypeError, __Pyx_RaiseKeywordRequired(func_name, *p);
"required keyword argument '%s' is missing", *p);
return -1;
}
"""]
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; return -1;
} }
"""] """]
......
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