Commit 384a2497 authored by Josh Tobin's avatar Josh Tobin

Optimize argument parsing code for posonly args

parent cd76a42a
...@@ -3691,10 +3691,13 @@ class DefNodeWrapper(FuncDefNode): ...@@ -3691,10 +3691,13 @@ class DefNodeWrapper(FuncDefNode):
code.putln('{') code.putln('{')
all_args = tuple(positional_args) + tuple(kw_only_args) all_args = tuple(positional_args) + tuple(kw_only_args)
code.putln("static PyObject **%s[] = {%s};" % ( non_posonly_args = [arg for arg in all_args if not arg.pos_only]
Naming.pykwdlist_cname, do_generate_kw_unpacking = bool(non_posonly_args) or self.starstar_arg
','.join(['&%s' % code.intern_identifier(arg.name) if do_generate_kw_unpacking:
for arg in all_args if not arg.pos_only] + ['0']))) code.putln("static PyObject **%s[] = {%s};" % (
Naming.pykwdlist_cname,
','.join(['&%s' % code.intern_identifier(arg.name)
for arg in non_posonly_args] + ['0'])))
# Before being converted and assigned to the target variables, # Before being converted and assigned to the target variables,
# borrowed references to all unpacked argument values are # borrowed references to all unpacked argument values are
...@@ -3710,9 +3713,14 @@ class DefNodeWrapper(FuncDefNode): ...@@ -3710,9 +3713,14 @@ class DefNodeWrapper(FuncDefNode):
code.putln("if (%s(%s)) {" % ( code.putln("if (%s(%s)) {" % (
(self.num_required_kw_args > 0) and "likely" or "unlikely", (self.num_required_kw_args > 0) and "likely" or "unlikely",
Naming.kwds_cname)) Naming.kwds_cname))
self.generate_keyword_unpacking_code(
min_positional_args, max_positional_args, if do_generate_kw_unpacking:
has_fixed_positional_count, has_kw_only_args, all_args, argtuple_error_label, code) self.generate_keyword_unpacking_code(
min_positional_args, max_positional_args,
has_fixed_positional_count, has_kw_only_args, all_args, argtuple_error_label, code)
else:
code.putln("PyErr_Format(PyExc_TypeError, \"%s() takes no keyword arguments\");" % self.name)
code.putln(code.error_goto(self.pos))
# --- 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 and min_positional_args > 0) or min_positional_args == max_positional_args: if (self.num_required_kw_args and min_positional_args > 0) or min_positional_args == max_positional_args:
...@@ -3877,6 +3885,14 @@ class DefNodeWrapper(FuncDefNode): ...@@ -3877,6 +3885,14 @@ class DefNodeWrapper(FuncDefNode):
def generate_keyword_unpacking_code(self, min_positional_args, max_positional_args, def generate_keyword_unpacking_code(self, min_positional_args, max_positional_args,
has_fixed_positional_count, has_fixed_positional_count,
has_kw_only_args, all_args, argtuple_error_label, code): has_kw_only_args, all_args, argtuple_error_label, code):
# First we count how many arguments must be passed as positional
num_required_posonly_args = num_posonly_args = 0
for i, arg in enumerate(all_args):
if arg.pos_only:
num_posonly_args += 1
if not arg.default:
num_required_posonly_args += 1
code.putln('Py_ssize_t kw_args;') code.putln('Py_ssize_t kw_args;')
code.putln('const Py_ssize_t pos_args = PyTuple_GET_SIZE(%s);' % Naming.args_cname) code.putln('const Py_ssize_t pos_args = PyTuple_GET_SIZE(%s);' % Naming.args_cname)
# copy the values from the args tuple and check that it's not too long # copy the values from the args tuple and check that it's not too long
...@@ -3885,10 +3901,24 @@ class DefNodeWrapper(FuncDefNode): ...@@ -3885,10 +3901,24 @@ class DefNodeWrapper(FuncDefNode):
code.putln('default:') code.putln('default:')
for i in range(max_positional_args-1, -1, -1): for i in range(max_positional_args-1, -1, -1):
code.put('case %2d: ' % (i+1)) code.put('case %2d: ' % (i+1))
code.putln("values[%d] = PyTuple_GET_ITEM(%s, %d);" % ( if i+1 > num_required_posonly_args:
i, Naming.args_cname, i)) code.putln("values[%d] = PyTuple_GET_ITEM(%s, %d);" % (
code.putln('CYTHON_FALLTHROUGH;') i, Naming.args_cname, i))
code.putln('case 0: break;') code.putln('CYTHON_FALLTHROUGH;')
elif i+1 == num_required_posonly_args:
for j in range(num_required_posonly_args-1, -1, -1):
code.putln("values[%d] = PyTuple_GET_ITEM(%s, %d);" % (
j, Naming.args_cname, j))
code.putln('break;')
else:
code.putln('CYTHON_FALLTHROUGH;')
if num_required_posonly_args == 0:
code.putln('case 0: break;')
else:
# catch-all for not enough pos-only args passed
code.putln('case 0:')
code.put_goto(argtuple_error_label)
if not self.star_arg: if not self.star_arg:
code.put('default: ') # more arguments than allowed code.put('default: ') # more arguments than allowed
code.put_goto(argtuple_error_label) code.put_goto(argtuple_error_label)
...@@ -3906,31 +3936,24 @@ class DefNodeWrapper(FuncDefNode): ...@@ -3906,31 +3936,24 @@ class DefNodeWrapper(FuncDefNode):
code.putln('kw_args = PyDict_Size(%s);' % Naming.kwds_cname) code.putln('kw_args = PyDict_Size(%s);' % Naming.kwds_cname)
if self.num_required_args or max_positional_args > 0: if self.num_required_args or max_positional_args > 0:
last_required_arg = -1 last_required_arg = -1
last_required_posonly_arg = -1
for i, arg in enumerate(all_args): for i, arg in enumerate(all_args):
if not arg.default: if not arg.default:
last_required_arg = i last_required_arg = i
if arg.pos_only and not arg.default:
last_required_posonly_arg = i
if last_required_arg < max_positional_args: if last_required_arg < max_positional_args:
last_required_arg = max_positional_args-1 last_required_arg = max_positional_args-1
if max_positional_args > 0: if max_positional_args > num_posonly_args:
code.putln('switch (pos_args) {') code.putln('switch (pos_args) {')
for i, arg in enumerate(all_args[:last_required_arg+1]): for i, arg in enumerate(all_args[:last_required_arg+1]):
if max_positional_args > 0 and i <= max_positional_args: if i < num_posonly_args:
if i != 0: continue
if max_positional_args > num_posonly_args and i <= max_positional_args:
if i != num_posonly_args:
code.putln('CYTHON_FALLTHROUGH;') code.putln('CYTHON_FALLTHROUGH;')
if self.star_arg and i == max_positional_args: if self.star_arg and i == max_positional_args:
code.putln('default:') code.putln('default:')
else: else:
code.putln('case %2d:' % i) code.putln('case %2d:' % i)
pystring_cname = code.intern_identifier(arg.name) pystring_cname = code.intern_identifier(arg.name)
if arg.pos_only:
if i == last_required_posonly_arg:
code.put_goto(argtuple_error_label)
elif i == last_required_arg:
code.putln('break;')
continue
if arg.default: if arg.default:
if arg.kw_only: if arg.kw_only:
# optional kw-only args are handled separately below # optional kw-only args are handled separately below
...@@ -3969,7 +3992,7 @@ class DefNodeWrapper(FuncDefNode): ...@@ -3969,7 +3992,7 @@ class DefNodeWrapper(FuncDefNode):
self.name, pystring_cname)) self.name, pystring_cname))
code.putln(code.error_goto(self.pos)) code.putln(code.error_goto(self.pos))
code.putln('}') code.putln('}')
if max_positional_args > 0: if max_positional_args > num_posonly_args:
code.putln('}') code.putln('}')
if has_kw_only_args: if has_kw_only_args:
......
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