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):
#
src = self
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):
return self
......@@ -3633,7 +3635,10 @@ class IndexNode(_IndexingBaseNode):
self.nogil = env.nogil
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.original_index_type = self.index.type
......@@ -3720,7 +3725,10 @@ class IndexNode(_IndexingBaseNode):
def analyse_as_c_array(self, env, is_slice):
base_type = self.base.type
self.type = base_type.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
if is_slice:
self.type = base_type
elif self.index.type.is_pyobject:
......@@ -3969,9 +3977,12 @@ class IndexNode(_IndexingBaseNode):
else:
assert False, "unexpected base type in indexing: %s" % self.base.type
elif self.base.type.is_cfunction:
return "%s<%s>" % (
self.base.result(),
",".join([param.empty_declaration_code() for param in self.type_indices]))
if self.base.entry.is_cgetter:
index_code = "(%s[%s])"
else:
return "%s<%s>" % (
self.base.result(),
",".join([param.empty_declaration_code() for param in self.type_indices]))
elif self.base.type.is_ctuple:
index = self.index.constant_result
if index < 0:
......@@ -12212,6 +12223,10 @@ class CmpNode(object):
operand2 = self.operand2
type1 = operand1.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
......
......@@ -1767,6 +1767,11 @@ class FuncDefNode(StatNode, BlockNode):
def generate_function_definitions(self, env, code):
from . import Buffer
if self.entry.is_cgetter:
# no code to generate
return
if self.return_type.is_memoryviewslice:
from . import MemoryView
......
......@@ -581,7 +581,9 @@ class PxdPostParse(CythonTransform, SkipDeclarations):
err = None # allow these slots
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')):
node.inline_in_pxd = True
if node.visibility != 'private':
......
......@@ -244,14 +244,27 @@ cdef extern from "numpy/arrayobject.h":
cdef:
# 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
int ndim "nd"
npy_intp *shape "dimensions"
npy_intp *strides
dtype descr # deprecated since NumPy 1.7 !
dtype descr
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
# experimental exception made for __getbuffer__ and __releasebuffer__
# -- the details of this may change.
......
......@@ -8,8 +8,7 @@ from Cython.Compiler.Errors import CompileError
from distutils.core import setup
# 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))
for name in ("getter_fail0.pyx", "getter_fail1.pyx"):
......@@ -32,6 +31,7 @@ typedef struct {
int f0;
int f1;
int f2;
int v[10];
} FooStructNominal;
typedef struct {
......@@ -58,6 +58,11 @@ int PyFoo_Get2F(FooStructOpaque *f)
return PyFoo_GET2M(f);
}
int *PyFoo_GetV(FooStructOpaque *f)
{
return ((FooStructNominal*)f)->v;
}
#ifdef __cplusplus
}
#endif
......@@ -66,6 +71,7 @@ int PyFoo_Get2F(FooStructOpaque *f)
cdef class Foo:
cdef public int _field0, _field1, _field2;
cdef public int _vector[10];
@property
def field0(self):
......@@ -79,10 +85,22 @@ cdef class Foo:
def field2(self):
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._field1 = f1
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
class OpaqueFoo(Foo):
......@@ -116,7 +134,7 @@ def sum(Foo f):
# notices the alias and replaces the __getattr__ in c by f->f0 anyway
return f.field0 + f.field1 + f.field2
######## getter1.pyx ########
######## getter.pxd ########
# Access base Foo fields from C via getter functions
......@@ -135,15 +153,29 @@ cdef extern from "foo.h":
cdef int fieldM2(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_Get1F(Foo);
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')
# notices the getter and replaces the __getattr__ in c by PyFoo_GET anyway
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 ########
......@@ -203,7 +235,7 @@ except AttributeError as e:
# - C accesses the fields through getter calls (maybe macros)
# - 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)
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