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 }