summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormattip <matti.picus@gmail.com>2018-11-13 17:51:40 -0800
committermattip <matti.picus@gmail.com>2018-11-13 17:51:40 -0800
commita6ec7fb9a6350611394a8955096df7f81238af5d (patch)
tree2e4f616535a48ff18c3aeab07496b9278b9f1b4a
parentfdddf34e099e8e8ede7c67ccf7ac3f09b4844d7c (diff)
downloadcython-a6ec7fb9a6350611394a8955096df7f81238af5d.tar.gz
ENH: handle c-level property decorator to produce a cfunc call
-rw-r--r--Cython/Compiler/ExprNodes.py11
-rw-r--r--Cython/Compiler/Nodes.py7
-rw-r--r--Cython/Compiler/ParseTreeTransforms.py17
-rw-r--r--Cython/Compiler/Symtab.py25
4 files changed, 41 insertions, 19 deletions
diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py
index 2c7e025b8..612996503 100644
--- a/Cython/Compiler/ExprNodes.py
+++ b/Cython/Compiler/ExprNodes.py
@@ -6976,7 +6976,10 @@ class AttributeNode(ExprNode):
self.obj = self.obj.analyse_types(env)
self.analyse_attribute(env)
if self.entry and self.entry.is_cmethod and not self.is_called:
-# error(self.pos, "C method can only be called")
+ # It must be a property method. This should be done at a different level??
+ self.is_called = 1
+ self.op = ''
+ self.result_ctype = self.type.return_type
pass
## Reference to C array turns into pointer to first element.
#while self.type.is_array:
@@ -7146,6 +7149,8 @@ class AttributeNode(ExprNode):
obj_code = obj.result_as(obj.type)
#print "...obj_code =", obj_code ###
if self.entry and self.entry.is_cmethod:
+ if getattr(self.entry.type, 'is_cgetter', False):
+ return "%s(%s)" %(self.entry.func_cname, obj_code)
if obj.type.is_extension_type and not self.entry.is_builtin_cmethod:
if self.entry.final_func_cname:
return self.entry.final_func_cname
@@ -11223,6 +11228,10 @@ class NumBinopNode(BinopNode):
self.operand2 = self.operand2.coerce_to(self.type, env)
def compute_c_result_type(self, type1, type2):
+ if type1.is_cfunction and type1.is_cgetter:
+ type1 = type1.return_type
+ if type2.is_cfunction and type2.is_cgetter:
+ type2 = type2.return_type
if self.c_types_okay(type1, type2):
widest_type = PyrexTypes.widest_numeric_type(type1, type2)
if widest_type is PyrexTypes.c_bint_type:
diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py
index a1e390218..bbf0c9073 100644
--- a/Cython/Compiler/Nodes.py
+++ b/Cython/Compiler/Nodes.py
@@ -2422,11 +2422,16 @@ class CFuncDefNode(FuncDefNode):
type.is_const_method = self.is_const_method
type.is_static_method = self.is_static_method
+ property = False
+ if self.decorators:
+ for _node in self.decorators:
+ if _node.decorator.is_name and _node.decorator.name == 'property':
+ property = True
self.entry = env.declare_cfunction(
name, type, self.pos,
cname=cname, visibility=self.visibility, api=self.api,
defining=self.body is not None, modifiers=self.modifiers,
- overridable=self.overridable)
+ overridable=self.overridable, property=property)
self.entry.inline_func_in_pxd = self.inline_in_pxd
self.return_type = type.return_type
if self.return_type.is_array and self.visibility != 'extern':
diff --git a/Cython/Compiler/ParseTreeTransforms.py b/Cython/Compiler/ParseTreeTransforms.py
index aa5486631..85deda5d1 100644
--- a/Cython/Compiler/ParseTreeTransforms.py
+++ b/Cython/Compiler/ParseTreeTransforms.py
@@ -2243,21 +2243,20 @@ class ReplacePropertyNode(CythonTransform):
def visit_CFuncDefNode(self, node):
if not node.decorators:
return node
-
- # transform @property decorators
+ # transform @property decorators on ctypedef class functions
for decorator_node in node.decorators[::-1]:
decorator = decorator_node.decorator
if decorator.is_name and decorator.name == 'property':
if len(node.decorators) > 1:
return self._reject_decorated_property(node, decorator_node)
- name = node.declarator.base.name
- node.name = name #EncodedString('__get__')
- newstats = node.body.analyse_expressions(node.local_scope)
- newnode = newstats.stats[0].value
- newnode.name = name
+ # Mark the node as a cgetter
+ node.type.is_cgetter = True
+ # Add a func_cname to be output instead of the attribute
+ node.entry.func_cname = node.body.stats[0].value.function.name
+ # done - remove the decorator node
node.decorators.remove(decorator_node)
- return [newnode]
- # XXX still need to update everywhere that used the old node
+ return [node]
+
class FindInvalidUseOfFusedTypes(CythonTransform):
diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py
index e9606b58d..504783b33 100644
--- a/Cython/Compiler/Symtab.py
+++ b/Cython/Compiler/Symtab.py
@@ -752,7 +752,8 @@ class Scope(object):
def declare_cfunction(self, name, type, pos,
cname=None, visibility='private', api=0, in_pxd=0,
- defining=0, modifiers=(), utility_code=None, overridable=False):
+ defining=0, modifiers=(), utility_code=None,
+ overridable=False, property=False):
# Add an entry for a C function.
if not cname:
if visibility != 'private' or api:
@@ -829,7 +830,8 @@ class Scope(object):
type.entry = entry
return entry
- def add_cfunction(self, name, type, pos, cname, visibility, modifiers, inherited=False):
+ def add_cfunction(self, name, type, pos, cname, visibility, modifiers,
+ inherited=False, property=False):
# Add a C function entry without giving it a func_cname.
entry = self.declare(name, cname, type, pos, visibility)
entry.is_cfunction = 1
@@ -837,6 +839,8 @@ class Scope(object):
entry.func_modifiers = modifiers
if inherited or type.is_fused:
self.cfunc_entries.append(entry)
+ elif property:
+ self.property_entries.append(entry)
else:
# For backwards compatibility reasons, we must keep all non-fused methods
# before all fused methods, but separately for each type.
@@ -1435,7 +1439,8 @@ class ModuleScope(Scope):
def declare_cfunction(self, name, type, pos,
cname=None, visibility='private', api=0, in_pxd=0,
- defining=0, modifiers=(), utility_code=None, overridable=False):
+ defining=0, modifiers=(), utility_code=None,
+ overridable=False, property=False):
if not defining and 'inline' in modifiers:
# TODO(github/1736): Make this an error.
warning(pos, "Declarations should not be declared inline.", 1)
@@ -1459,7 +1464,7 @@ class ModuleScope(Scope):
self, name, type, pos,
cname=cname, visibility=visibility, api=api, in_pxd=in_pxd,
defining=defining, modifiers=modifiers, utility_code=utility_code,
- overridable=overridable)
+ overridable=overridable, property=property)
return entry
def declare_global(self, name, pos):
@@ -2214,7 +2219,8 @@ class CClassScope(ClassScope):
def declare_cfunction(self, name, type, pos,
cname=None, visibility='private', api=0, in_pxd=0,
- defining=0, modifiers=(), utility_code=None, overridable=False):
+ defining=0, modifiers=(), utility_code=None,
+ overridable=False, property=False):
if get_special_method_signature(name) and not self.parent_type.is_builtin_type:
error(pos, "Special methods must be declared with 'def', not 'cdef'")
args = type.args
@@ -2258,7 +2264,8 @@ class CClassScope(ClassScope):
error(pos,
"C method '%s' not previously declared in definition part of"
" extension type '%s'" % (name, self.class_name))
- entry = self.add_cfunction(name, type, pos, cname, visibility, modifiers)
+ entry = self.add_cfunction(name, type, pos, cname, visibility,
+ modifiers, property=property)
if defining:
entry.func_cname = self.mangle(Naming.func_prefix, name)
entry.utility_code = utility_code
@@ -2274,11 +2281,13 @@ class CClassScope(ClassScope):
return entry
- def add_cfunction(self, name, type, pos, cname, visibility, modifiers, inherited=False):
+ def add_cfunction(self, name, type, pos, cname, visibility, modifiers,
+ inherited=False, property=False):
# Add a cfunction entry without giving it a func_cname.
prev_entry = self.lookup_here(name)
entry = ClassScope.add_cfunction(self, name, type, pos, cname,
- visibility, modifiers, inherited=inherited)
+ visibility, modifiers,
+ inherited=inherited, property=property)
entry.is_cmethod = 1
entry.prev_entry = prev_entry
return entry