Commit f0081bd3 authored by Stefan Behnel's avatar Stefan Behnel Committed by GitHub

Merge pull request #2927 from rjtobin/pos_args

Adds positional only args support (PEP 570)
parents 1ae69ef8 185a8f39
...@@ -859,8 +859,9 @@ class CArgDeclNode(Node): ...@@ -859,8 +859,9 @@ class CArgDeclNode(Node):
# annotation ExprNode or None Py3 function arg annotation # annotation ExprNode or None Py3 function arg annotation
# is_self_arg boolean Is the "self" arg of an extension type method # is_self_arg boolean Is the "self" arg of an extension type method
# is_type_arg boolean Is the "class" arg of an extension type classmethod # is_type_arg boolean Is the "class" arg of an extension type classmethod
# is_kw_only boolean Is a keyword-only argument # kw_only boolean Is a keyword-only argument
# is_dynamic boolean Non-literal arg stored inside CyFunction # is_dynamic boolean Non-literal arg stored inside CyFunction
# pos_only boolean Is a positional-only argument
child_attrs = ["base_type", "declarator", "default", "annotation"] child_attrs = ["base_type", "declarator", "default", "annotation"]
outer_attrs = ["default", "annotation"] outer_attrs = ["default", "annotation"]
...@@ -869,6 +870,7 @@ class CArgDeclNode(Node): ...@@ -869,6 +870,7 @@ class CArgDeclNode(Node):
is_type_arg = 0 is_type_arg = 0
is_generic = 1 is_generic = 1
kw_only = 0 kw_only = 0
pos_only = 0
not_none = 0 not_none = 0
or_none = 0 or_none = 0
type = None type = None
...@@ -3653,6 +3655,7 @@ class DefNodeWrapper(FuncDefNode): ...@@ -3653,6 +3655,7 @@ class DefNodeWrapper(FuncDefNode):
positional_args = [] positional_args = []
required_kw_only_args = [] required_kw_only_args = []
optional_kw_only_args = [] optional_kw_only_args = []
num_pos_only_args = 0
for arg in args: for arg in args:
if arg.is_generic: if arg.is_generic:
if arg.default: if arg.default:
...@@ -3666,6 +3669,9 @@ class DefNodeWrapper(FuncDefNode): ...@@ -3666,6 +3669,9 @@ class DefNodeWrapper(FuncDefNode):
elif not arg.is_self_arg and not arg.is_type_arg: elif not arg.is_self_arg and not arg.is_type_arg:
positional_args.append(arg) positional_args.append(arg)
if arg.pos_only:
num_pos_only_args += 1
# sort required kw-only args before optional ones to avoid special # sort required kw-only args before optional ones to avoid special
# cases in the unpacking code # cases in the unpacking code
kw_only_args = required_kw_only_args + optional_kw_only_args kw_only_args = required_kw_only_args + optional_kw_only_args
...@@ -3683,10 +3689,10 @@ class DefNodeWrapper(FuncDefNode): ...@@ -3683,10 +3689,10 @@ 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,0};" % ( code.putln("static PyObject **%s[] = {%s};" % (
Naming.pykwdlist_cname, Naming.pykwdlist_cname,
','.join(['&%s' % code.intern_identifier(arg.name) ','.join(['&%s' % code.intern_identifier(arg.name)
for arg in all_args]))) for arg in all_args if not arg.pos_only] + ['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
...@@ -3704,8 +3710,8 @@ class DefNodeWrapper(FuncDefNode): ...@@ -3704,8 +3710,8 @@ class DefNodeWrapper(FuncDefNode):
Naming.kwds_cname)) Naming.kwds_cname))
self.generate_keyword_unpacking_code( self.generate_keyword_unpacking_code(
min_positional_args, max_positional_args, min_positional_args, max_positional_args,
has_fixed_positional_count, has_kw_only_args, num_pos_only_args, has_fixed_positional_count,
all_args, argtuple_error_label, code) has_kw_only_args, all_args, argtuple_error_label, code)
# --- 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:
...@@ -3868,8 +3874,8 @@ class DefNodeWrapper(FuncDefNode): ...@@ -3868,8 +3874,8 @@ class DefNodeWrapper(FuncDefNode):
code.putln('values[%d] = %s;' % (i, arg.type.as_pyobject(default_value))) code.putln('values[%d] = %s;' % (i, arg.type.as_pyobject(default_value)))
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_kw_only_args, num_pos_only_args, has_fixed_positional_count,
all_args, argtuple_error_label, code): has_kw_only_args, all_args, argtuple_error_label, code):
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
...@@ -3899,9 +3905,12 @@ class DefNodeWrapper(FuncDefNode): ...@@ -3899,9 +3905,12 @@ 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 > 0:
...@@ -3915,6 +3924,12 @@ class DefNodeWrapper(FuncDefNode): ...@@ -3915,6 +3924,12 @@ class DefNodeWrapper(FuncDefNode):
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,20 +3984,45 @@ class DefNodeWrapper(FuncDefNode): ...@@ -3969,20 +3984,45 @@ class DefNodeWrapper(FuncDefNode):
# arguments, this will always do the right thing for unpacking # arguments, this will always do the right thing for unpacking
# keyword arguments, so that we can concentrate on optimising # keyword arguments, so that we can concentrate on optimising
# common cases above. # common cases above.
#
# ParseOptionalKeywords() needs to know how many of the arguments
# that could be passed as keywords have in fact been passed as
# positional args.
if num_pos_only_args > 0:
# There are positional-only arguments which we don't want to count,
# since they cannot be keyword arguments. Subtract the number of
# pos-only arguments from the number of positional arguments we got.
# If we get a negative number then none of the keyword arguments were
# passed as positional args.
code.putln('const Py_ssize_t kwd_pos_args = (pos_args < %d) ? 0 : (pos_args - %d);' % (
num_pos_only_args, num_pos_only_args))
elif max_positional_args > 0:
code.putln('const Py_ssize_t kwd_pos_args = pos_args;')
if max_positional_args == 0: if max_positional_args == 0:
pos_arg_count = "0" pos_arg_count = "0"
elif self.star_arg: elif self.star_arg:
code.putln("const Py_ssize_t used_pos_args = (pos_args < %d) ? pos_args : %d;" % ( # If there is a *arg, the number of used positional args could be larger than
max_positional_args, max_positional_args)) # the number of possible keyword arguments. But ParseOptionalKeywords() uses the
# number of positional args as an index into the keyword argument name array,
# if this is larger than the number of kwd args we get a segfault. So round
# this down to max_positional_args - num_pos_only_args (= num possible kwd args).
code.putln("const Py_ssize_t used_pos_args = (kwd_pos_args < %d) ? kwd_pos_args : %d;" % (
max_positional_args - num_pos_only_args, max_positional_args - num_pos_only_args))
pos_arg_count = "used_pos_args" pos_arg_count = "used_pos_args"
else: else:
pos_arg_count = "pos_args" pos_arg_count = "kwd_pos_args"
if num_pos_only_args > 0 and num_pos_only_args < len(all_args):
values_array = 'values + %d' % (num_pos_only_args)
else:
values_array = 'values'
code.globalstate.use_utility_code( code.globalstate.use_utility_code(
UtilityCode.load_cached("ParseKeywords", "FunctionArguments.c")) UtilityCode.load_cached("ParseKeywords", "FunctionArguments.c"))
code.putln('if (unlikely(__Pyx_ParseOptionalKeywords(%s, %s, %s, values, %s, "%s") < 0)) %s' % ( code.putln('if (unlikely(__Pyx_ParseOptionalKeywords(%s, %s, %s, %s, %s, "%s") < 0)) %s' % (
Naming.kwds_cname, Naming.kwds_cname,
Naming.pykwdlist_cname, Naming.pykwdlist_cname,
self.starstar_arg and self.starstar_arg.entry.cname or '0', self.starstar_arg and self.starstar_arg.entry.cname or '0',
values_array,
pos_arg_count, pos_arg_count,
self.name, self.name,
code.error_goto(self.pos))) code.error_goto(self.pos)))
...@@ -3991,12 +4031,19 @@ class DefNodeWrapper(FuncDefNode): ...@@ -3991,12 +4031,19 @@ class DefNodeWrapper(FuncDefNode):
def generate_optional_kwonly_args_unpacking_code(self, all_args, code): def generate_optional_kwonly_args_unpacking_code(self, all_args, code):
optional_args = [] optional_args = []
first_optional_arg = -1 first_optional_arg = -1
num_posonly_args = 0
for i, arg in enumerate(all_args): for i, arg in enumerate(all_args):
if arg.pos_only:
num_posonly_args += 1
if not arg.kw_only or not arg.default: if not arg.kw_only or not arg.default:
continue continue
if not optional_args: if not optional_args:
first_optional_arg = i first_optional_arg = i
optional_args.append(arg.name) optional_args.append(arg.name)
if num_posonly_args > 0:
posonly_correction = '-%d' % num_posonly_args
else:
posonly_correction = ''
if optional_args: if optional_args:
if len(optional_args) > 1: if len(optional_args) > 1:
# if we receive more than the named kwargs, we either have **kwargs # if we receive more than the named kwargs, we either have **kwargs
...@@ -4012,8 +4059,8 @@ class DefNodeWrapper(FuncDefNode): ...@@ -4012,8 +4059,8 @@ class DefNodeWrapper(FuncDefNode):
else: else:
code.putln('if (kw_args == 1) {') code.putln('if (kw_args == 1) {')
code.putln('const Py_ssize_t index = %d;' % first_optional_arg) code.putln('const Py_ssize_t index = %d;' % first_optional_arg)
code.putln('PyObject* value = __Pyx_PyDict_GetItemStr(%s, *%s[index]);' % ( code.putln('PyObject* value = __Pyx_PyDict_GetItemStr(%s, *%s[index%s]);' % (
Naming.kwds_cname, Naming.pykwdlist_cname)) Naming.kwds_cname, Naming.pykwdlist_cname, posonly_correction))
code.putln('if (value) { values[index] = value; kw_args--; }') code.putln('if (value) { values[index] = value; kw_args--; }')
if len(optional_args) > 1: if len(optional_args) > 1:
code.putln('}') code.putln('}')
......
...@@ -2965,7 +2965,7 @@ def p_exception_value_clause(s): ...@@ -2965,7 +2965,7 @@ def p_exception_value_clause(s):
exc_val = p_test(s) exc_val = p_test(s)
return exc_val, exc_check return exc_val, exc_check
c_arg_list_terminators = cython.declare(set, set(['*', '**', '.', ')', ':'])) c_arg_list_terminators = cython.declare(set, set(['*', '**', '.', ')', ':', '/']))
def p_c_arg_list(s, ctx = Ctx(), in_pyfunc = 0, cmethod_flag = 0, def p_c_arg_list(s, ctx = Ctx(), in_pyfunc = 0, cmethod_flag = 0,
nonempty_declarators = 0, kw_only = 0, annotated = 1): nonempty_declarators = 0, kw_only = 0, annotated = 1):
...@@ -3424,6 +3424,20 @@ def p_varargslist(s, terminator=')', annotated=1): ...@@ -3424,6 +3424,20 @@ def p_varargslist(s, terminator=')', annotated=1):
annotated = annotated) annotated = annotated)
star_arg = None star_arg = None
starstar_arg = None starstar_arg = None
if s.sy == '/':
if len(args) == 0:
s.error("Got zero positional-only arguments despite presence of "
"positional-only specifier '/'")
s.next()
# Mark all args to the left as pos only
for arg in args:
arg.pos_only = 1
if s.sy == ',':
s.next()
args.extend(p_c_arg_list(s, in_pyfunc = 1,
nonempty_declarators = 1, annotated = annotated))
elif s.sy != terminator:
s.error("Syntax error in Python function argument list")
if s.sy == '*': if s.sy == '*':
s.next() s.next()
if s.sy == 'IDENT': if s.sy == 'IDENT':
......
# mode: error
# tag: posonly
def f(a, b = 5, /, c):
pass
def f(a = 5, b, /, c):
pass
def f(a = 5, b, /):
pass
def f(a, /, a):
pass
def f(a, /, *, a):
pass
#def f(a, b/2, c): #D
# pass
#def f(*args, /): #D
# pass
#def f(*args, a, /):
# pass
#def f(**kwargs, /):
# pass
#def f(/, a = 1): # D
# pass
#def f(/, a):
# pass
#def f(/):
# pass
#def f(*, a, /):
# pass
#def f(*, /, a):
# pass
#def f(a, /, c, /):
# pass
#def f(a, /, c, /, d):
# pass
#def f(a, /, c, /, d, *, e):
# pass
#def f(a, *, c, /, d, e):
# pass
def test_invalid_syntax_lambda(self):
lambda a, b = 5, /, c: None
lambda a = 5, b, /, c: None
lambda a = 5, b, /: None
lambda a, /, a: None
lambda a, /, *, a: None
# lambda *args, /: None
# lambda *args, a, /: None
# lambda **kwargs, /: None
# lambda /, a = 1: None
# lambda /, a: None
# lambda /: None
# lambda *, a, /: None
# lambda *, /, a: None
async def f(a, b = 5, /, c):
pass
#def test_multiple_seps(a,/,b,/):
# pass
_ERRORS = u"""
4:19: Non-default argument following default argument
7:13: Non-default argument following default argument
7:19: Non-default argument following default argument
10:13: Non-default argument following default argument
13:6: Previous declaration is here
13:12: 'a' redeclared
16:6: Previous declaration is here
16:15: 'a' redeclared
59:24: Non-default argument following default argument
60:18: Non-default argument following default argument
60:24: Non-default argument following default argument
61:18: Non-default argument following default argument
62:11: Previous declaration is here
62:17: 'a' redeclared
63:11: Previous declaration is here
63:20: 'a' redeclared
73:25: Non-default argument following default argument
"""
# mode: error
# tag: posonly
def f(a, b/2, c):
pass
_ERRORS = u"""
4:11: Syntax error in Python function argument list
"""
# mode: error
# tag: posonly
def f(*args, /):
pass
def f(*args, a, /):
pass
_ERRORS = u"""
4:13: Expected ')', found '/'
"""
# mode: error
# tag: posonly
def f(/, a = 1):
pass
_ERRORS = u"""
4:6: Got zero positional-only arguments despite presence of positional-only specifier '/'
"""
# mode: error
# tag: posonly
def test_multiple_seps(a,/,b,/):
pass
_ERRORS = u"""
4:29: Expected ')', found '/'
"""
# mode: run
# tag: posonly
# TODO: remove posonly tag before merge
import cython
import sys
import pickle
def test_optional_posonly_args1(a, b=10, /, c=100):
"""
>>> test_optional_posonly_args1(1, 2, 3)
6
>>> test_optional_posonly_args1(1, 2, c=3)
6
>>> test_optional_posonly_args1(1, b=2, c=3)
Traceback (most recent call last):
TypeError: test_optional_posonly_args1() got an unexpected keyword argument 'b'
>>> test_optional_posonly_args1(1, 2)
103
>>> test_optional_posonly_args1(1, b=2)
Traceback (most recent call last):
TypeError: test_optional_posonly_args1() got an unexpected keyword argument 'b'
"""
return a + b + c
def test_optional_posonly_args2(a=1, b=10, /, c=100):
"""
>>> test_optional_posonly_args2(1, 2, 3)
6
>>> test_optional_posonly_args2(1, 2, c=3)
6
>>> test_optional_posonly_args2(1, b=2, c=3)
Traceback (most recent call last):
TypeError: test_optional_posonly_args2() got an unexpected keyword argument 'b'
>>> test_optional_posonly_args2(1, 2)
103
>>> test_optional_posonly_args2(1, b=2)
Traceback (most recent call last):
TypeError: test_optional_posonly_args2() got an unexpected keyword argument 'b'
>>> test_optional_posonly_args2(1, c=2)
13
"""
return a + b + c
# TODO: this causes a line that is too long for old versions of Clang
#def many_args(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,a21,
# a22,a23,a24,a25,a26,a27,a28,a29,a30,a31,a32,a33,a34,a35,a36,a37,a38,a39,a40,
# a41,a42,a43,a44,a45,a46,a47,a48,a49,a50,a51,a52,a53,a54,a55,a56,a57,a58,a59,
# a60,a61,a62,a63,a64,a65,a66,a67,a68,a69,a70,a71,a72,a73,a74,a75,a76,a77,a78,
# a79,a80,a81,a82,a83,a84,a85,a86,a87,a88,a89,a90,a91,a92,a93,a94,a95,a96,a97,
# a98,a99,a100,a101,a102,a103,a104,a105,a106,a107,a108,a109,a110,a111,a112,
# a113,a114,a115,a116,a117,a118,a119,a120,a121,a122,a123,a124,a125,a126,a127,
# a128,a129,a130,a131,a132,a133,a134,a135,a136,a137,a138,a139,a140,a141,a142,
# a143,a144,a145,a146,a147,a148,a149,a150,a151,a152,a153,a154,a155,a156,a157,
# a158,a159,a160,a161,a162,a163,a164,a165,a166,a167,a168,a169,a170,a171,a172,
# a173,a174,a175,a176,a177,a178,a179,a180,a181,a182,a183,a184,a185,a186,a187,
# a188,a189,a190,a191,a192,a193,a194,a195,a196,a197,a198,a199,a200,a201,a202,
# a203,a204,a205,a206,a207,a208,a209,a210,a211,a212,a213,a214,a215,a216,a217,
# a218,a219,a220,a221,a222,a223,a224,a225,a226,a227,a228,a229,a230,a231,a232,
# a233,a234,a235,a236,a237,a238,a239,a240,a241,a242,a243,a244,a245,a246,a247,
# a248,a249,a250,a251,a252,a253,a254,a255,a256,a257,a258,a259,a260,a261,a262,
# a263,a264,a265,a266,a267,a268,a269,a270,a271,a272,a273,a274,a275,a276,a277,
# a278,a279,a280,a281,a282,a283,a284,a285,a286,a287,a288,a289,a290,a291,a292,
# a293,a294,a295,a296,a297,a298,a299,/,b,c=42,*,d):
# """
# >>> many_args(*range(299),b=1,c=2,d=3)
# (298, 1, 2, 3)
# >>> many_args(*range(299),b=1,d=3)
# (298, 1, 42, 3)
# >>> many_args(*range(300),d=3)
# (298, 299, 42, 3)
# """
# return (a299, b, c, d)
#TODO: update this test for Python 3.8
@cython.binding(True)
def func_introspection1(a, b, c, /, d, e=1, *, f, g=2):
"""
>>> if sys.version_info[0] < 3:
... assert func_introspection2.__code__.co_argcount == 7
... else:
... assert func_introspection2.__code__.co_argcount == 5
>>> func_introspection1.__defaults__
(1,)
"""
@cython.binding(True)
def func_introspection2(a, b, c=1, /, d=2, e=3, *, f, g=4):
"""
>>> if sys.version_info[0] < 3:
... assert func_introspection2.__code__.co_argcount == 7
... else:
... assert func_introspection2.__code__.co_argcount == 5
>>> func_introspection2.__defaults__
(1, 2, 3)
"""
def test_pos_only_call_via_unpacking(a, b, /):
"""
>>> test_pos_only_call_via_unpacking(*[1,2])
3
"""
return a + b
def test_use_positional_as_keyword1(a, /):
"""
>>> test_use_positional_as_keyword1(1)
>>> test_use_positional_as_keyword1(a=1)
Traceback (most recent call last):
TypeError: test_use_positional_as_keyword1() takes no keyword arguments
"""
def test_use_positional_as_keyword2(a, /, b):
"""
>>> test_use_positional_as_keyword2(1, 2)
>>> test_use_positional_as_keyword2(1, b=2)
>>> test_use_positional_as_keyword2(a=1, b=2)
Traceback (most recent call last):
TypeError: test_use_positional_as_keyword2() takes exactly 2 positional arguments (0 given)
"""
def test_use_positional_as_keyword3(a, b, /):
"""
>>> test_use_positional_as_keyword3(1, 2)
>>> test_use_positional_as_keyword3(a=1, b=2)
Traceback (most recent call last):
TypeError: test_use_positional_as_keyword3() takes exactly 2 positional arguments (0 given)
"""
def test_positional_only_and_arg_invalid_calls(a, b, /, c):
"""
>>> test_positional_only_and_arg_invalid_calls(1, 2, 3)
>>> test_positional_only_and_arg_invalid_calls(1, 2, c=3)
>>> test_positional_only_and_arg_invalid_calls(1, 2)
Traceback (most recent call last):
TypeError: test_positional_only_and_arg_invalid_calls() takes exactly 3 positional arguments (2 given)
>>> test_positional_only_and_arg_invalid_calls(1)
Traceback (most recent call last):
TypeError: test_positional_only_and_arg_invalid_calls() takes exactly 3 positional arguments (1 given)
>>> test_positional_only_and_arg_invalid_calls(1,2,3,4)
Traceback (most recent call last):
TypeError: test_positional_only_and_arg_invalid_calls() takes exactly 3 positional arguments (4 given)
"""
def test_positional_only_and_optional_arg_invalid_calls(a, b, /, c=3):
"""
>>> test_positional_only_and_optional_arg_invalid_calls(1, 2)
>>> test_positional_only_and_optional_arg_invalid_calls(1)
Traceback (most recent call last):
TypeError: test_positional_only_and_optional_arg_invalid_calls() takes at least 2 positional arguments (1 given)
>>> test_positional_only_and_optional_arg_invalid_calls()
Traceback (most recent call last):
TypeError: test_positional_only_and_optional_arg_invalid_calls() takes at least 2 positional arguments (0 given)
>>> test_positional_only_and_optional_arg_invalid_calls(1, 2, 3, 4)
Traceback (most recent call last):
TypeError: test_positional_only_and_optional_arg_invalid_calls() takes at most 3 positional arguments (4 given)
"""
def test_positional_only_and_kwonlyargs_invalid_calls(a, b, /, c, *, d, e):
"""
>>> test_positional_only_and_kwonlyargs_invalid_calls(1, 2, 3, d=1, e=2)
>>> test_positional_only_and_kwonlyargs_invalid_calls(1, 2, 3, e=2)
Traceback (most recent call last):
TypeError: test_positional_only_and_kwonlyargs_invalid_calls() needs keyword-only argument d
>>> test_positional_only_and_kwonlyargs_invalid_calls(1, 2, 3)
Traceback (most recent call last):
TypeError: test_positional_only_and_kwonlyargs_invalid_calls() needs keyword-only argument d
>>> test_positional_only_and_kwonlyargs_invalid_calls(1, 2)
Traceback (most recent call last):
TypeError: test_positional_only_and_kwonlyargs_invalid_calls() takes exactly 3 positional arguments (2 given)
>>> test_positional_only_and_kwonlyargs_invalid_calls(1)
Traceback (most recent call last):
TypeError: test_positional_only_and_kwonlyargs_invalid_calls() takes exactly 3 positional arguments (1 given)
>>> test_positional_only_and_kwonlyargs_invalid_calls()
Traceback (most recent call last):
TypeError: test_positional_only_and_kwonlyargs_invalid_calls() takes exactly 3 positional arguments (0 given)
>>> test_positional_only_and_kwonlyargs_invalid_calls(1, 2, 3, 4, 5, 6, d=7, e=8)
Traceback (most recent call last):
TypeError: test_positional_only_and_kwonlyargs_invalid_calls() takes exactly 3 positional arguments (6 given)
>>> test_positional_only_and_kwonlyargs_invalid_calls(1, 2, 3, d=1, e=4, f=56)
Traceback (most recent call last):
TypeError: test_positional_only_and_kwonlyargs_invalid_calls() got an unexpected keyword argument 'f'
"""
def test_positional_only_invalid_calls(a, b, /):
"""
>>> test_positional_only_invalid_calls(1, 2)
>>> test_positional_only_invalid_calls(1)
Traceback (most recent call last):
TypeError: test_positional_only_invalid_calls() takes exactly 2 positional arguments (1 given)
>>> test_positional_only_invalid_calls()
Traceback (most recent call last):
TypeError: test_positional_only_invalid_calls() takes exactly 2 positional arguments (0 given)
>>> test_positional_only_invalid_calls(1, 2, 3)
Traceback (most recent call last):
TypeError: test_positional_only_invalid_calls() takes exactly 2 positional arguments (3 given)
"""
def test_positional_only_with_optional_invalid_calls(a, b=2, /):
"""
>>> test_positional_only_with_optional_invalid_calls(1)
>>> test_positional_only_with_optional_invalid_calls()
Traceback (most recent call last):
TypeError: test_positional_only_with_optional_invalid_calls() takes at least 1 positional argument (0 given)
>>> test_positional_only_with_optional_invalid_calls(1, 2, 3)
Traceback (most recent call last):
TypeError: test_positional_only_with_optional_invalid_calls() takes at most 2 positional arguments (3 given)
"""
def test_no_standard_args_usage(a, b, /, *, c):
"""
>>> test_no_standard_args_usage(1, 2, c=3)
>>> test_no_standard_args_usage(1, b=2, c=3)
Traceback (most recent call last):
TypeError: test_no_standard_args_usage() takes exactly 2 positional arguments (1 given)
"""
#def test_change_default_pos_only():
# TODO: probably remove this, since __defaults__ is not writable in Cython?
# """
# >>> test_change_default_pos_only()
# True
# True
# """
# def f(a, b=2, /, c=3):
# return a + b + c
#
# print((2,3) == f.__defaults__)
# f.__defaults__ = (1, 2, 3)
# print(f(1, 2, 3) == 6)
def test_lambdas():
"""
>>> test_lambdas()
3
3
3
3
3
"""
x = lambda a, /, b: a + b
print(x(1,2))
print(x(1,b=2))
x = lambda a, /, b=2: a + b
print(x(1))
x = lambda a, b, /: a + b
print(x(1, 2))
x = lambda a, b, /, : a + b
print(x(1, 2))
class TestPosonlyMethods(object):
"""
>>> TestPosonlyMethods().f(1,2)
(1, 2)
>>> TestPosonlyMethods.f(TestPosonlyMethods(), 1, 2)
(1, 2)
>>> try:
... TestPosonlyMethods.f(1,2)
... except TypeError:
... print("Got type error")
Got type error
>>> TestPosonlyMethods().f(1, b=2)
Traceback (most recent call last):
TypeError: f() takes exactly 3 positional arguments (2 given)
"""
def f(self, a, b, /):
return a, b
class TestMangling(object):
"""
>>> TestMangling().f()
42
>>> TestMangling().f2()
42
>>> TestMangling().f3()
(42, 43)
>>> TestMangling().f4()
(42, 43, 44)
>>> TestMangling().f2(1)
1
>>> TestMangling().f3(1, __b=2)
(1, 2)
>>> TestMangling().f4(1, __b=2, __c=3)
(1, 2, 3)
"""
def f(self, *, __a=42):
return __a
def f2(self, __a=42, /):
return __a
def f3(self, __a=42, /, __b=43):
return (__a, __b)
def f4(self, __a=42, /, __b=43, *, __c=44):
return (__a, __b, __c)
def test_module_function(a, b, /):
"""
>>> test_module_function(1, 2)
>>> test_module_function()
Traceback (most recent call last):
TypeError: test_module_function() takes exactly 2 positional arguments (0 given)
"""
def test_closures1(x,y):
"""
>>> test_closures1(1,2)(3,4)
10
>>> test_closures1(1,2)(3)
Traceback (most recent call last):
TypeError: g() takes exactly 2 positional arguments (1 given)
>>> test_closures1(1,2)(3,4,5)
Traceback (most recent call last):
TypeError: g() takes exactly 2 positional arguments (3 given)
"""
def g(x2,/,y2):
return x + y + x2 + y2
return g
def test_closures2(x,/,y):
"""
>>> test_closures2(1,2)(3,4)
10
"""
def g(x2,y2):
return x + y + x2 + y2
return g
def test_closures3(x,/,y):
"""
>>> test_closures3(1,2)(3,4)
10
>>> test_closures3(1,2)(3)
Traceback (most recent call last):
TypeError: g() takes exactly 2 positional arguments (1 given)
>>> test_closures3(1,2)(3,4,5)
Traceback (most recent call last):
TypeError: g() takes exactly 2 positional arguments (3 given)
"""
def g(x2,/,y2):
return x + y + x2 + y2
return g
def test_same_keyword_as_positional_with_kwargs(something, /, **kwargs):
"""
>>> test_same_keyword_as_positional_with_kwargs(42, something=42)
(42, {'something': 42})
>>> test_same_keyword_as_positional_with_kwargs(something=42)
Traceback (most recent call last):
TypeError: test_same_keyword_as_positional_with_kwargs() takes exactly 1 positional argument (0 given)
>>> test_same_keyword_as_positional_with_kwargs(42)
(42, {})
"""
return (something, kwargs)
def test_serialization1(a, b, /):
"""
>>> pickled_posonly = pickle.dumps(test_serialization1)
>>> unpickled_posonly = pickle.loads(pickled_posonly)
>>> unpickled_posonly(1, 2)
(1, 2)
>>> unpickled_posonly(a=1, b=2)
Traceback (most recent call last):
TypeError: test_serialization1() takes exactly 2 positional arguments (0 given)
"""
return (a, b)
def test_serialization2(a, /, b):
"""
>>> pickled_optional = pickle.dumps(test_serialization2)
>>> unpickled_optional = pickle.loads(pickled_optional)
>>> unpickled_optional(1, 2)
(1, 2)
>>> unpickled_optional(a=1, b=2)
Traceback (most recent call last):
TypeError: test_serialization2() takes exactly 2 positional arguments (0 given)
"""
return (a, b)
def test_serialization3(a=1, /, b=2):
"""
>>> pickled_defaults = pickle.dumps(test_serialization3)
>>> unpickled_defaults = pickle.loads(pickled_defaults)
>>> unpickled_defaults(1, 2)
(1, 2)
>>> unpickled_defaults(a=1, b=2)
Traceback (most recent call last):
TypeError: test_serialization3() got an unexpected keyword argument 'a'
"""
return (a, b)
async def test_async(a=1, /, b=2):
"""
>>> test_async(a=1, b=2)
Traceback (most recent call last):
TypeError: test_async() got an unexpected keyword argument 'a'
"""
return a, b
def test_async_call(*args, **kwargs):
"""
>>> test_async_call(1, 2)
>>> test_async_call(1, b=2)
>>> test_async_call(1)
>>> test_async_call()
"""
if sys.version_info < (3, 6):
return
try:
coro = test_async(*args, **kwargs)
coro.send(None)
except StopIteration as e:
result = e.value
assert result == (1, 2)
def test_generator(a=1, /, b=2):
"""
>>> test_generator(a=1, b=2)
Traceback (most recent call last):
TypeError: test_generator() got an unexpected keyword argument 'a'
>>> gen = test_generator(1, 2)
>>> next(gen)
(1, 2)
>>> gen = test_generator(1, b=2)
>>> next(gen)
(1, 2)
>>> gen = test_generator(1)
>>> next(gen)
(1, 2)
>>> gen = test_generator()
>>> next(gen)
(1, 2)
"""
yield a, b
def f_call_1_0_0(a,/):
"""
>>> f_call_1_0_0(1)
(1,)
"""
return (a,)
def f_call_1_1_0(a,/,b):
"""
>>> f_call_1_1_0(1,2)
(1, 2)
"""
return (a,b)
def f_call_1_1_1(a,/,b,*,c):
"""
>>> f_call_1_1_1(1,2,c=3)
(1, 2, 3)
"""
return (a,b,c)
def f_call_1_1_1_star(a,/,b,*args,c):
"""
>>> f_call_1_1_1_star(1,2,c=3)
(1, 2, (), 3)
>>> f_call_1_1_1_star(1,2,3,4,5,6,7,8,c=9)
(1, 2, (3, 4, 5, 6, 7, 8), 9)
"""
return (a,b,args,c)
def f_call_1_1_1_kwds(a,/,b,*,c,**kwds):
"""
>>> f_call_1_1_1_kwds(1,2,c=3)
(1, 2, 3, {})
>>> f_call_1_1_1_kwds(1,2,c=3,d=4,e=5) == (1, 2, 3, {'d': 4, 'e': 5})
True
"""
return (a,b,c,kwds)
def f_call_1_1_1_star_kwds(a,/,b,*args,c,**kwds):
"""
>>> f_call_1_1_1_star_kwds(1,2,c=3,d=4,e=5) == (1, 2, (), 3, {'d': 4, 'e': 5})
True
>>> f_call_1_1_1_star_kwds(1,2,3,4,c=5,d=6,e=7) == (1, 2, (3, 4), 5, {'d': 6, 'e': 7})
True
"""
return (a,b,args,c,kwds)
def f_call_one_optional_kwd(a,/,*,b=2):
"""
>>> f_call_one_optional_kwd(1)
(1, 2)
>>> f_call_one_optional_kwd(1, b=3)
(1, 3)
"""
return (a,b)
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