cython

Форк
0
/
ext_attr_getter.srctree 
367 строк · 8.2 Кб
1
# mode: run
2
# tag: cgetter, property
3

4
"""
5
PYTHON setup.py build_ext --inplace
6
PYTHON run_failure_tests.py
7
PYTHON runner.py
8
"""
9

10
######## setup.py ########
11

12
from Cython.Build.Dependencies import cythonize
13
from distutils.core import setup
14

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))
18

19

20
######## run_failure_tests.py ########
21

22
import glob
23
import sys
24

25
from Cython.Build.Dependencies import cythonize
26
from Cython.Compiler.Errors import CompileError
27

28
# Run the failure tests
29
failed_tests = []
30
passed_tests = []
31

32
def run_test(name):
33
    title = name
34
    with open(name, 'r') as f:
35
        for line in f:
36
            if 'TEST' in line:
37
                title = line.partition('TEST:')[2].strip()
38
                break
39
    sys.stderr.write("\n### TESTING: %s\n" % title)
40

41
    try:
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)
46
    else:
47
        sys.stderr.write("\nFAIL: compilation did not detect the error\n")
48
        failed_tests.append(name)
49

50
for name in sorted(glob.glob("getter_fail*.pyx")):
51
    run_test(name)
52

53
assert not failed_tests, "Failed tests: %s" % failed_tests
54
assert passed_tests  # check that tests were found at all
55

56

57
######## foo.h ########
58

59
#include <Python.h>
60

61
#ifdef __cplusplus
62
extern "C" {
63
#endif
64

65
typedef struct {
66
    PyObject_HEAD
67
    int f0;
68
    int f1;
69
    int f2;
70
    int v[10];
71
} FooStructNominal;
72

73
typedef struct {
74
    PyObject_HEAD
75
} FooStructOpaque;
76

77

78
#define PyFoo_GET0M(a) (((FooStructNominal*)a)->f0)
79
#define PyFoo_GET1M(a) (((FooStructNominal*)a)->f1)
80
#define PyFoo_GET2M(a) (((FooStructNominal*)a)->f2)
81

82
int PyFoo_Get0F(FooStructOpaque *f)
83
{
84
    return PyFoo_GET0M(f);
85
}
86

87
int PyFoo_Get1F(FooStructOpaque *f)
88
{
89
    return PyFoo_GET1M(f);
90
}
91

92
int PyFoo_Get2F(FooStructOpaque *f)
93
{
94
    return PyFoo_GET2M(f);
95
}
96

97
int *PyFoo_GetV(FooStructOpaque *f)
98
{
99
    return ((FooStructNominal*)f)->v;
100
}
101

102
#ifdef __cplusplus
103
}
104
#endif
105

106

107
######## foo_extension.pyx ########
108

109
cdef class Foo:
110
    cdef public int _field0, _field1, _field2;
111
    cdef public int _vector[10];
112

113
    @property
114
    def field0(self):
115
        return self._field0
116

117
    @property
118
    def field1(self):
119
        return self._field1
120

121
    @property
122
    def field2(self):
123
        return self._field2
124

125
    def __init__(self, f0, f1, f2, vec=None):
126
        if vec is None:
127
            vec = ()
128
        if not isinstance(vec, tuple):
129
            raise ValueError("v must be None or a tuple")
130
        self._field0 = f0
131
        self._field1 = f1
132
        self._field2 = f2
133
        i = 0
134
        for v in vec:
135
            self._vector[i] = v
136
            if i > 9:
137
                break
138
            i += 1
139
        for j in range(i,10):
140
            self._vector[j] = 0
141

142
# A pure-python class that disallows direct access to fields
143
class OpaqueFoo(Foo):
144

145
    @property
146
    def field0(self):
147
        raise AttributeError('no direct access to field0')
148

149
    @property
150
    def field1(self):
151
        raise AttributeError('no direct access to field1')
152

153
    @property
154
    def field2(self):
155
        raise AttributeError('no direct access to field2')
156

157

158
######## getter0.pyx ########
159

160
# Access base Foo fields from C via aliased field names
161

162
cdef extern from "foo.h":
163

164
    ctypedef class foo_extension.Foo [object FooStructNominal]:
165
        cdef:
166
            int field0 "f0"
167
            int field1 "f1"
168
            int field2 "f2"
169

170
def sum(Foo f):
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
174

175
def check_pyobj(Foo f):
176
    # compare the c code to the check_pyobj in getter2.pyx
177
    return bool(f.field1)
178

179

180
######## getter.pxd ########
181

182
# Access base Foo fields from C via getter functions
183

184

185
cdef extern from "foo.h":
186
    ctypedef class foo_extension.Foo [object FooStructOpaque, check_size ignore]:
