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
Gwenaël Samain
cython
Commits
c036607a
Commit
c036607a
authored
Apr 12, 2014
by
Stefan Behnel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
implement support for extracting type declarations from signature annotations
parent
cbe735b8
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
101 additions
and
31 deletions
+101
-31
CHANGES.rst
CHANGES.rst
+3
-0
Cython/Compiler/ExprNodes.py
Cython/Compiler/ExprNodes.py
+30
-17
Cython/Compiler/Nodes.py
Cython/Compiler/Nodes.py
+56
-14
Cython/Compiler/Options.py
Cython/Compiler/Options.py
+2
-0
docs/src/tutorial/pure.rst
docs/src/tutorial/pure.rst
+10
-0
No files found.
CHANGES.rst
View file @
c036607a
...
@@ -9,6 +9,9 @@ Latest
...
@@ -9,6 +9,9 @@ Latest
Features added
Features added
--------------
--------------
* Simple support for declaring Python object types in Python signature
annotations.
Bugs fixed
Bugs fixed
----------
----------
...
...
Cython/Compiler/ExprNodes.py
View file @
c036607a
...
@@ -289,15 +289,16 @@ class ExprNode(Node):
...
@@ -289,15 +289,16 @@ class ExprNode(Node):
#
#
#
#
is_sequence_constructor
=
0
is_sequence_constructor
=
False
is_string_literal
=
0
is_dict_literal
=
False
is_attribute
=
0
is_string_literal
=
False
is_subscript
=
0
is_attribute
=
False
is_subscript
=
False
saved_subexpr_nodes
=
None
saved_subexpr_nodes
=
None
is_temp
=
0
is_temp
=
False
is_target
=
0
is_target
=
False
is_starred
=
0
is_starred
=
False
constant_result
=
constant_value_not_set
constant_result
=
constant_value_not_set
...
@@ -1174,6 +1175,20 @@ class FloatNode(ConstNode):
...
@@ -1174,6 +1175,20 @@ class FloatNode(ConstNode):
self
.
result_code
=
c_value
self
.
result_code
=
c_value
def
_analyse_name_as_type
(
name
,
pos
,
env
):
type
=
PyrexTypes
.
parse_basic_type
(
name
)
if
type
is
not
None
:
return
type
from
TreeFragment
import
TreeFragment
pos
=
(
pos
[
0
],
pos
[
1
],
pos
[
2
]
-
7
)
declaration
=
TreeFragment
(
u"sizeof(%s)"
%
name
,
name
=
pos
[
0
].
filename
,
initial_pos
=
pos
)
sizeof_node
=
declaration
.
root
.
stats
[
0
].
expr
sizeof_node
=
sizeof_node
.
analyse_types
(
env
)
if
isinstance
(
sizeof_node
,
SizeofTypeNode
):
return
sizeof_node
.
arg_type
return
None
class
BytesNode
(
ConstNode
):
class
BytesNode
(
ConstNode
):
# A char* or bytes literal
# A char* or bytes literal
#
#
...
@@ -1196,16 +1211,7 @@ class BytesNode(ConstNode):
...
@@ -1196,16 +1211,7 @@ class BytesNode(ConstNode):
return
self
.
value
return
self
.
value
def
analyse_as_type
(
self
,
env
):
def
analyse_as_type
(
self
,
env
):
type
=
PyrexTypes
.
parse_basic_type
(
self
.
value
)
return
_analyse_name_as_type
(
self
.
value
.
decode
(
'ISO8859-1'
),
self
.
pos
,
env
)
if
type
is
not
None
:
return
type
from
TreeFragment
import
TreeFragment
pos
=
(
self
.
pos
[
0
],
self
.
pos
[
1
],
self
.
pos
[
2
]
-
7
)
declaration
=
TreeFragment
(
u"sizeof(%s)"
%
self
.
value
,
name
=
pos
[
0
].
filename
,
initial_pos
=
pos
)
sizeof_node
=
declaration
.
root
.
stats
[
0
].
expr
sizeof_node
=
sizeof_node
.
analyse_types
(
env
)
if
isinstance
(
sizeof_node
,
SizeofTypeNode
):
return
sizeof_node
.
arg_type
def
can_coerce_to_char_literal
(
self
):
def
can_coerce_to_char_literal
(
self
):
return
len
(
self
.
value
)
==
1
return
len
(
self
.
value
)
==
1
...
@@ -1279,6 +1285,9 @@ class UnicodeNode(ConstNode):
...
@@ -1279,6 +1285,9 @@ class UnicodeNode(ConstNode):
def
calculate_constant_result
(
self
):
def
calculate_constant_result
(
self
):
self
.
constant_result
=
self
.
value
self
.
constant_result
=
self
.
value
def
analyse_as_type
(
self
,
env
):
return
_analyse_name_as_type
(
self
.
value
,
self
.
pos
,
env
)
def
as_sliced_node
(
self
,
start
,
stop
,
step
=
None
):
def
as_sliced_node
(
self
,
start
,
stop
,
step
=
None
):
if
StringEncoding
.
string_contains_surrogates
(
self
.
value
[:
stop
]):
if
StringEncoding
.
string_contains_surrogates
(
self
.
value
[:
stop
]):
# this is unsafe as it may give different results
# this is unsafe as it may give different results
...
@@ -1388,6 +1397,9 @@ class StringNode(PyConstNode):
...
@@ -1388,6 +1397,9 @@ class StringNode(PyConstNode):
# only the Unicode value is portable across Py2/3
# only the Unicode value is portable across Py2/3
self
.
constant_result
=
self
.
unicode_value
self
.
constant_result
=
self
.
unicode_value
def
analyse_as_type
(
self
,
env
):
return
_analyse_name_as_type
(
self
.
unicode_value
or
self
.
value
.
decode
(
'ISO8859-1'
),
self
.
pos
,
env
)
def
as_sliced_node
(
self
,
start
,
stop
,
step
=
None
):
def
as_sliced_node
(
self
,
start
,
stop
,
step
=
None
):
value
=
type
(
self
.
value
)(
self
.
value
[
start
:
stop
:
step
])
value
=
type
(
self
.
value
)(
self
.
value
[
start
:
stop
:
step
])
value
.
encoding
=
self
.
value
.
encoding
value
.
encoding
=
self
.
value
.
encoding
...
@@ -6706,6 +6718,7 @@ class DictNode(ExprNode):
...
@@ -6706,6 +6718,7 @@ class DictNode(ExprNode):
is_temp
=
1
is_temp
=
1
exclude_null_values
=
False
exclude_null_values
=
False
type
=
dict_type
type
=
dict_type
is_dict_literal
=
True
obj_conversion_errors
=
[]
obj_conversion_errors
=
[]
...
...
Cython/Compiler/Nodes.py
View file @
c036607a
...
@@ -612,8 +612,8 @@ class CFuncDeclaratorNode(CDeclaratorNode):
...
@@ -612,8 +612,8 @@ class CFuncDeclaratorNode(CDeclaratorNode):
nonempty
-=
1
nonempty
-=
1
func_type_args
=
[]
func_type_args
=
[]
for
i
,
arg_node
in
enumerate
(
self
.
args
):
for
i
,
arg_node
in
enumerate
(
self
.
args
):
name_declarator
,
type
=
arg_node
.
analyse
(
env
,
nonempty
=
nonempty
,
name_declarator
,
type
=
arg_node
.
analyse
(
is_self_arg
=
(
i
==
0
and
env
.
is_c_class_scope
))
env
,
nonempty
=
nonempty
,
is_self_arg
=
(
i
==
0
and
env
.
is_c_class_scope
))
name
=
name_declarator
.
name
name
=
name_declarator
.
name
if
name
in
directive_locals
:
if
name
in
directive_locals
:
type_node
=
directive_locals
[
name
]
type_node
=
directive_locals
[
name
]
...
@@ -800,7 +800,7 @@ class CArgDeclNode(Node):
...
@@ -800,7 +800,7 @@ class CArgDeclNode(Node):
if
nonempty
:
if
nonempty
:
if
self
.
base_type
.
is_basic_c_type
:
if
self
.
base_type
.
is_basic_c_type
:
# char, short, long called "int"
# char, short, long called "int"
type
=
self
.
base_type
.
analyse
(
env
,
could_be_name
=
True
)
type
=
self
.
base_type
.
analyse
(
env
,
could_be_name
=
True
)
arg_name
=
type
.
declaration_code
(
""
)
arg_name
=
type
.
declaration_code
(
""
)
else
:
else
:
arg_name
=
self
.
base_type
.
name
arg_name
=
self
.
base_type
.
name
...
@@ -811,9 +811,10 @@ class CArgDeclNode(Node):
...
@@ -811,9 +811,10 @@ class CArgDeclNode(Node):
else
:
else
:
could_be_name
=
False
could_be_name
=
False
self
.
base_type
.
is_arg
=
True
self
.
base_type
.
is_arg
=
True
base_type
=
self
.
base_type
.
analyse
(
env
,
could_be_name
=
could_be_name
)
base_type
=
self
.
base_type
.
analyse
(
env
,
could_be_name
=
could_be_name
)
if
hasattr
(
self
.
base_type
,
'arg_name'
)
and
self
.
base_type
.
arg_name
:
if
hasattr
(
self
.
base_type
,
'arg_name'
)
and
self
.
base_type
.
arg_name
:
self
.
declarator
.
name
=
self
.
base_type
.
arg_name
self
.
declarator
.
name
=
self
.
base_type
.
arg_name
# The parser is unable to resolve the ambiguity of [] as part of the
# The parser is unable to resolve the ambiguity of [] as part of the
# type (e.g. in buffers) or empty declarator (as with arrays).
# type (e.g. in buffers) or empty declarator (as with arrays).
# This is only arises for empty multi-dimensional arrays.
# This is only arises for empty multi-dimensional arrays.
...
@@ -825,10 +826,45 @@ class CArgDeclNode(Node):
...
@@ -825,10 +826,45 @@ class CArgDeclNode(Node):
declarator
=
declarator
.
base
declarator
=
declarator
.
base
declarator
.
base
=
self
.
base_type
.
array_declarator
declarator
.
base
=
self
.
base_type
.
array_declarator
base_type
=
base_type
.
base_type
base_type
=
base_type
.
base_type
return
self
.
declarator
.
analyse
(
base_type
,
env
,
nonempty
=
nonempty
)
# inject type declaration from annotations
if
self
.
annotation
and
env
.
directives
[
'annotation_typing'
]
and
self
.
base_type
.
name
is
None
:
arg_type
=
self
.
inject_type_from_annotations
(
env
)
if
arg_type
is
not
None
:
base_type
=
arg_type
return
self
.
declarator
.
analyse
(
base_type
,
env
,
nonempty
=
nonempty
)
else
:
else
:
return
self
.
name_declarator
,
self
.
type
return
self
.
name_declarator
,
self
.
type
def
inject_type_from_annotations
(
self
,
env
):
annotation
=
self
.
annotation
if
not
annotation
:
return
explicit_pytype
=
explicit_ctype
=
False
if
annotation
.
is_dict_literal
:
for
name
,
value
in
annotation
.
key_value_pairs
:
if
not
name
.
is_string_literal
:
continue
if
name
.
value
==
'type'
:
explicit_pytype
=
True
if
not
explicit_ctype
:
annotation
=
value
elif
name
.
value
==
'ctype'
:
explicit_ctype
=
True
annotation
=
value
if
explicit_pytype
and
explicit_ctype
:
warning
(
annotation
.
pos
,
"Duplicate type declarations found in signature annotation"
)
arg_type
=
annotation
.
analyse_as_type
(
env
)
if
arg_type
is
not
None
:
if
explicit_pytype
and
not
explicit_ctype
and
not
arg_type
.
is_pyobject
:
warning
(
annotation
.
pos
,
"Python type declaration in signature annotation does not refer to a Python type"
)
self
.
base_type
=
CAnalysedBaseTypeNode
(
annotation
.
pos
,
type
=
arg_type
,
is_arg
=
True
)
else
:
warning
(
annotation
.
pos
,
"Unknown type declaration found in signature annotation"
)
return
arg_type
def
calculate_default_value_code
(
self
,
code
):
def
calculate_default_value_code
(
self
,
code
):
if
self
.
default_value
is
None
:
if
self
.
default_value
is
None
:
if
self
.
default
:
if
self
.
default
:
...
@@ -1524,19 +1560,25 @@ class FuncDefNode(StatNode, BlockNode):
...
@@ -1524,19 +1560,25 @@ class FuncDefNode(StatNode, BlockNode):
error
(
arg
.
pos
,
"Non-default argument following default argument"
)
error
(
arg
.
pos
,
"Non-default argument following default argument"
)
def
align_argument_type
(
self
,
env
,
arg
):
def
align_argument_type
(
self
,
env
,
arg
):
# @cython.locals()
directive_locals
=
self
.
directive_locals
directive_locals
=
self
.
directive_locals
type
=
arg
.
type
orig_
type
=
arg
.
type
if
arg
.
name
in
directive_locals
:
if
arg
.
name
in
directive_locals
:
type_node
=
directive_locals
[
arg
.
name
]
type_node
=
directive_locals
[
arg
.
name
]
other_type
=
type_node
.
analyse_as_type
(
env
)
other_type
=
type_node
.
analyse_as_type
(
env
)
if
other_type
is
None
:
elif
isinstance
(
arg
,
CArgDeclNode
)
and
arg
.
annotation
:
error
(
type_node
.
pos
,
"Not a type"
)
type_node
=
arg
.
annotation
elif
(
type
is
not
PyrexTypes
.
py_object_type
other_type
=
arg
.
inject_type_from_annotations
(
env
)
and
not
type
.
same_as
(
other_type
)):
else
:
error
(
arg
.
base_type
.
pos
,
"Signature does not agree with previous declaration"
)
return
arg
error
(
type_node
.
pos
,
"Previous declaration here"
)
if
other_type
is
None
:
else
:
error
(
type_node
.
pos
,
"Not a type"
)
arg
.
type
=
other_type
elif
(
orig_type
is
not
PyrexTypes
.
py_object_type
and
not
orig_type
.
same_as
(
other_type
)):
error
(
arg
.
base_type
.
pos
,
"Signature does not agree with previous declaration"
)
error
(
type_node
.
pos
,
"Previous declaration here"
)
else
:
arg
.
type
=
other_type
return
arg
return
arg
def
need_gil_acquisition
(
self
,
lenv
):
def
need_gil_acquisition
(
self
,
lenv
):
...
...
Cython/Compiler/Options.py
View file @
c036607a
...
@@ -101,6 +101,7 @@ directive_defaults = {
...
@@ -101,6 +101,7 @@ directive_defaults = {
'profile'
:
False
,
'profile'
:
False
,
'no_gc_clear'
:
False
,
'no_gc_clear'
:
False
,
'linetrace'
:
False
,
'linetrace'
:
False
,
'annotation_typing'
:
False
,
# read type declarations from Python function annotations
'infer_types'
:
None
,
'infer_types'
:
None
,
'infer_types.verbose'
:
False
,
'infer_types.verbose'
:
False
,
'autotestdict'
:
True
,
'autotestdict'
:
True
,
...
@@ -228,6 +229,7 @@ directive_scopes = { # defaults to available everywhere
...
@@ -228,6 +229,7 @@ directive_scopes = { # defaults to available everywhere
'test_assert_path_exists'
:
(
'function'
,
'class'
,
'cclass'
),
'test_assert_path_exists'
:
(
'function'
,
'class'
,
'cclass'
),
'test_fail_if_path_exists'
:
(
'function'
,
'class'
,
'cclass'
),
'test_fail_if_path_exists'
:
(
'function'
,
'class'
,
'cclass'
),
'freelist'
:
(
'cclass'
,),
'freelist'
:
(
'cclass'
,),
'annotation_typing'
:
(
'module'
,),
# FIXME: analysis currently lacks more specific function scope
# Avoid scope-specific to/from_py_functions for c_string.
# Avoid scope-specific to/from_py_functions for c_string.
'c_string_type'
:
(
'module'
,),
'c_string_type'
:
(
'module'
,),
'c_string_encoding'
:
(
'module'
,),
'c_string_encoding'
:
(
'module'
,),
...
...
docs/src/tutorial/pure.rst
View file @
c036607a
...
@@ -36,6 +36,16 @@ The currently supported attributes of the ``cython`` module are:
...
@@ -36,6 +36,16 @@ The currently supported attributes of the ``cython`` module are:
def foo(a, b, x, y):
def foo(a, b, x, y):
...
...
* Starting with Cython 0.21, Python signature annotations can be used to
declare argument types. Cython recognises three ways to do this, as
shown in the following example::
def func(plain_python_type: dict,
named_python_type: 'dict',
explicit_python_type: {'type': dict},
explicit_c_type: {'ctype': 'int'}):
...
* ``address`` is used in place of the ``&`` operator::
* ``address`` is used in place of the ``&`` operator::
cython.declare(x=cython.int, x_ptr=cython.p_int)
cython.declare(x=cython.int, x_ptr=cython.p_int)
...
...
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