summaryrefslogtreecommitdiff
path: root/tests/run/ext_attr_getter.srctree
blob: 6fdf7503ca8fa0dc6b8e2c8d7c04cfb9ee7e5587 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
PYTHON setup.py build_ext --inplace
PYTHON -c "import runner"

######## setup.py ########

from Cython.Build.Dependencies import cythonize
from distutils.core import setup

# force the build order
setup(ext_modules= cythonize("foo_extension.pyx"))

setup(ext_modules = cythonize("getter*.pyx"))

######## foo_nominal.h ########

#include <Python.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef struct {
    PyObject_HEAD
    int f0;
    int f1;
    int f2;
} FooStructNominal;

#ifdef __cplusplus
}
#endif

######## foo_extension.pyx ########

cdef class Foo:
    cdef public int field0, field1, field2;

    def __init__(self, f0, f1, f2):
        self.field0 = f0
        self.field1 = f1
        self.field2 = f2

cdef get_field0(Foo f):
    return f.field0

cdef get_field1(Foo f):
    return f.field1

cdef get_field2(Foo f):
    return f.field2

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

    @property
    def field0(self):
        raise AttributeError('no direct access to field0')

    @property
    def field1(self):
        raise AttributeError('no direct access to field1')

    @property
    def field2(self):
        raise AttributeError('no direct access to field2')


######## getter0.pyx ########

# Access base Foo fields from C via aliased field names

cdef extern from "foo_nominal.h":

    ctypedef class foo_extension.Foo [object FooStructNominal]:
        cdef:
            int field0 "f0"
            int field1 "f1"
            int field2 "f2"

def sum(Foo f):
    # the f.__getattr__('field0') is replaced in c by f->f0
    return f.field0 + f.field1 + f.field2

######## runner.py ########

import foo_extension, getter0

foo = foo_extension.Foo(23, 123, 1023)

assert foo.field0 == 23
assert foo.field1 == 123
assert foo.field2 == 1023

ret =  getter0.sum(foo)
assert ret == foo.field0 + foo.field1 + foo.field2

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

# C can access the fields through the aliases
opaque_ret = getter0.sum(opaque_foo)
assert opaque_ret == ret
try:
    # Python cannot access the fields
    f0 = opaque_ret.field0
    assert False
except AttributeError as e:
    pass