Commit 30628523 authored by Matti Picus's avatar Matti Picus Committed by Stefan Behnel

Support wider use of @property decorator on CClassDef AttributeNodes (GH-3095)

* TEST: add cgetter test for pointer and pxd use
* BUG: handle more AttributeNode-with-property-decorator
* BUG: fix numpy/__init__.pxd
* ENH: add @property for ndarray.size, formatting cleanup
parent 2ffd5060
...@@ -875,6 +875,8 @@ class ExprNode(Node): ...@@ -875,6 +875,8 @@ class ExprNode(Node):
# #
src = self src = self
src_type = self.type src_type = self.type
if src_type.is_cfunction and src_type.entry.is_cgetter:
src_type = src_type.return_type
if self.check_for_coercion_error(dst_type, env): if self.check_for_coercion_error(dst_type, env):
return self return self
...@@ -3633,7 +3635,10 @@ class IndexNode(_IndexingBaseNode): ...@@ -3633,7 +3635,10 @@ class IndexNode(_IndexingBaseNode):
self.nogil = env.nogil self.nogil = env.nogil
base_type = self.base.type base_type = self.base.type
if not base_type.is_cfunction: if base_type.is_cfunction:
if self.base.entry.is_cgetter:
base_type = base_type.return_type
else:
self.index = self.index.analyse_types(env) self.index = self.index.analyse_types(env)
self.original_index_type = self.index.type self.original_index_type = self.index.type
...@@ -3720,6 +3725,9 @@ class IndexNode(_IndexingBaseNode): ...@@ -3720,6 +3725,9 @@ class IndexNode(_IndexingBaseNode):
def analyse_as_c_array(self, env, is_slice): def analyse_as_c_array(self, env, is_slice):
base_type = self.base.type base_type = self.base.type
if hasattr(self.base, 'entry') and self.base.entry.is_cgetter:
self.type = base_type.return_type.base_type
else:
self.type = base_type.base_type self.type = base_type.base_type
if is_slice: if is_slice:
self.type = base_type self.type = base_type
...@@ -3969,6 +3977,9 @@ class IndexNode(_IndexingBaseNode): ...@@ -3969,6 +3977,9 @@ class IndexNode(_IndexingBaseNode):
else: else:
assert False, "unexpected base type in indexing: %s" % self.base.type assert False, "unexpected base type in indexing: %s" % self.base.type
elif self.base.type.is_cfunction: elif self.base.type.is_cfunction:
if self.base.entry.is_cgetter:
index_code = "(%s[%s])"
else:
return "%s<%s>" % ( return "%s<%s>" % (
self.base.result(), self.base.result(),
",".join([param.empty_declaration_code() for param in self.type_indices])) ",".join([param.empty_declaration_code() for param in self.type_indices]))
...@@ -12212,6 +12223,10 @@ class CmpNode(object): ...@@ -12212,6 +12223,10 @@ class CmpNode(object):
operand2 = self.operand2 operand2 = self.operand2
type1 = operand1.type type1 = operand1.type
type2 = operand2.type type2 = operand2.type
if type1.is_cfunction and type1.entry.is_cgetter:
type1 = type1.return_type
if type2.is_cfunction and type2.entry.is_cgetter:
type2 = type2.return_type
new_common_type = None new_common_type = None
......
...@@ -1767,6 +1767,11 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1767,6 +1767,11 @@ class FuncDefNode(StatNode, BlockNode):
def generate_function_definitions(self, env, code): def generate_function_definitions(self, env, code):
from . import Buffer from . import Buffer
if self.entry.is_cgetter:
# no code to generate
return
if self.return_type.is_memoryviewslice: if self.return_type.is_memoryviewslice:
from . import MemoryView from . import MemoryView
......
...@@ -581,7 +581,9 @@ class PxdPostParse(CythonTransform, SkipDeclarations): ...@@ -581,7 +581,9 @@ class PxdPostParse(CythonTransform, SkipDeclarations):
err = None # allow these slots err = None # allow these slots
if isinstance(node, Nodes.CFuncDefNode): if isinstance(node, Nodes.CFuncDefNode):
if (u'inline' in node.modifiers and if node.decorators and self.scope_type == 'cclass':
err = None
elif (u'inline' in node.modifiers and
self.scope_type in ('pxd', 'cclass')): self.scope_type in ('pxd', 'cclass')):
node.inline_in_pxd = True node.inline_in_pxd = True
if node.visibility != 'private': if node.visibility != 'private':
......
...@@ -244,14 +244,27 @@ cdef extern from "numpy/arrayobject.h": ...@@ -244,14 +244,27 @@ cdef extern from "numpy/arrayobject.h":
cdef: cdef:
# Only taking a few of the most commonly used and stable fields. # Only taking a few of the most commonly used and stable fields.
# One should use PyArray_* macros instead to access the C fields.
char *data char *data
int ndim "nd" dtype descr
npy_intp *shape "dimensions"
npy_intp *strides
dtype descr # deprecated since NumPy 1.7 !
PyObject* base PyObject* base
@property
cdef int ndim(self):
return PyArray_NDIM(self)
@property
cdef npy_intp *shape(self):
return PyArray_DIMS(self)
@property
cdef npy_intp *strides(self):
return PyArray_STRIDES(self)
@property
cdef npy_intp size(self):
return PyArray_SIZE(ndarray)
# Note: This syntax (function definition in pxd files) is an # Note: This syntax (function definition in pxd files) is an
# experimental exception made for __getbuffer__ and __releasebuffer__ # experimental exception made for __getbuffer__ and __releasebuffer__
# -- the details of this may change. # -- the details of this may change.
......
...@@ -8,8 +8,7 @@ from Cython.Compiler.Errors import CompileError ...@@ -8,8 +8,7 @@ from Cython.Compiler.Errors import CompileError
from distutils.core import setup from distutils.core import setup
# force the build order # force the build order
setup(ext_modules= cythonize("foo_extension.pyx", language_level=3)) setup(ext_modules = cythonize("foo_extension.pyx", language_level=3))
setup(ext_modules = cythonize("getter[0-9].pyx", language_level=3)) setup(ext_modules = cythonize("getter[0-9].pyx", language_level=3))
for name in ("getter_fail0.pyx", "getter_fail1.pyx"): for name in ("getter_fail0.pyx", "getter_fail1.pyx"):
...@@ -32,6 +31,7 @@ typedef struct { ...@@ -32,6 +31,7 @@ typedef struct {
int f0; int f0;
int f1; int f1;
int f2; int f2;
int v[10];
} FooStructNominal; } FooStructNominal;
typedef struct { typedef struct {
...@@ -58,6 +58,11 @@ int PyFoo_Get2F(FooStructOpaque *f) ...@@ -58,6 +58,11 @@ int PyFoo_Get2F(FooStructOpaque *f)
return PyFoo_GET2M(f); return PyFoo_GET2M(f);
} }
int *PyFoo_GetV(FooStructOpaque *f)
{
return ((FooStructNominal*)f)->v;
}
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
...@@ -66,6 +71,7 @@ int PyFoo_Get2F(FooStructOpaque *f) ...@@ -66,6 +71,7 @@ int PyFoo_Get2F(FooStructOpaque *f)
cdef class Foo: cdef class Foo:
cdef public int _field0, _field1, _field2; cdef public int _field0, _field1, _field2;
cdef public int _vector[10];
@property @property
def field0(self): def field0(self):
...@@ -79,10 +85,22 @@ cdef class Foo: ...@@ -79,10 +85,22 @@ cdef class Foo:
def field2(self): def field2(self):
return self._field2 return self._field2
def __init__(self, f0, f1, f2): def __init__(self, f0, f1, f2, vec=None):
if vec is None:
vec = ()
if not isinstance(vec, tuple):
raise ValueError("v must be None or a tuple")
self._field0 = f0 self._field0 = f0
self._field1 = f1 self._field1 = f1
self._field2 = f2 self._field2 = f2
i = 0
for v in vec:
self._vector[i] = v
if i > 9:
break
i += 1
for j in range(i,10):
self._vector[j] = 0
# A pure-python class that disallows direct access to fields # A pure-python class that disallows direct access to fields
class OpaqueFoo(Foo): class OpaqueFoo(Foo):
...@@ -116,7 +134,7 @@ def sum(Foo f): ...@@ -116,7 +134,7 @@ def sum(Foo f):
# notices the alias and replaces the __getattr__ in c by f->f0 anyway # notices the alias and replaces the __getattr__ in c by f->f0 anyway
return f.field0 + f.field1 + f.field2 return f.field0 + f.field1 + f.field2
######## getter1.pyx ######## ######## getter.pxd ########
# Access base Foo fields from C via getter functions # Access base Foo fields from C via getter functions
...@@ -135,15 +153,29 @@ cdef extern from "foo.h": ...@@ -135,15 +153,29 @@ cdef extern from "foo.h":
cdef int fieldM2(self): cdef int fieldM2(self):
return PyFoo_GET2M(self) return PyFoo_GET2M(self)
@property
cdef int *vector(self):
return PyFoo_GetV(self)
int PyFoo_GET0M(Foo); # this is actually a macro ! int PyFoo_GET0M(Foo); # this is actually a macro !
int PyFoo_Get1F(Foo); int PyFoo_Get1F(Foo);
int PyFoo_GET2M(Foo); # this is actually a macro ! int PyFoo_GET2M(Foo); # this is actually a macro !
int *PyFoo_GetV(Foo);
def sum(Foo f): ######## getter1.pyx ########
cimport getter
def sum(getter.Foo f):
# Note - not a cdef function but compiling the f.__getattr__('field0') # Note - not a cdef function but compiling the f.__getattr__('field0')
# notices the getter and replaces the __getattr__ in c by PyFoo_GET anyway # notices the getter and replaces the __getattr__ in c by PyFoo_GET anyway
return f.fieldM0 + f.fieldF1 + f.fieldM2 return f.fieldM0 + f.fieldF1 + f.fieldM2
def check_10(getter.Foo f):
return f.fieldF1 != 10
def vec0(getter.Foo f):
return f.vector[0]
######## getter_fail0.pyx ######## ######## getter_fail0.pyx ########
...@@ -203,7 +235,7 @@ except AttributeError as e: ...@@ -203,7 +235,7 @@ except AttributeError as e:
# - C accesses the fields through getter calls (maybe macros) # - C accesses the fields through getter calls (maybe macros)
# - Python accesses the fields through attribute lookup # - Python accesses the fields through attribute lookup
opaque_foo = foo_extension.OpaqueFoo(23, 123, 1023) opaque_foo = foo_extension.OpaqueFoo(23, 123, 1023, (1, 2, 3))
opaque_ret = getter1.sum(opaque_foo) opaque_ret = getter1.sum(opaque_foo)
assert opaque_ret == ret assert opaque_ret == ret
......
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