2
# tag: cgetter, property
5
PYTHON setup.py build_ext --inplace
6
PYTHON run_failure_tests.py
10
######## setup.py ########
12
from Cython.Build.Dependencies import cythonize
13
from distutils.core import setup
15
# Enforce the right build order
16
setup(ext_modules = cythonize("foo_extension.pyx", language_level=3))
17
setup(ext_modules = cythonize("getter[0-9].pyx", language_level=3))
20
######## run_failure_tests.py ########
25
from Cython.Build.Dependencies import cythonize
26
from Cython.Compiler.Errors import CompileError
28
# Run the failure tests
34
with open(name, 'r') as f:
37
title = line.partition('TEST:')[2].strip()
39
sys.stderr.write("\n### TESTING: %s\n" % title)
42
cythonize(name, language_level=3)
43
except CompileError as e:
44
sys.stderr.write("\nOK: got expected exception\n")
45
passed_tests.append(name)
47
sys.stderr.write("\nFAIL: compilation did not detect the error\n")
48
failed_tests.append(name)
50
for name in sorted(glob.glob("getter_fail*.pyx")):
53
assert not failed_tests, "Failed tests: %s" % failed_tests
54
assert passed_tests # check that tests were found at all
57
######## foo.h ########
78
#define PyFoo_GET0M(a) (((FooStructNominal*)a)->f0)
79
#define PyFoo_GET1M(a) (((FooStructNominal*)a)->f1)
80
#define PyFoo_GET2M(a) (((FooStructNominal*)a)->f2)
82
int PyFoo_Get0F(FooStructOpaque *f)
84
return PyFoo_GET0M(f);
87
int PyFoo_Get1F(FooStructOpaque *f)
89
return PyFoo_GET1M(f);
92
int PyFoo_Get2F(FooStructOpaque *f)
94
return PyFoo_GET2M(f);
97
int *PyFoo_GetV(FooStructOpaque *f)
99
return ((FooStructNominal*)f)->v;
107
######## foo_extension.pyx ########
110
cdef public int _field0, _field1, _field2;
111
cdef public int _vector[10];
125
def __init__(self, f0, f1, f2, vec=None):
128
if not isinstance(vec, tuple):
129
raise ValueError("v must be None or a tuple")
139
for j in range(i,10):
142
# A pure-python class that disallows direct access to fields
147
raise AttributeError('no direct access to field0')
151
raise AttributeError('no direct access to field1')
155
raise AttributeError('no direct access to field2')
158
######## getter0.pyx ########
160
# Access base Foo fields from C via aliased field names
162
cdef extern from "foo.h":
164
ctypedef class foo_extension.Foo [object FooStructNominal]:
171
# Note - not a cdef function but compiling the f.__getattr__('field0')
172
# notices the alias and replaces the __getattr__ in c by f->f0 anyway
173
return f.field0 + f.field1 + f.field2
175
def check_pyobj(Foo f):
176
# compare the c code to the check_pyobj in getter2.pyx
177
return bool(f.field1)
180
######## getter.pxd ########
182
# Access base Foo fields from C via getter functions
185
cdef extern from "foo.h":
186
ctypedef class foo_extension.Foo [object FooStructOpaque, check_size ignore]:
188
cdef inline int fieldM0(self):
189
return PyFoo_GET0M(self)
192
cdef inline int fieldF1(self) except -123:
193
return PyFoo_Get1F(self)
196
cdef inline int fieldM2(self):
197
return PyFoo_GET2M(self)
200
cdef inline int *vector(self):
201
return PyFoo_GetV(self)
204
cdef inline int meaning_of_life(self) except -99:
209
int PyFoo_GET0M(Foo); # this is actually a macro !
210
int PyFoo_Get1F(Foo);
211
int PyFoo_GET2M(Foo); # this is actually a macro !
212
int *PyFoo_GetV(Foo);
215
######## getter1.pyx ########
219
def sum(getter.Foo f):
220
# Note - not a cdef function but compiling the f.__getattr__('field0')
221
# notices the getter and replaces the __getattr__ in c by PyFoo_GET anyway
222
return f.fieldM0 + f.fieldF1 + f.fieldM2
224
def check_10(getter.Foo f):
225
return f.fieldF1 != 10
227
def vec0(getter.Foo f):
230
def check_binop(getter.Foo f):
231
return f.fieldF1 / 10
234
######## getter2.pyx ########
238
def check_pyobj(getter.Foo f):
239
return bool(f.fieldF1)
241
def check_unary(getter.Foo f):
244
def check_meaning_of_life(getter.Foo f):
245
return f.meaning_of_life
248
######## getter_fail_classmethod.pyx ########
250
# TEST: Make sure not all decorators are accepted.
252
cdef extern from "foo.h":
253
ctypedef class foo_extension.Foo [object FooStructOpaque]:
256
cdef inline int field0(cls):
257
print('in classmethod of Foo')
260
######## getter_fail_dot_getter.pyx ########
262
# TEST: Make sure not all decorators are accepted.
264
cdef extern from "foo.h":
265
ctypedef class foo_extension.Foo [object FooStructOpaque]:
267
cdef inline int field0(self):
271
cdef inline void field1(self):
275
######## getter_fail_no_inline.pyx ########
277
# TEST: Properties must be declared "inline".
279
cdef extern from "foo.h":
280
ctypedef class foo_extension.Foo [object FooStructOpaque]:
282
cdef int field0(self):
286
######## getter_fail_void.pyx ########
288
# TEST: Properties must have a non-void return type.
290
cdef extern from "foo.h":
291
ctypedef class foo_extension.Foo [object FooStructOpaque]:
293
cdef void field0(self):
297
######## getter_fail_no_args.pyx ########
299
# TEST: Properties must have the right signature.
301
cdef extern from "foo.h":
302
ctypedef class foo_extension.Foo [object FooStructOpaque]:
308
######## getter_fail_too_many_args.pyx ########
310
# TEST: Properties must have the right signature.
312
cdef extern from "foo.h":
313
ctypedef class foo_extension.Foo [object FooStructOpaque]:
315
cdef int field0(x, y):
319
######## runner.py ########
322
import foo_extension, getter0, getter1, getter2
325
# pure python field access, but code is identical to cython cdef sum
326
return f.field0 + f.field1 + f.field2
328
# Baseline test: if this fails something else is wrong
329
foo = foo_extension.Foo(23, 123, 1023)
331
assert foo.field0 == 23
332
assert foo.field1 == 123
333
assert foo.field2 == 1023
335
ret = getter0.sum(foo)
336
assert ret == sum(foo)
338
# Aliasing test. Check 'cdef int field0 "f0" works as advertised:
339
# - C can access the fields through the aliases
340
# - Python cannot access the fields at all
342
opaque_foo = foo_extension.OpaqueFoo(23, 123, 1023)
344
opaque_ret = getter0.sum(opaque_foo)
345
assert opaque_ret == ret
347
val = getter2.check_pyobj(opaque_foo)
349
val = getter2.check_unary(opaque_foo)
351
val = getter2.check_meaning_of_life(opaque_foo)
355
f0 = opaque_ret.field0
357
except AttributeError as e:
360
# Getter test. Check C-level getter works as advertised:
361
# - C accesses the fields through getter calls (maybe macros)
362
# - Python accesses the fields through attribute lookup
364
opaque_foo = foo_extension.OpaqueFoo(23, 123, 1023, (1, 2, 3))
366
opaque_ret = getter1.sum(opaque_foo)
367
assert opaque_ret == ret