Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cython
Commits
b3cfe42f
Commit
b3cfe42f
authored
Apr 14, 2019
by
Josh Tobin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Adds positional only args support (PEP 570)
parent
d5da2dbc
Changes
4
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
400 additions
and
11 deletions
+400
-11
Cython/Compiler/Nodes.py
Cython/Compiler/Nodes.py
+45
-10
Cython/Compiler/Parsing.py
Cython/Compiler/Parsing.py
+15
-1
tests/compile/posonly.pyx
tests/compile/posonly.pyx
+17
-0
tests/run/posonly.pyx
tests/run/posonly.pyx
+323
-0
No files found.
Cython/Compiler/Nodes.py
View file @
b3cfe42f
...
...
@@ -861,8 +861,9 @@ class CArgDeclNode(Node):
# annotation ExprNode or None Py3 function arg annotation
# 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_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
# pos_only boolean Is a positional-only argument
child_attrs
=
[
"base_type"
,
"declarator"
,
"default"
,
"annotation"
]
outer_attrs
=
[
"default"
,
"annotation"
]
...
...
@@ -871,6 +872,7 @@ class CArgDeclNode(Node):
is_type_arg
=
0
is_generic
=
1
kw_only
=
0
pos_only
=
0
not_none
=
0
or_none
=
0
type
=
None
...
...
@@ -3655,6 +3657,7 @@ class DefNodeWrapper(FuncDefNode):
positional_args
=
[]
required_kw_only_args
=
[]
optional_kw_only_args
=
[]
num_pos_only_args
=
0
for
arg
in
args
:
if
arg
.
is_generic
:
if
arg
.
default
:
...
...
@@ -3668,6 +3671,9 @@ class DefNodeWrapper(FuncDefNode):
elif
not
arg
.
is_self_arg
and
not
arg
.
is_type_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
# cases in the unpacking code
kw_only_args
=
required_kw_only_args
+
optional_kw_only_args
...
...
@@ -3685,10 +3691,10 @@ class DefNodeWrapper(FuncDefNode):
code
.
putln
(
'{'
)
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
,
','
.
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,
# borrowed references to all unpacked argument values are
...
...
@@ -3706,8 +3712,8 @@ class DefNodeWrapper(FuncDefNode):
Naming
.
kwds_cname
))
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
)
num_pos_only_args
,
has_fixed_positional_count
,
has_kw_only_args
,
all_args
,
argtuple_error_label
,
code
)
# --- 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
:
...
...
@@ -3870,8 +3876,8 @@ class DefNodeWrapper(FuncDefNode):
code
.
putln
(
'values[%d] = %s;'
%
(
i
,
arg
.
type
.
as_pyobject
(
default_value
)))
def
generate_keyword_unpacking_code
(
self
,
min_positional_args
,
max_positional_args
,
has_fixed_positional_count
,
has_kw_only_args
,
all_args
,
argtuple_error_label
,
code
):
num_pos_only_args
,
has_fixed_positional_count
,
has_kw_only_args
,
all_args
,
argtuple_error_label
,
code
):
code
.
putln
(
'Py_ssize_t kw_args;'
)
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
...
...
@@ -3901,9 +3907,12 @@ class DefNodeWrapper(FuncDefNode):
code
.
putln
(
'kw_args = PyDict_Size(%s);'
%
Naming
.
kwds_cname
)
if
self
.
num_required_args
or
max_positional_args
>
0
:
last_required_arg
=
-
1
last_required_posonly_arg
=
-
1
for
i
,
arg
in
enumerate
(
all_args
):
if
not
arg
.
default
:
last_required_arg
=
i
if
arg
.
pos_only
and
not
arg
.
default
:
last_required_posonly_arg
=
i
if
last_required_arg
<
max_positional_args
:
last_required_arg
=
max_positional_args
-
1
if
max_positional_args
>
0
:
...
...
@@ -3917,6 +3926,12 @@ class DefNodeWrapper(FuncDefNode):
else
:
code
.
putln
(
'case %2d:'
%
i
)
pystring_cname
=
code
.
intern_identifier
(
arg
.
name
)
if
arg
.
pos_only
:
if
i
==
last_required_posonly_arg
:
code
.
put_goto
(
argtuple_error_label
)
if
i
==
last_required_arg
:
code
.
putln
(
'break;'
)
continue
if
arg
.
default
:
if
arg
.
kw_only
:
# optional kw-only args are handled separately below
...
...
@@ -3971,14 +3986,34 @@ class DefNodeWrapper(FuncDefNode):
# arguments, this will always do the right thing for unpacking
# keyword arguments, so that we can concentrate on optimising
# 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
:
pos_arg_count
=
"0"
elif
self
.
star_arg
:
code
.
putln
(
"const Py_ssize_t used_pos_args = (pos_args < %d) ? pos_args : %d;"
%
(
max_positional_args
,
max_positional_args
))
# If there is a *arg, the number of used positional args could be larger than
# 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"
else
:
pos_arg_count
=
"pos_args"
pos_arg_count
=
"
kwd_
pos_args"
code
.
globalstate
.
use_utility_code
(
UtilityCode
.
load_cached
(
"ParseKeywords"
,
"FunctionArguments.c"
))
code
.
putln
(
'if (unlikely(__Pyx_ParseOptionalKeywords(%s, %s, %s, values, %s, "%s") < 0)) %s'
%
(
...
...
Cython/Compiler/Parsing.py
View file @
b3cfe42f
...
...
@@ -2965,7 +2965,7 @@ def p_exception_value_clause(s):
exc_val
=
p_test
(
s
)
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
,
nonempty_declarators
=
0
,
kw_only
=
0
,
annotated
=
1
):
...
...
@@ -3424,6 +3424,20 @@ def p_varargslist(s, terminator=')', annotated=1):
annotated
=
annotated
)
star_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
==
'*'
:
s
.
next
()
if
s
.
sy
==
'IDENT'
:
...
...
tests/compile/posonly.pyx
0 → 100644
View file @
b3cfe42f
# mode: compile
# tag: posonly
# TODO: remove posonly tag before merge (and maybe remove this test,
# since it seems covered by the runs/ test)
def
test
(
x
,
y
,
z
=
42
,
/
,
w
=
43
):
pass
def
test2
(
x
,
y
,
/
):
pass
def
test3
(
x
,
/
,
z
):
pass
def
test4
(
x
,
/
,
z
,
*
,
w
):
pass
tests/run/posonly.pyx
0 → 100644
View file @
b3cfe42f
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment