1 /**
2 This module contains D code for the contract tests between Python
3 and its C API regarding user-defined types.
4 */
5 module contract.udt;
6
7
8 import python;
9
10
11 extern(C):
12
13
14 package PyObject* simple_struct_func(PyObject* self, PyObject *args) nothrow @nogc {
15
16 static struct MyType {
17 mixin PyObjectHead;
18 int i = 2;
19 double d = 3;
20 }
21
22 // either this of PyGetSetDef
23 static PyMemberDef[3] members;
24 members[0].name = cast(typeof(PyMemberDef.name)) &"the_int"[0];
25 members[0].type = MemberType.Int;
26 members[0].offset = MyType.i.offsetof;
27 members[1].name = cast(typeof(PyMemberDef.name)) &"the_double"[0];
28 members[1].type = MemberType.Double;
29 members[1].offset = MyType.d.offsetof;
30
31 static PyTypeObject type;
32
33 if(type == type.init) {
34 type.tp_name = &"MyType"[0];
35 type.tp_basicsize = MyType.sizeof;
36 type.tp_members = &members[0];
37 type.tp_flags = TypeFlags.Default;
38
39 if(PyType_Ready(&type) < 0) {
40 PyErr_SetString(PyExc_TypeError, &"not ready"[0]);
41 return null;
42 }
43 }
44
45 auto obj = pyObjectNew!MyType(&type);
46 obj.i = 42;
47 obj.d = 33.3;
48
49 return cast(typeof(return)) obj;
50 }
51
52
53 package PyObject* twice_struct_func(PyObject* self, PyObject *args) nothrow @nogc {
54
55 import python.cooked: pyMethodDef;
56
57 static struct Twice {
58 mixin PyObjectHead;
59 int i;
60 int twice() @safe @nogc pure nothrow const {
61 return i * 2;
62 }
63 }
64
65 static extern(C) PyObject* twice(Twice* self, PyObject* args) {
66 return PyLong_FromLong(self.twice);
67 }
68
69 if(PyTuple_Size(args) != 1) {
70 PyErr_SetString(PyExc_TypeError, &"Must be used with one parameter"[0]);
71 return null;
72 }
73
74 auto arg = PyTuple_GetItem(args, 0);
75 if(arg is null) {
76 PyErr_SetString(PyExc_TypeError, &"Could not get 1st argument"[0]);
77 return null;
78 }
79
80 static PyMethodDef[2] methods;
81 // can't use a function literal because they're not allowed to be extern(C)
82 methods[0] = pyMethodDef!("twice")(&twice);
83
84 static PyTypeObject type;
85
86 if(type == type.init) {
87 type.tp_name = &"Twice"[0];
88 type.tp_basicsize = Twice.sizeof;
89 type.tp_methods = &methods[0];
90 type.tp_flags = TypeFlags.Default;
91
92 if(PyType_Ready(&type) < 0) {
93 PyErr_SetString(PyExc_TypeError, &"not ready"[0]);
94 return null;
95 }
96 }
97
98 auto obj = pyObjectNew!Twice(&type);
99 obj.i = cast(int) PyLong_AsLong(arg);
100
101 return cast(typeof(return)) obj;
102 }
103
104
105 struct StructDefaultCtor {
106 int i = 42;
107 }
108
109 struct StructUserCtor {
110 int i = 42;
111 double d = 33.3;
112 string s = "foobar";
113 private int _shouldBeOk;
114
115 this(int i, string s) {
116 this.i = i;
117 this.d = 77.7;
118 this.s = s;
119 }
120
121 this(int i, double d, string s) {
122 this.i = i;
123 this.d = d;
124 this.s = s;
125 }
126 }
127
128
129 package PyObject* struct_getset(PyObject* self, PyObject *args) nothrow @nogc {
130
131 static struct StructGetSet {
132 mixin PyObjectHead;
133 double d;
134 Inner inner;
135
136 static struct Inner {
137 mixin PyObjectHead;
138 int i;
139 double d;
140 // we don't bother wrapping `s` just to show that what Python sees
141 // doesn't have to be exactly what D does.
142 string s;
143 }
144 }
145
146 static extern(C) PyObject* getOuterInt(PyObject*, void*) {
147 return PyLong_FromLong(42);
148 }
149
150 static extern(C) PyObject* getOuterDouble(PyObject* self_, void*) {
151 auto self = cast(StructGetSet*) self_;
152 return PyFloat_FromDouble(self.d);
153 }
154
155 static extern(C) int setOuterDouble(PyObject* self_, PyObject* value, void*) {
156 auto self = cast(StructGetSet*) self_;
157 self.d = PyFloat_AsDouble(value);
158 return 0;
159 }
160
161 static extern(C) PyObject* getInnerInt(PyObject* self_, void*) {
162 auto self = cast(StructGetSet.Inner*) self_;
163 return PyLong_FromLong(self.i);
164 }
165
166 static extern(C) PyObject* getInnerDouble(PyObject* self_, void*) {
167 auto self = cast(StructGetSet.Inner*) self_;
168 return PyFloat_FromDouble(self.d);
169 }
170
171 static extern(C) int setInnerInt(PyObject* self_, PyObject* value, void*) {
172 auto self = cast(StructGetSet.Inner*) self_;
173 self.i = cast(int) PyLong_AsLong(value);
174 return 0;
175 }
176
177 static extern(C) int setInnerDouble(PyObject* self_, PyObject* value, void*) {
178 auto self = cast(StructGetSet.Inner*) self_;
179 self.d = PyFloat_AsDouble(value);
180 return 0;
181 }
182
183 extern(C) static PyObject* reprOuter(PyObject* self_) {
184 import std.conv: text;
185
186 auto self = cast(StructGetSet*) self_;
187 auto ret = text(*self);
188
189 return pyUnicodeDecodeUTF8(ret.ptr, ret.length, null /*errors*/);
190 }
191
192 extern(C) static PyObject* reprInner(PyObject* self_) {
193 import std.conv: text;
194
195 auto self = cast(StructGetSet.Inner*) self_;
196 auto ret = text(*self);
197
198 return pyUnicodeDecodeUTF8(ret.ptr, ret.length, null /*errors*/);
199 }
200
201 static PyTypeObject outerType;
202 static PyTypeObject innerType;
203
204 static extern(C) PyObject* getInner(PyObject* self_, void*) {
205 auto self = cast(StructGetSet*) self_;
206 auto innerObj = cast(PyObject*) &self.inner;
207 pyIncRef(innerObj);
208 return innerObj;
209 }
210
211 static PyGetSetDef[10] outerGetSets;
212 static PyGetSetDef[10] innerGetSets;
213
214 if(outerType == outerType.init) {
215
216 outerGetSets[0].name = cast(typeof(PyGetSetDef.name)) &"i"[0];
217 outerGetSets[0].get = &getOuterInt;
218 // outerGetSets[0].set is null so will raise AttributeError
219 outerGetSets[1].name = cast(typeof(PyGetSetDef.name)) &"d"[0];
220 outerGetSets[1].get = &getOuterDouble;
221 outerGetSets[1].set = &setOuterDouble;
222 outerGetSets[2].name = cast(typeof(PyGetSetDef.name)) &"inner"[0];
223 outerGetSets[2].get = &getInner;
224
225 outerType.tp_name = &"StructGetSet"[0];
226 outerType.tp_basicsize = StructGetSet.sizeof;
227 outerType.tp_flags = TypeFlags.Default;
228 outerType.tp_repr = outerType.tp_str = &reprOuter;
229 outerType.tp_getset = &outerGetSets[0];
230
231 if(PyType_Ready(&outerType) < 0) {
232 PyErr_SetString(PyExc_TypeError, &"not ready"[0]);
233 return null;
234 }
235
236 innerGetSets[0].name = cast(typeof(PyGetSetDef.name)) &"i"[0];
237 innerGetSets[0].get = &getInnerInt;
238 innerGetSets[0].set = &setInnerInt;
239 innerGetSets[1].name = cast(typeof(PyGetSetDef.name)) &"d"[0];
240 innerGetSets[1].get = &getInnerDouble;
241 innerGetSets[1].set = &setInnerDouble;
242
243 innerType.tp_name = &"StructGetSet.Inner"[0];
244 innerType.tp_basicsize = StructGetSet.Inner.sizeof;
245 innerType.tp_flags = TypeFlags.Default;
246 innerType.tp_repr = innerType.tp_str = &reprInner;
247 innerType.tp_getset = &innerGetSets[0];
248
249 if(PyType_Ready(&innerType) < 0) {
250 PyErr_SetString(PyExc_TypeError, &"not ready"[0]);
251 return null;
252 }
253 }
254
255 auto ret = pyObjectNew!StructGetSet(&outerType);
256 ret.d = 3.3;
257 ret.inner.i = 999;
258 ret.inner.d = 777.77;
259 // this line below is important, it's the equivalent of pyObjectNew above
260 PyObject_Init(cast(PyObject*) &ret.inner, &innerType);
261
262 return cast(typeof(return)) ret;
263 }