Commit f5c283f7 authored by Stefan Behnel's avatar Stefan Behnel

support for keyword-only arguments and required keywords (PEP 3102)

parent b8e78260
......@@ -332,6 +332,7 @@ class CArgDeclNode(Node):
# default ExprNode or None
# default_entry Symtab.Entry Entry for the variable holding the default value
# is_self_arg boolean Is the "self" arg of an extension type method
# kw_only boolean Is a keyword-only argument
is_self_arg = 0
......@@ -1051,9 +1052,12 @@ class DefNode(FuncDefNode):
arg_addrs = []
arg_formats = []
default_seen = 0
kw_only_args = []
for arg in self.args:
arg_entry = arg.entry
if arg.is_generic:
if arg.kw_only:
kw_only_args.append(arg_entry)
if arg.default:
code.putln(
"%s = %s;" % (
......@@ -1062,7 +1066,7 @@ class DefNode(FuncDefNode):
if not default_seen:
arg_formats.append("|")
default_seen = 1
elif default_seen:
elif default_seen and not arg.kw_only:
error(arg.pos, "Non-default argument following default argument")
if arg.needs_conversion:
arg_addrs.append("&" + arg.hdr_cname)
......@@ -1076,6 +1080,18 @@ class DefNode(FuncDefNode):
error(arg.pos,
"Cannot convert Python object argument to type '%s' (when parsing input arguments)"
% arg.type)
error_return_code = "return %s;" % self.error_value()
if kw_only_args:
max_normal_args = len(self.args) - len(kw_only_args)
code.putln("if (%s && PyTuple_GET_SIZE(%s) > %d) {" % (
Naming.args_cname,
Naming.args_cname,
max_normal_args))
code.putln('PyErr_Format(PyExc_TypeError, "function takes at most %d non-keyword arguments (%%d given)", PyTuple_GET_SIZE(%s));' % (
max_normal_args,
Naming.args_cname))
code.putln(error_return_code)
code.putln("}")
argformat = '"%s"' % string.join(arg_formats, "")
has_starargs = self.star_arg is not None or self.starstar_arg is not None
if has_starargs:
......@@ -1086,7 +1102,6 @@ class DefNode(FuncDefNode):
code.put(
'if (unlikely(!PyArg_ParseTupleAndKeywords(%s))) ' %
pt_argstring)
error_return_code = "return %s;" % self.error_value()
if has_starargs:
code.putln("{")
code.put_xdecref(Naming.args_cname, py_object_type)
......@@ -1097,7 +1112,33 @@ class DefNode(FuncDefNode):
code.putln("}")
else:
code.putln(error_return_code)
# check that all required keywords were passed
required_keyword_entries = []
kw_checks = []
for arg in self.args:
if arg.is_generic and arg.kw_only and not arg.default:
arg_entry = arg.entry
required_keyword_entries.append(arg_entry)
kw_checks.append("!" + arg_entry.cname)
if required_keyword_entries:
kw_check = ' || '.join(kw_checks)
code.putln("if (unlikely(%s)) {" % kw_check)
for entry in required_keyword_entries:
kw_checks.pop()
if kw_checks:
code.putln('if (!%s) {' % entry.cname)
code.putln('PyErr_SetString(PyExc_TypeError, "keyword argument \'%s\' is required");' % (
entry.name))
if kw_checks:
code.put("} else ")
if has_starargs:
code.put_xdecref(Naming.args_cname, py_object_type)
code.put_xdecref(Naming.kwds_cname, py_object_type)
self.generate_arg_xdecref(self.star_arg, code)
self.generate_arg_xdecref(self.starstar_arg, code)
code.putln(error_return_code)
code.putln("}")
def put_stararg_decrefs(self, code):
if self.star_arg or self.starstar_arg:
code.put_xdecref(Naming.args_cname, py_object_type)
......@@ -1128,7 +1169,7 @@ class DefNode(FuncDefNode):
star_arg_addr,
starstar_arg_addr,
self.error_value()))
def generate_argument_conversion_code(self, code):
# Generate code to convert arguments from
# signature type to declared type, if needed.
......
......@@ -1529,15 +1529,15 @@ def p_c_func_options(s):
c_arg_list_terminators = ('*', '**', '.', ')')
c_arg_list_trailers = ('.', '*', '**')
def p_c_arg_list(s, in_pyfunc, cmethod_flag = 0):
def p_c_arg_list(s, in_pyfunc, cmethod_flag = 0, kw_only = 0):
args = []
if s.sy not in c_arg_list_terminators:
args.append(p_c_arg_decl(s, in_pyfunc, cmethod_flag))
args.append(p_c_arg_decl(s, in_pyfunc, cmethod_flag, kw_only))
while s.sy == ',':
s.next()
if s.sy in c_arg_list_terminators:
break
args.append(p_c_arg_decl(s, in_pyfunc))
args.append(p_c_arg_decl(s, in_pyfunc, kw_only = kw_only))
return args
def p_optional_ellipsis(s):
......@@ -1547,7 +1547,7 @@ def p_optional_ellipsis(s):
else:
return 0
def p_c_arg_decl(s, in_pyfunc, cmethod_flag = 0):
def p_c_arg_decl(s, in_pyfunc, cmethod_flag = 0, kw_only = 0):
pos = s.position()
not_none = 0
default = None
......@@ -1569,7 +1569,8 @@ def p_c_arg_decl(s, in_pyfunc, cmethod_flag = 0):
base_type = base_type,
declarator = declarator,
not_none = not_none,
default = default)
default = default,
kw_only = kw_only)
def p_cdef_statement(s, level, visibility = 'private'):
pos = s.position()
......@@ -1771,12 +1772,20 @@ def p_def_statement(s):
starstar_arg = None
if s.sy == '*':
s.next()
star_arg = p_py_arg_decl(s)
if s.sy == ',':
s.next()
if s.sy == '**':
if s.sy == 'IDENT':
args.extend(p_c_arg_list(s, in_pyfunc = 1, kw_only = 1))
else:
star_arg = p_py_arg_decl(s)
if s.sy == ',':
s.next()
starstar_arg = p_py_arg_decl(s)
if s.sy == '**':
s.next()
starstar_arg = p_py_arg_decl(s)
elif s.sy == 'IDENT':
args.extend(p_c_arg_list(s, in_pyfunc = 1,
kw_only = 1))
elif s.sy == '**':
s.next()
starstar_arg = p_py_arg_decl(s)
......
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