187
        @property
188
        cdef inline int fieldM0(self):
189
            return PyFoo_GET0M(self)
190

191
        @property
192
        cdef inline int fieldF1(self) except -123:
193
            return PyFoo_Get1F(self)
194

195
        @property
196
        cdef inline int fieldM2(self):
197
            return PyFoo_GET2M(self)
198

199
        @property
200
        cdef inline int *vector(self):
201
            return PyFoo_GetV(self)
202

203
        @property
204
        cdef inline int meaning_of_life(self) except -99:
205
            cdef int ret = 21
206
            ret *= 2
207
            return ret
208

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);
213

214

215
######## getter1.pyx ########
216

217
cimport getter
218

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
223

224
def check_10(getter.Foo f):
225
    return f.fieldF1 != 10
226

227
def vec0(getter.Foo f):
228
    return f.vector[0]
229

230
def check_binop(getter.Foo f):
231
    return f.fieldF1 / 10
232

233

234
######## getter2.pyx ########
235

236
cimport getter
237

238
def check_pyobj(getter.Foo f):
239
    return bool(f.fieldF1)
240
 
241
def check_unary(getter.Foo f):
242
    return -f.fieldF1
243

244
def check_meaning_of_life(getter.Foo f):
245
    return f.meaning_of_life
246

247

248
######## getter_fail_classmethod.pyx ########
249

250
# TEST: Make sure not all decorators are accepted.
251

252
cdef extern from "foo.h":
253
    ctypedef class foo_extension.Foo [object FooStructOpaque]:
254
        @property
255
        @classmethod
256
        cdef inline int field0(cls):
257
            print('in classmethod of Foo')
258

259

260
######## getter_fail_dot_getter.pyx ########
261

262
# TEST: Make sure not all decorators are accepted.
263

264
cdef extern from "foo.h":
265
    ctypedef class foo_extension.Foo [object FooStructOpaque]:
266
        @property
267
        cdef inline int field0(self):
268
            pass
269

270
        @field0.getter
271
        cdef inline void field1(self):
272
            pass
273

274

275
######## getter_fail_no_inline.pyx ########
276

277
# TEST: Properties must be declared "inline".
278

279
cdef extern from "foo.h":
280
    ctypedef class foo_extension.Foo [object FooStructOpaque]:
281
        @property
282
        cdef int field0(self):
283
            pass
284

285

286
######## getter_fail_void.pyx ########
287

288
# TEST: Properties must have a non-void return type.
289

290
cdef extern from "foo.h":
291
    ctypedef class foo_extension.Foo [object FooStructOpaque]:
292
        @property
293
        cdef void field0(self):
294
            pass
295

296

297
######## getter_fail_no_args.pyx ########
298

299
# TEST: Properties must have the right signature.
300

301
cdef extern from "foo.h":
302
    ctypedef class foo_extension.Foo [object FooStructOpaque]:
303
        @property
304
        cdef int field0():
305
            pass
306

307

308
######## getter_fail_too_many_args.pyx ########
309

310
# TEST: Properties must have the right signature.
311

312
cdef extern from "foo.h":
313
    ctypedef class foo_extension.Foo [object FooStructOpaque]:
314
        @property
315
        cdef int field0(x, y):
316
            pass
317

318

319
######## runner.py ########
320

321
import warnings
322
import foo_extension, getter0, getter1, getter2
323

324
def sum(f):
325
    # pure python field access, but code is identical to cython cdef sum
326
    return f.field0 + f.field1 + f.field2
327

328
# Baseline test: if this fails something else is wrong
329
foo = foo_extension.Foo(23, 123, 1023)
330

331
assert foo.field0 == 23
332
assert foo.field1 == 123
333
assert foo.field2 == 1023
334

335
ret =  getter0.sum(foo)
336
assert ret == sum(foo)
337

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
341

342
opaque_foo = foo_extension.OpaqueFoo(23, 123, 1023)
343

344
opaque_ret = getter0.sum(opaque_foo)
345
assert opaque_ret == ret
346

347
val = getter2.check_pyobj(opaque_foo)
348
assert val is True
349
val = getter2.check_unary(opaque_foo)
350
assert val == -123
351
val = getter2.check_meaning_of_life(opaque_foo)
352
assert val == 42
353

354
try:
355
    f0 = opaque_ret.field0
356
    assert False
357
except AttributeError as e:
358
    pass
359

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
363

364
opaque_foo = foo_extension.OpaqueFoo(23, 123, 1023, (1, 2, 3))
365

366
opaque_ret = getter1.sum(opaque_foo)
367
assert opaque_ret == ret
368

369

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.