1 /** 2 This module contains D code for the contract tests between Python 3 and its C API regarding scalars. The functions below are meant to 4 document the behaviour of said API when values are passed through 5 the layer between the two languages, be it as function parameters 6 or return values. 7 */ 8 module contract.scalars; 9 10 import python; 11 12 extern(C): 13 14 15 package PyObject* always_none(PyObject* self, PyObject *args) nothrow @nogc { 16 pyIncRef(pyNone); 17 return pyNone; 18 } 19 20 21 // Always returns 42. Takes no parameters. 22 // Tests PyLongFromUnsignedLong. 23 package PyObject* the_answer(PyObject* self, PyObject *args) nothrow @nogc { 24 25 if(!PyArg_ParseTuple(args, "")) 26 return null; 27 28 return PyLong_FromUnsignedLong(42UL); 29 } 30 31 32 // returns the boolean opposite of the bool passed in 33 package PyObject* one_bool_param_to_not(PyObject* self, PyObject *args) nothrow @nogc { 34 if(PyTuple_Size(args) != 1) return null; 35 36 auto arg = PyTuple_GetItem(args, 0); 37 if(arg is null) return null; 38 39 if(!pyBoolCheck(arg)) return null; 40 const dArg = arg == pyTrue; 41 42 return PyBool_FromLong(!dArg); 43 } 44 45 46 // returns the number passed in multiplied by two 47 package PyObject* one_int_param_to_times_two(PyObject* self, PyObject *args) nothrow @nogc { 48 49 long arg; 50 51 if(!PyArg_ParseTuple(args, "l", &arg)) 52 return null; 53 54 return PyLong_FromLong(arg * 2); 55 } 56 57 58 // returns the number passed in multiplied by 3 59 package PyObject* one_double_param_to_times_three(PyObject* self, PyObject *args) nothrow @nogc { 60 61 double arg; 62 63 if(!PyArg_ParseTuple(args, "d", &arg)) 64 return null; 65 66 return PyFloat_FromDouble(arg * 3); 67 } 68 69 70 // returns the length of the single passed-in string 71 package PyObject* one_string_param_to_length(PyObject* self, PyObject *args) nothrow @nogc { 72 import core.stdc..string: cstrlen = strlen; 73 74 const char* arg; 75 76 if(!PyArg_ParseTuple(args, "s", &arg)) 77 return null; 78 79 const auto len = cstrlen(arg); 80 return PyLong_FromLong(len); 81 } 82 83 84 // appends "_suffix" to the string passed in 85 package PyObject* one_string_param_to_string(PyObject* self, PyObject *args) nothrow { 86 import std..string: fromStringz; 87 88 if(PyTuple_Size(args) != 1) { 89 PyErr_SetString(PyExc_TypeError, &"Wrong number of arguments"[0]); 90 return null; 91 } 92 93 auto arg = PyTuple_GetItem(args, 0); 94 if(arg is null) { 95 PyErr_SetString(PyExc_TypeError, &"Could not get first argument"[0]); 96 return null; 97 } 98 99 if(!pyUnicodeCheck(arg)) arg = pyObjectUnicode(arg); 100 101 if(!pyUnicodeCheck(arg)) { 102 PyErr_SetString(PyExc_TypeError, &"Argument not a unicode string"[0]); 103 return null; 104 } 105 106 auto unicodeArg = pyUnicodeAsUtf8String(arg); 107 if(!unicodeArg) { 108 PyErr_SetString(PyExc_TypeError, &"Could not decode UTF8"[0]); 109 return null; 110 } 111 112 const ptr = pyBytesAsString(unicodeArg); 113 114 const ret = ptr.fromStringz ~ "_suffix"; 115 116 return pyUnicodeDecodeUTF8(&ret[0], ret.length, null /*errors*/); 117 } 118 119 120 // appends "_suffix" to the string passed in without using the GC 121 package PyObject* one_string_param_to_string_manual_mem(PyObject* self, PyObject *args) nothrow @nogc { 122 import std..string: fromStringz; 123 import core.stdc.stdlib: malloc; 124 import core.stdc..string: strlen; 125 126 if(PyTuple_Size(args) != 1) { 127 PyErr_SetString(PyExc_TypeError, &"Wrong number of arguments"[0]); 128 return null; 129 } 130 131 auto arg = PyTuple_GetItem(args, 0); 132 if(arg is null) { 133 PyErr_SetString(PyExc_TypeError, &"Could not get first argument"[0]); 134 return null; 135 } 136 137 if(!pyUnicodeCheck(arg)) arg = pyObjectUnicode(arg); 138 139 if(!pyUnicodeCheck(arg)) { 140 PyErr_SetString(PyExc_TypeError, &"Argument not a unicode string"[0]); 141 return null; 142 } 143 144 auto unicodeArg = pyUnicodeAsUtf8String(arg); 145 if(!unicodeArg) { 146 PyErr_SetString(PyExc_TypeError, &"Could not decode UTF8"[0]); 147 return null; 148 } 149 150 const ptr = pyBytesAsString(unicodeArg); 151 152 enum suffix = "_suffix"; 153 const ptrLen = strlen(ptr); 154 const retLength = strlen(ptr) + suffix.length; 155 auto ret = cast(char*) malloc(retLength); 156 scope(exit) free(cast(void*) ret); 157 158 ret[0 .. ptrLen] = ptr[0 .. ptrLen]; 159 ret[ptrLen .. retLength] = suffix[]; 160 161 return pyUnicodeDecodeUTF8(&ret[0], retLength, null /*errors*/); 162 } 163 164 165 // takes a list and returns its length + 1 166 package PyObject* one_list_param(PyObject* self, PyObject *args) nothrow { 167 if(PyTuple_Size(args) != 1) return null; 168 169 auto arg = PyTuple_GetItem(args, 0); 170 if(arg is null) return null; 171 172 if(!pyListCheck(arg)) return null; 173 // lists have iterators 174 if(PyObject_GetIter(arg) is null) return null; 175 176 return PyLong_FromLong(PyList_Size(arg) + 1); 177 } 178 179 180 // takes a list and returns its length + 1 181 package PyObject* one_list_param_to_list(PyObject* self, PyObject *args) nothrow { 182 if(PyTuple_Size(args) != 1) return null; 183 184 auto arg = PyTuple_GetItem(args, 0); 185 if(arg is null) return null; 186 187 if(!pyListCheck(arg)) return null; 188 189 auto ret = PyList_New(PyList_Size(arg)); 190 191 foreach(i; 0 .. PyList_Size(arg)) 192 PyList_SetItem(ret, i, PyList_GetItem(arg, i)); 193 194 PyList_Append(ret, PyLong_FromLong(4)); 195 PyList_Append(ret, PyLong_FromLong(5)); 196 197 return ret; 198 } 199 200 201 // takes a tuple and returns its length + 2 202 package PyObject* one_tuple_param(PyObject* self, PyObject *args) nothrow { 203 if(PyTuple_Size(args) != 1) return null; 204 205 auto arg = PyTuple_GetItem(args, 0); 206 if(arg is null) return null; 207 208 if(!pyTupleCheck(arg)) return null; 209 // tuples have iterators 210 if(PyObject_GetIter(arg) is null) return null; 211 212 return PyLong_FromLong(PyTuple_Size(arg) + 2); 213 } 214 215 216 // takes a range and returns its length + 3 217 package PyObject* one_range_param(PyObject* self, PyObject *args) nothrow { 218 if(PyTuple_Size(args) != 1) return null; 219 220 auto arg = PyTuple_GetItem(args, 0); 221 if(arg is null) return null; 222 223 auto iter = PyObject_GetIter(arg); 224 if(iter is null) return null; 225 226 int length; 227 while(PyIter_Next(iter)) ++length; 228 return PyLong_FromLong(length + 3); 229 } 230 231 232 // takes a range and returns its length + 3 233 package PyObject* one_dict_param(PyObject* self, PyObject *args) nothrow { 234 if(PyTuple_Size(args) != 1) return null; 235 236 auto arg = PyTuple_GetItem(args, 0); 237 if(arg is null) return null; 238 239 if(!pyDictCheck(arg)) return null; 240 241 return PyLong_FromLong(PyDict_Size(arg)); 242 } 243 244 245 // takes a dict and returns a copy with an extra `"oops": "noooo"` in it 246 package PyObject* one_dict_param_to_dict(PyObject* self, PyObject *args) nothrow { 247 if(PyTuple_Size(args) != 1) return null; 248 249 auto arg = PyTuple_GetItem(args, 0); 250 if(arg is null) return null; 251 252 if(!pyDictCheck(arg)) return null; 253 254 auto ret = PyDict_New; 255 256 auto keysIter = PyObject_GetIter(PyDict_Keys(arg)); 257 for(auto key = PyIter_Next(keysIter); 258 key; 259 key = PyIter_Next(keysIter)) 260 { 261 PyDict_SetItem(ret, key, PyDict_GetItem(arg, key)); 262 } 263 264 const oops = "oops"; 265 const no = "noooo"; 266 267 auto pyOops = pyUnicodeDecodeUTF8(&oops[0], oops.length, null /*errors*/); 268 auto pyNo = pyUnicodeDecodeUTF8(&no[0], no.length, null /*errors*/); 269 270 PyDict_SetItem(ret, pyOops, pyNo); 271 272 return ret; 273 } 274 275 276 // Adds a certain number of days to the passed in object 277 package PyObject* add_days_to_date(PyObject* self, PyObject *args) { 278 import std.datetime: Date, days; 279 280 if(PyTuple_Size(args) != 2) return null; 281 282 auto dateArg = PyTuple_GetItem(args, 0); 283 if(dateArg is null) return null; 284 285 auto deltaArg = PyTuple_GetItem(args, 1); 286 if(deltaArg is null) return null; 287 288 const year = pyDateTimeYear(dateArg); 289 const month = pyDateTimeMonth(dateArg); 290 const day = pyDateTimeDay(dateArg); 291 292 auto date = Date(year, month, day); 293 date += PyLong_AsLong(deltaArg).days; 294 295 return pyDateFromDate(date.year, date.month, date.day); 296 } 297 298 299 // Adds a certain number of days to the passed in object 300 package PyObject* add_days_to_datetime(PyObject* self, PyObject *args) { 301 import std.datetime: DateTime, days; 302 303 if(PyTuple_Size(args) != 2) return null; 304 305 auto dateArg = PyTuple_GetItem(args, 0); 306 if(dateArg is null) return null; 307 308 auto deltaArg = PyTuple_GetItem(args, 1); 309 if(deltaArg is null) return null; 310 311 const year = pyDateTimeYear(dateArg); 312 const month = pyDateTimeMonth(dateArg); 313 const day = pyDateTimeDay(dateArg); 314 const hour = pyDateTimeHour(dateArg); 315 const minute = pyDateTimeMinute(dateArg); 316 const second = pyDateTimeSecond(dateArg); 317 318 auto date = DateTime(year, month, day, hour, minute, second); 319 date += PyLong_AsLong(deltaArg).days; 320 321 return pyDateTimeFromDateAndTime(date.year, date.month, date.day, 322 date.hour, date.minute, date.second); 323 } 324 325 326 327 package PyObject* throws_runtime_error(PyObject* self, PyObject *args) @nogc nothrow { 328 enum str = "Generic runtime error"; 329 PyErr_SetString(PyExc_RuntimeError, str.ptr); 330 return null; 331 } 332 333 334 // Returns the length of kwargs, which is a dict 335 package PyObject* kwargs_count(PyObject* self, PyObject *args, PyObject* kwargs) @nogc nothrow { 336 if(PyTuple_Size(args) != 2) { 337 PyErr_BadArgument(); 338 return null; 339 } 340 341 return PyLong_FromLong(PyObject_Length(kwargs)); 342 }