1 /* 2 Copyright 2006, 2007 Kirk McDonald 3 4 Permission is hereby granted, free of charge, to any person obtaining a copy of 5 this software and associated documentation files (the "Software"), to deal in 6 the Software without restriction, including without limitation the rights to 7 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 of the Software, and to permit persons to whom the Software is furnished to do 9 so, subject to the following conditions: 10 11 The above copyright notice and this permission notice shall be included in all 12 copies or substantial portions of the Software. 13 14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 SOFTWARE. 21 */ 22 23 /++ 24 This module contains some useful type conversion functions. The two 25 most interesting operations here are python_to_d and d_to_python. 26 27 Additionally, the py function is provided as a convenience to directly 28 convert a D object into an instance of PydObject. 29 30 To convert a PydObject to a D type, use PydObject.to_d. 31 +/ 32 module pyd.make_object; 33 34 import deimos.python.Python; 35 36 import std.array; 37 import std.algorithm; 38 import std.complex; 39 import std.typetuple; 40 import std.bigint; 41 import std.datetime; 42 import std.traits; 43 import std.typecons; 44 import std.conv; 45 import std.range; 46 import std.stdio; 47 48 import pyd.references; 49 import pyd.pydobject; 50 import pyd.class_wrap; 51 import pyd.struct_wrap; 52 import pyd.func_wrap; 53 import pyd.def; 54 import pyd.exception; 55 56 57 shared static this() { 58 init_rangewrapper(); 59 } 60 61 void init_rangewrapper() { 62 version(PydPythonExtension) { 63 on_py_init({ 64 wrap_struct!(RangeWrapper, 65 Def!(RangeWrapper.iter, PyName!"__iter__"), 66 Def!(RangeWrapper.next))(); 67 rangeWrapperInited = true; 68 }, PyInitOrdering.After); 69 }else{ 70 on_py_init( { 71 add_module!(ModuleName!"pyd")(); 72 }); 73 on_py_init( { 74 wrap_struct!(RangeWrapper, 75 ModuleName!"pyd", 76 Def!(RangeWrapper.iter, PyName!"__iter__"), 77 Def!(RangeWrapper.next))(); 78 rangeWrapperInited = true; 79 }, PyInitOrdering.After); 80 } 81 } 82 83 bool rangeWrapperInited = false; 84 85 class to_conversion_wrapper(dg_t) { 86 alias ParameterTypeTuple!(dg_t)[0] T; 87 alias ReturnType!(dg_t) Intermediate; 88 dg_t dg; 89 this(dg_t fn) { dg = fn; } 90 PyObject* opCall(T t) { 91 static if (is(Intermediate == PyObject*)) { 92 return dg(t); 93 } else { 94 return d_to_python(dg(t)); 95 } 96 } 97 } 98 class from_conversion_wrapper(dg_t) { 99 alias ParameterTypeTuple!(dg_t)[0] Intermediate; 100 alias ReturnType!(dg_t) T; 101 dg_t dg; 102 this(dg_t fn) { dg = fn; } 103 T opCall(PyObject* o) { 104 static if (is(Intermediate == PyObject*)) { 105 return dg(o); 106 } else { 107 return dg(python_to_d!(Intermediate)(o)); 108 } 109 } 110 } 111 112 template to_converter_registry(From) { 113 PyObject* delegate(From) dg=null; 114 } 115 template from_converter_registry(To) { 116 To delegate(PyObject*) dg=null; 117 } 118 119 /** 120 Extend pyd's conversion mechanism. Will be used by d_to_python only if d_to_python cannot 121 convert its argument by regular means. 122 123 Params: 124 dg = A callable which takes a D type and returns a PyObject*, or any 125 type convertible by d_to_python. 126 */ 127 void ex_d_to_python(dg_t) (dg_t dg) { 128 static if (is(dg_t == delegate) && is(ReturnType!(dg_t) == PyObject*)) { 129 to_converter_registry!(ParameterTypeTuple!(dg_t)[0]).dg = dg; 130 } else { 131 auto o = new to_conversion_wrapper!(dg_t)(dg); 132 to_converter_registry!(typeof(o).T).dg = &o.opCall; 133 } 134 } 135 136 /** 137 Extend pyd's conversion mechanims. Will be used by python_to_d only if python_to_d 138 cannot convert its argument by regular means. 139 140 Params: 141 dg = A callable which takes a PyObject*, or any type convertible by python_to_d, 142 and returns a D type. 143 */ 144 void ex_python_to_d(dg_t) (dg_t dg) { 145 static if (is(dg_t == delegate) && is(ParameterTypeTuple!(dg_t)[0] == PyObject*)) { 146 from_converter_registry!(ReturnType!(dg_t)).dg = dg; 147 } else { 148 auto o = new from_conversion_wrapper!(dg_t)(dg); 149 from_converter_registry!(typeof(o).T).dg = &o.opCall; 150 } 151 } 152 153 /** 154 * Returns a new (owned) reference to a Python object based on the passed 155 * argument. If the passed argument is a PyObject*, this "steals" the 156 * reference. (In other words, it returns the PyObject* without changing its 157 * reference count.) If the passed argument is a PydObject, this returns a new 158 * reference to whatever the PydObject holds a reference to. 159 * 160 * If the passed argument can't be converted to a PyObject, a Python 161 * RuntimeError will be raised and this function will return null. 162 */ 163 PyObject* d_to_python(T) (T t) { 164 static if (!is(T == PyObject*) && is(typeof(t is null)) && 165 !isAssociativeArray!T && !isArray!T) { 166 if (t is null) { 167 return Py_INCREF(Py_None()); 168 } 169 } 170 static if (isBoolean!T) { 171 return Py_INCREF(t ? Py_True : Py_False); 172 } else static if(isIntegral!T) { 173 static if(isUnsigned!T) { 174 return PyLong_FromUnsignedLongLong(t); 175 }else static if(isSigned!T) { 176 return PyLong_FromLongLong(t); 177 } 178 } else static if (isFloatingPoint!T) { 179 return PyFloat_FromDouble(t); 180 } else static if( isTuple!T) { 181 return d_tuple_to_python!T(t); 182 } else static if (is(T == DateTime)) { 183 if(PyDateTimeAPI is null) { 184 PyDateTime_IMPORT(); 185 } 186 return PyDateTime_FromDateAndTime(t.year, t.month, t.day, t.hour, t.minute, t.second, 0); 187 } else static if (is(T == Date)) { 188 if(PyDateTimeAPI is null) { 189 PyDateTime_IMPORT(); 190 } 191 return PyDate_FromDate(t.year, t.month, t.day); 192 } else static if (is(T == SysTime)) { 193 if(PyDateTimeAPI is null) { 194 PyDateTime_IMPORT(); 195 } 196 return PyDateTime_FromDateAndTime(t.year, t.month, t.day, t.hour, t.minute, t.second, 0); 197 } else static if (is(T == TimeOfDay)) { 198 if(PyDateTimeAPI is null) { 199 PyDateTime_IMPORT(); 200 } 201 return PyTime_FromTime(t.hour, t.minute, t.second, 0); 202 } else static if (is(Unqual!T _unused : Complex!F, F)) { 203 return PyComplex_FromDoubles(t.re, t.im); 204 } else static if(is(T == std.bigint.BigInt)) { 205 return d_bigint_to_python(t); 206 } else static if(is(Unqual!T _unused : PydInputRange!E, E)) { 207 return Py_INCREF(t.ptr); 208 } else static if(isSomeString!T) { 209 return d_string_to_python(t); 210 } else static if (isArray!(T)) { 211 return d_array_to_python(t); 212 } else static if (isAssociativeArray!(T)) { 213 return d_aarray_to_python(t); 214 } else static if (isDelegate!T || isFunctionPointer!T) { 215 PydWrappedFunc_Ready!(T)(); 216 return wrap_d_object(t); 217 } else static if (is(T : PydObject)) { 218 return Py_INCREF(t.ptr()); 219 } else static if (is(T : PyObject*)) { 220 // The function expects to be passed a borrowed reference and return an 221 // owned reference. Thus, if passed a PyObject*, this will increment the 222 // reference count. 223 Py_XINCREF(t); 224 return t; 225 } else static if (is(T == class)) { 226 // Convert wrapped type to a PyObject* 227 alias Unqual!T Tu; 228 // But only if it actually is a wrapped type. :-) 229 PyTypeObject** type = Tu.classinfo in wrapped_classes; 230 if (type) { 231 return wrap_d_object(t, *type); 232 } 233 return d_to_python_try_extends(t); 234 // If it's not a wrapped type, fall through to the exception. 235 // If converting a struct by value, create a copy and wrap that 236 } else static if (is(T == struct) && 237 !is(T == RangeWrapper) && 238 isInputRange!T) { 239 if (to_converter_registry!(T).dg) { 240 return d_to_python_try_extends(t); 241 }else static if(__traits(compiles, wrap_range(t))) { 242 assert(is_wrapped!(RangeWrapper*)); 243 return d_to_python(wrap_range(t)); 244 } 245 } else static if (is(T == struct)) { 246 alias Unqual!T Tu; 247 if (is_wrapped!(Tu*)) { 248 Tu* temp = new Tu; 249 *temp = cast(Tu) t; 250 return wrap_d_object(cast(T*)temp); 251 } 252 return d_to_python_try_extends(t); 253 // If converting a struct by reference, wrap the thing directly 254 } else static if (is(typeof(*t) == struct)) { 255 alias Unqual!T Tu; 256 if (is_wrapped!(Tu)) { 257 if (t is null) { 258 return Py_INCREF(Py_None()); 259 } 260 return wrap_d_object(t); 261 } 262 return d_to_python_try_extends(t); 263 } 264 265 assert(0); 266 } 267 268 PyObject* d_to_python_try_extends(T) (T t) { 269 if (to_converter_registry!(T).dg) { 270 return to_converter_registry!(T).dg(t); 271 } 272 PyErr_SetString(PyExc_RuntimeError, ("D conversion function d_to_python failed with type " ~ typeid(T).toString()).ptr); 273 return null; 274 } 275 276 PyObject* d_tuple_to_python(T) (T t) if (isTuple!T) { 277 T.Types tuple; 278 foreach(i, _t; T.Types) { 279 tuple[i] = t[i]; 280 } 281 return PyTuple_FromItems(tuple); 282 } 283 284 PyObject* d_bigint_to_python(BigInt t) { 285 import std..string: format; 286 string num_str = format("%s\0",t); 287 return PyLong_FromString(num_str.dup.ptr, null, 10); 288 } 289 290 PyObject* d_string_to_python(T)(T t) if(isSomeString!T) { 291 alias Unqual!(typeof(T.init[0])) C; 292 static if(is(C == char)) { 293 return PyUnicode_DecodeUTF8(t.ptr, cast(Py_ssize_t) t.length, null); 294 }else static if(is(C == wchar)) { 295 return PyUnicode_DecodeUTF16(cast(char*) t.ptr, 296 cast(Py_ssize_t)(2*t.length), null, null); 297 }else static if(is(C == dchar)) { 298 version(Python_2_6_Or_Later) { 299 return PyUnicode_DecodeUTF32(cast(char*) t.ptr, 300 cast(Py_ssize_t)(4*t.length), null, null); 301 }else{ 302 return d_to_python(to!string(t)); 303 } 304 }else static assert(false, "waht is this T? " ~ T.stringof); 305 } 306 307 PyObject* d_array_to_python(T)(T t) if(isArray!T) { 308 // Converts any array (static or dynamic) to a Python list 309 PyObject* lst = PyList_New(cast(Py_ssize_t) t.length); 310 PyObject* temp; 311 if (lst is null) return null; 312 for(int i=0; i<t.length; ++i) { 313 temp = d_to_python(t[i]); 314 if (temp is null) { 315 Py_DECREF(lst); 316 return null; 317 } 318 // Steals the reference to temp 319 PyList_SET_ITEM(lst, cast(Py_ssize_t) i, temp); 320 } 321 return lst; 322 } 323 324 PyObject* d_aarray_to_python(T)(T t) if(isAssociativeArray!T) { 325 // Converts any associative array to a Python dict 326 PyObject* dict = PyDict_New(); 327 PyObject* ktemp, vtemp; 328 int result; 329 if (dict is null) return null; 330 foreach(k, v; t) { 331 ktemp = d_to_python(k); 332 vtemp = d_to_python(v); 333 if (ktemp is null || vtemp is null) { 334 if (ktemp !is null) Py_DECREF(ktemp); 335 if (vtemp !is null) Py_DECREF(vtemp); 336 Py_DECREF(dict); 337 return null; 338 } 339 result = PyDict_SetItem(dict, ktemp, vtemp); 340 Py_DECREF(ktemp); 341 Py_DECREF(vtemp); 342 if (result == -1) { 343 Py_DECREF(dict); 344 return null; 345 } 346 } 347 return dict; 348 } 349 350 T python_to_aarray(T)(PyObject* py) if(isAssociativeArray!T) { 351 PyObject* keys = null; 352 if(PyDict_Check(py)) { 353 keys = PyDict_Keys(py); 354 }else if(PyMapping_Keys(py)) { 355 keys = PyMapping_Keys(py); 356 }else{ 357 could_not_convert!(T)(py); 358 assert(0); 359 } 360 PyObject* iterator = PyObject_GetIter(keys); 361 T result; 362 PyObject* key; 363 while ((key=PyIter_Next(iterator)) !is null) { 364 PyObject* value = PyObject_GetItem(py, key); 365 auto d_key = python_to_d!(KeyType!T)(key); 366 auto d_value = python_to_d!(ValueType!T)(value); 367 result[d_key] = d_value; 368 Py_DECREF(key); 369 Py_DECREF(value); 370 } 371 Py_DECREF(iterator); 372 return result; 373 } 374 375 /** 376 * Helper function for creating a PyTuple from a series of D items. 377 */ 378 PyObject* PyTuple_FromItems(T ...)(T t) { 379 PyObject* tuple = PyTuple_New(t.length); 380 PyObject* temp; 381 if (tuple is null) return null; 382 foreach(i, arg; t) { 383 temp = d_to_python(arg); 384 if (temp is null) { 385 Py_DECREF(tuple); 386 return null; 387 } 388 PyTuple_SetItem(tuple, i, temp); 389 } 390 return tuple; 391 } 392 393 /** 394 * Constructs an object based on the type of the argument passed in. 395 * 396 * For example, calling py(10) would return a PydObject holding the value 10. 397 * 398 * Calling this with a PydObject will return back a reference to the very same 399 * PydObject. 400 */ 401 PydObject py(T) (T t) { 402 static if(is(T : PydObject)) { 403 return t; 404 } else { 405 return new PydObject(d_to_python(t)); 406 } 407 } 408 409 /** 410 * An exception class used by python_to_d. 411 */ 412 class PydConversionException : Exception { 413 this(string msg, string file = __FILE__, size_t line = __LINE__) { 414 super(msg, file, line); 415 } 416 } 417 418 /** 419 * This converts a PyObject* to a D type. The template argument is the type to 420 * convert to. The function argument is the PyObject* to convert. For instance: 421 * 422 *$(D_CODE PyObject* i = PyInt_FromLong(20); 423 *int n = _python_to_d!(int)(i); 424 *assert(n == 20);) 425 * 426 * This throws a PydConversionException if the PyObject can't be converted to 427 * the given D type. 428 */ 429 T python_to_d(T) (PyObject* o) { 430 import std..string: format; 431 432 // This ordering is somewhat important. The checks for Tuple and Complex 433 // must be before the check for general structs. 434 version(PydPythonExtension) { 435 // druntime doesn't run module ctors :( 436 if(!rangeWrapperInited) { 437 init_rangewrapper(); 438 } 439 } 440 441 static if (is(PyObject* : T)) { 442 return o; 443 } else static if (is(PydObject : T)) { 444 return new PydObject(borrowed(o)); 445 } else static if (is(T == void)) { 446 if (o != cast(PyObject*) Py_None()) could_not_convert!(T)(o); 447 return; 448 } else static if (isTuple!T) { 449 if(PyTuple_Check(o)) { 450 return python_to_d_tuple!T(o); 451 } 452 return python_to_d_try_extends!T(o); 453 } else static if (is(Unqual!T _unused : Complex!F, F)) { 454 if (PyComplex_Check(o) || isNumpyComplexNumber(o)) { 455 return python_to_d_complex!T(o); 456 } 457 return python_to_d_try_extends!T(o); 458 } else static if(is(Unqual!T == std.bigint.BigInt)) { 459 if (isPyNumber(o)) { 460 return python_to_d_bigint!T(o); 461 } 462 if (isNumpyNumber(o)) { 463 auto i = to_python_int(o); 464 return python_to_d_bigint!T(i); 465 } 466 return python_to_d_try_extends!T(o); 467 } else static if(is(T == DateTime)) { 468 if(PyDateTimeAPI is null) { 469 PyDateTime_IMPORT(); 470 } 471 if(PyDateTime_Check(o)) { 472 int year = PyDateTime_GET_YEAR(o); 473 int month = PyDateTime_GET_MONTH(o); 474 int day = PyDateTime_GET_DAY(o); 475 int hour = PyDateTime_DATE_GET_HOUR(o); 476 int minute = PyDateTime_DATE_GET_MINUTE(o); 477 int second = PyDateTime_DATE_GET_SECOND(o); 478 return DateTime(year, month, day, hour, minute, second); 479 } 480 if(PyDate_Check(o)) { 481 int year = PyDateTime_GET_YEAR(o); 482 int month = PyDateTime_GET_MONTH(o); 483 int day = PyDateTime_GET_DAY(o); 484 return DateTime(year, month, day, 0, 0, 0); 485 } 486 if(is_numpy_datetime64(o)) { 487 return python_to_d_numpy_datetime64!T(o); 488 } 489 490 } else static if(is(T == Date)) { 491 if(PyDateTimeAPI is null) { 492 PyDateTime_IMPORT(); 493 } 494 if(PyDateTime_Check(o) || PyDate_Check(o)) { 495 int year = PyDateTime_GET_YEAR(o); 496 int month = PyDateTime_GET_MONTH(o); 497 int day = PyDateTime_GET_DAY(o); 498 return Date(year, month, day); 499 } 500 if(is_numpy_datetime64(o)) { 501 return python_to_d_numpy_datetime64!T(o); 502 } 503 } else static if(is(T == SysTime)) { 504 if(PyDateTimeAPI is null) { 505 PyDateTime_IMPORT(); 506 } 507 if(PyDateTime_Check(o)) { 508 int year = PyDateTime_GET_YEAR(o); 509 int month = PyDateTime_GET_MONTH(o); 510 int day = PyDateTime_GET_DAY(o); 511 int hour = PyDateTime_DATE_GET_HOUR(o); 512 int minute = PyDateTime_DATE_GET_MINUTE(o); 513 int second = PyDateTime_DATE_GET_SECOND(o); 514 auto dt = DateTime(year, month, day, hour, minute, second); 515 return SysTime(dt); 516 } 517 if(PyDate_Check(o)) { 518 int year = PyDateTime_GET_YEAR(o); 519 int month = PyDateTime_GET_MONTH(o); 520 int day = PyDateTime_GET_DAY(o); 521 auto dt = DateTime(year, month, day, 0, 0, 0); 522 return SysTime(dt); 523 } 524 if(is_numpy_datetime64(o)) { 525 return python_to_d_numpy_datetime64!T(o); 526 } 527 } else static if(is(T == TimeOfDay)) { 528 if(PyDateTimeAPI is null) { 529 PyDateTime_IMPORT(); 530 } 531 if(PyTime_Check(o)) { 532 int hour = PyDateTime_TIME_GET_HOUR(o); 533 int minute = PyDateTime_TIME_GET_MINUTE(o); 534 int second = PyDateTime_TIME_GET_SECOND(o); 535 return TimeOfDay(hour, minute, second); 536 } 537 if(PyDateTime_Check(o)) { 538 int hour = PyDateTime_DATE_GET_HOUR(o); 539 int minute = PyDateTime_DATE_GET_MINUTE(o); 540 int second = PyDateTime_DATE_GET_SECOND(o); 541 return TimeOfDay(hour, minute, second); 542 } 543 if(is_numpy_datetime64(o)) { 544 return python_to_d_numpy_datetime64!T(o); 545 } 546 } else static if(is(Unqual!T _unused : PydInputRange!E, E)) { 547 return cast(T) PydInputRange!E(borrowed(o)); 548 } else static if (is(T == class)) { 549 // We can only convert to a class if it has been wrapped, and of course 550 // we can only convert the object if it is the wrapped type. 551 if ( 552 is_wrapped!(T) && 553 PyObject_IsInstance(o, cast(PyObject*)&PydTypeObject!(T)) ) 554 { 555 if ( get_d_reference!T(o) !is null) { 556 return get_d_reference!(T)(o); 557 } 558 } 559 return python_to_d_try_extends!T(o); 560 } else static if (is(T == struct)) { // struct by value 561 // struct is wrapped 562 if (is_wrapped!(T*) && PyObject_TypeCheck(o, &PydTypeObject!(T*))) { 563 return *get_d_reference!(T*)(o); 564 } 565 // or struct is wrapped range 566 if(PyObject_IsInstance(o, 567 cast(PyObject*)&PydTypeObject!(RangeWrapper*))) { 568 RangeWrapper* wrapper = get_d_reference!(RangeWrapper*)(o); 569 if(typeid(T) != wrapper.tid) { 570 could_not_convert!T(o, format("typeid mismatch: %s vs %s", 571 wrapper.tid, typeid(T))); 572 } 573 T t = *cast(T*) wrapper.range; 574 return t; 575 } 576 return python_to_d_try_extends!T(o); 577 } else static if (isPointer!T && is(PointerTarget!T == struct)) { 578 // pointer to struct 579 if (is_wrapped!(T) && PyObject_TypeCheck(o, &PydTypeObject!(T))) { 580 return get_d_reference!(T)(o); 581 } 582 return python_to_d_try_extends!T(o); 583 } else static if (is(T == delegate)) { 584 // Get the original wrapped delegate out if this is a wrapped delegate 585 if (is_wrapped!(T) && PyObject_TypeCheck(o, &PydTypeObject!(T))) { 586 return get_d_reference!(T)(o); 587 // Otherwise, wrap the PyCallable with a delegate 588 } else if (PyCallable_Check(o)) { 589 return PydCallable_AsDelegate!(T)(o); 590 } 591 return python_to_d_try_extends!T(o); 592 } else static if (isDelegate!T || isFunctionPointer!T) { 593 // We can only make it a function pointer if we originally wrapped a 594 // function pointer. 595 if (is_wrapped!(T) && PyObject_TypeCheck(o, &PydTypeObject!(T))) { 596 return get_d_reference!(T)(o); 597 } 598 return python_to_d_try_extends!T(o); 599 } else static if (isSomeString!T) { 600 return python_to_d_string!T(o); 601 } else static if (isArray!T || IsStaticArrayPointer!T) { 602 static if(isPointer!T) { 603 alias Unqual!(ElementType!(PointerTarget!T)) E; 604 }else { 605 alias Unqual!(ElementType!T) E; 606 } 607 version(Python_2_6_Or_Later) { 608 if(PyObject_CheckBuffer(o)) { 609 return python_buffer_to_d!(T)(o); 610 } 611 } 612 if(o.ob_type is array_array_Type) { 613 return python_array_array_to_d!T(o); 614 }else { 615 return python_iter_to_d!T(o); 616 } 617 } else static if (isAssociativeArray!T) { 618 return python_to_aarray!T(o); 619 } else static if (isFloatingPoint!T) { 620 if (isPyNumber(o) || isNumpyNumber(o)) { 621 double res = PyFloat_AsDouble(o); 622 return cast(T) res; 623 } 624 return python_to_d_try_extends!T(o); 625 } else static if(isIntegral!T) { 626 if (isNumpyNumber(o)) { 627 o = to_python_int(o); 628 } 629 630 version(Python_3_0_Or_Later) { 631 }else{ 632 if(PyInt_Check(o)) { 633 C_long res = PyInt_AsLong(o); 634 handle_exception(); 635 static if(isUnsigned!T) { 636 if(res < 0) could_not_convert!T(o, format("%s out of bounds [%s, %s]", res, 0, T.max)); 637 if(T.max < res) could_not_convert!T(o,format("%s out of bounds [%s, %s]", res, 0, T.max)); 638 return cast(T) res; 639 }else static if(isSigned!T) { 640 if(T.min > res) could_not_convert!T(o, format("%s out of bounds [%s, %s]", res, T.min, T.max)); 641 if(T.max < res) could_not_convert!T(o, format("%s out of bounds [%s, %s]", res, T.min, T.max)); 642 return cast(T) res; 643 } 644 } 645 } 646 if(PyLong_Check(o)) { 647 static if(isUnsigned!T) { 648 static assert(T.sizeof <= C_ulonglong.sizeof); 649 C_ulonglong res = PyLong_AsUnsignedLongLong(o); 650 handle_exception(); 651 // no overflow from python to C_ulonglong, 652 // overflow from C_ulonglong to T? 653 if(T.max < res) could_not_convert!T(o); 654 return cast(T) res; 655 }else static if(isSigned!T) { 656 static assert(T.sizeof <= C_longlong.sizeof); 657 C_longlong res = PyLong_AsLongLong(o); 658 handle_exception(); 659 // no overflow from python to C_longlong, 660 // overflow from C_longlong to T? 661 if(T.min > res) could_not_convert!T(o); 662 if(T.max < res) could_not_convert!T(o); 663 return cast(T) res; 664 } 665 } 666 667 return python_to_d_try_extends!T(o); 668 } else static if (isBoolean!T) { 669 if (isPyNumber(o) || isNumpyNumber(o)) { 670 int res = PyObject_IsTrue(o); 671 return res == 1; 672 } 673 return python_to_d_try_extends!T(o); 674 } 675 676 assert(0); 677 } 678 679 PyObject* to_python_int(PyObject* o) { 680 auto builtins = new PydObject(PyEval_GetBuiltins()); 681 auto int_ = builtins["int"]; 682 return int_(o).to_d!(PyObject*)(); 683 } 684 685 T python_to_d_try_extends(T) (PyObject* o) { 686 if (from_converter_registry!(T).dg) { 687 return from_converter_registry!(T).dg(o); 688 } 689 could_not_convert!(T)(o); 690 assert(0); 691 } 692 693 T python_to_d_tuple(T) (PyObject* o) { 694 T.Types tuple; 695 auto len = PyTuple_Size(o); 696 if(len != T.Types.length) could_not_convert!T(o); 697 foreach(i,_t; T.Types) { 698 auto obj = Py_XINCREF(PyTuple_GetItem(o, i)); 699 tuple[i] = python_to_d!_t(obj); 700 Py_DECREF(obj); 701 } 702 return T(tuple); 703 } 704 705 T python_to_d_complex(T) (PyObject* o) { 706 import util.conv; 707 static if (is(Unqual!T _unused : Complex!F, F)) { 708 double real_ = python_to_d!double(PyObject_GetAttrString(o, "real")); 709 handle_exception(); 710 double imag = python_to_d!double(PyObject_GetAttrString(o, "imag")); 711 handle_exception(); 712 return complex!(F,F)(real_, imag); 713 }else static assert(false); 714 } 715 716 T python_to_d_bigint(T) (PyObject* o) { 717 string num_str = python_to_d!string(o); 718 if(num_str.endsWith("L")) num_str = num_str[0..$-1]; 719 return BigInt(num_str); 720 } 721 722 T python_to_d_string(T) (PyObject* o) { 723 alias Unqual!(typeof(T.init[0])) C; 724 PyObject* str; 725 if(PyBytes_Check(o)) { 726 static if(is(C == char)) { 727 str = o; 728 }else{ 729 version(Python_3_0_Or_Later) { 730 str = PyObject_Str(o); 731 if(!str) handle_exception(); 732 }else{ 733 str = PyObject_Unicode(o); 734 if(!str) handle_exception(); 735 } 736 } 737 }else if(PyUnicode_Check(o)) { 738 str = o; 739 }else { 740 str = PyObject_Repr(o); 741 if(!str) handle_exception(); 742 version(Python_3_0_Or_Later) { 743 }else{ 744 static if(!is(C == char)) { 745 str = PyObject_Unicode(str); 746 if(!str) handle_exception(); 747 } 748 } 749 } 750 static if(is(C == char)) { 751 if(PyBytes_Check(str)) { 752 const(char)* res = PyBytes_AsString(str); 753 if(!res) handle_exception(); 754 return to!T(res); 755 } 756 } 757 758 if(PyUnicode_Check(str)) { 759 static if(is(C == char)) { 760 PyObject* utf8 = PyUnicode_AsUTF8String(str); 761 if(!utf8) handle_exception(); 762 const(char)* res = PyBytes_AsString(utf8); 763 if(!res) handle_exception(); 764 return to!T(res); 765 }else static if(is(C == wchar)) { 766 PyObject* utf16 = PyUnicode_AsUTF16String(str); 767 if(!utf16) handle_exception(); 768 // PyUnicode_AsUTF16String puts a BOM character in front of 769 // string 770 auto ptr = cast(const(wchar)*)(PyBytes_AsString(utf16)+2); 771 Py_ssize_t len = PyBytes_Size(utf16)/2-1; 772 wchar[] ws = new wchar[](len); 773 ws[] = ptr[0 .. len]; 774 return cast(T) ws; 775 }else static if(is(C == dchar)) { 776 version(Python_2_6_Or_Later) { 777 PyObject* utf32 = PyUnicode_AsUTF32String(str); 778 if(!utf32) handle_exception(); 779 // PyUnicode_AsUTF32String puts a BOM character in front of 780 // string 781 auto ptr = cast(const(dchar)*)(PyBytes_AsString(utf32)+4); 782 Py_ssize_t len = PyBytes_Size(utf32)/4-1; 783 dchar[] ds = new dchar[](len); 784 ds[] = ptr[0 .. len]; 785 return cast(T) ds; 786 }else{ 787 return to!(T)(python_to_d!string(str)); 788 } 789 }else static assert(false, "what T is this!? " ~ T.stringof); 790 } 791 assert(0); 792 } 793 794 795 // (*^&* array doesn't implement the buffer interface, but we still 796 // want it to copy fast. 797 /// Convert an array.array object to a D object. 798 /// 799 /// Used by python_to_d. 800 T python_array_array_to_d(T)(PyObject* o) 801 if(isArray!T || IsStaticArrayPointer!T) { 802 import std.exception: enforce; 803 import std..string: format; 804 import core.stdc..string : memcpy; 805 806 static if(isPointer!T) 807 alias Unqual!(ElementType!(PointerTarget!T)) E; 808 else 809 alias Unqual!(ElementType!T) E; 810 if(o.ob_type !is array_array_Type) 811 could_not_convert!T(o, "not an array.array"); 812 arrayobject* arr_o = cast(arrayobject*) o; 813 // array.array's data can be got with a single memcopy. 814 enforce(arr_o.ob_descr, "array.ob_descr null!"); 815 char typecode = cast(char) arr_o.ob_descr.typecode; 816 if(!match_format_type!E(""~typecode)) { 817 could_not_convert!T(o, format("item mismatch: '%s' vs %s", 818 typecode, E.stringof)); 819 } 820 821 int itemsize = arr_o.ob_descr.itemsize; 822 if(itemsize != E.sizeof) 823 could_not_convert!T(o, 824 format("item size mismatch: %s vs %s", 825 itemsize, E.sizeof)); 826 Py_ssize_t count = Py_SIZE(arr_o); 827 if(count < 0) 828 could_not_convert!T(o, format("nonsensical array length: %s", 829 count)); 830 MatrixInfo!T.unqual _array; 831 static if(isDynamicArray!T) { 832 _array = new MatrixInfo!T.unqual(count); 833 }else { 834 if(!MatrixInfo!T.check([count])) 835 could_not_convert!T(o, 836 format("length mismatch: %s vs %s", count, T.length)); 837 } 838 // copy data, don't take slice 839 static if(isPointer!(typeof(_array))) { 840 memcpy((*_array).ptr, arr_o.ob_item, count*itemsize); 841 }else{ 842 memcpy(_array.ptr, arr_o.ob_item, count*itemsize); 843 } 844 //_array[] = cast(E[]) arr_o.ob_item[0 .. count*itemsize]; 845 return cast(T) _array; 846 } 847 848 /** 849 Convert a d array to a python array.array. 850 array.array does not support 8 byte integers. 851 852 Not used by d_to_python. 853 */ 854 PyObject* d_to_python_array_array(T)(T t) 855 if((isArray!T || IsStaticArrayPointer!T) && 856 MatrixInfo!T.ndim == 1 && 857 SimpleFormatType!(MatrixInfo!T.MatrixElementType).supported) { 858 import core.stdc..string : memcpy; 859 860 alias MatrixInfo!T.MatrixElementType ME; 861 PyObject* pyformat = SimpleFormatType!ME.pyType(); 862 PyObject* args = PyTuple_New(1); 863 PyTuple_SetItem(args, 0, pyformat); 864 scope(exit) Py_DECREF(args); 865 PyObject* obj = array_array_Type.tp_new(array_array_Type, args, null); 866 if(!obj) handle_exception(); 867 arrayobject* arr_o = cast(arrayobject*) obj; 868 Py_ssize_t[] shape = MatrixInfo!T.build_shape(t); 869 size_t datalen = ME.sizeof*shape[0]; 870 Py_SET_SIZE(arr_o, shape[0]); 871 void* data = PyMem_Malloc(datalen); 872 static if(isPointer!T) { 873 memcpy(data, t, datalen); 874 }else { 875 memcpy(data, t.ptr, datalen); 876 } 877 arr_o.ob_item = cast(ubyte*) data; 878 return obj; 879 } 880 881 /** 882 Convert a D object to python bytes (str, in python 2). 883 */ 884 PyObject* d_to_python_bytes(T)(T t) if(is(T == string)) { 885 return PyBytes_FromStringAndSize(t.ptr, cast(Py_ssize_t) t.length); 886 } 887 888 /** Convert an iterable Python object to a D object. 889 * 890 * Used by python_to_d. 891 */ 892 T python_iter_to_d(T)(PyObject* o) if(isArray!T || IsStaticArrayPointer!T) { 893 import std..string: format; 894 895 static if(isPointer!T) 896 alias Unqual!(ElementType!(PointerTarget!T)) E; 897 else 898 alias Unqual!(ElementType!T) E; 899 PyObject* iter = PyObject_GetIter(o); 900 if (iter is null) { 901 PyErr_Clear(); 902 could_not_convert!(T)(o); 903 } 904 scope(exit) Py_DECREF(iter); 905 Py_ssize_t len = PyObject_Length(o); 906 if (len == -1) { 907 PyErr_Clear(); 908 could_not_convert!(T)(o); 909 } 910 911 MatrixInfo!T.unqual _array; 912 static if(isDynamicArray!T) { 913 _array = new MatrixInfo!T.unqual(len); 914 }else static if(isStaticArray!T){ 915 if(len != T.length) 916 could_not_convert!T(o, 917 format("length mismatch: %s vs %s", 918 len, T.length)); 919 }else static if(isPointer!T){ 920 ubyte[] bufi = new ubyte[](PointerTarget!T.sizeof); 921 _array = cast(MatrixInfo!T.unqual)(bufi.ptr); 922 } 923 int i = 0; 924 PyObject* item = PyIter_Next(iter); 925 while (item) { 926 try { 927 _array[i] = python_to_d!(E)(item); 928 } catch(PydConversionException e) { 929 Py_DECREF(item); 930 // We re-throw the original conversion exception, rather than 931 // complaining about being unable to convert to an array. The 932 // partially constructed array is left to the GC. 933 throw e; 934 } 935 ++i; 936 Py_DECREF(item); 937 item = PyIter_Next(iter); 938 } 939 return cast(T) _array; 940 } 941 942 bool isPyNumber(PyObject* obj) { 943 version(Python_3_0_Or_Later) { 944 return 945 PyLong_Check(obj) || 946 PyFloat_Check(obj); 947 }else{ 948 return 949 PyInt_Check(obj) || 950 PyLong_Check(obj) || 951 PyFloat_Check(obj); 952 } 953 } 954 955 const(char)[] type_name(PyObject* obj) { 956 import core.stdc..string : strlen; 957 958 auto type = cast(PyTypeObject*)PyObject_Type(obj); 959 return type.tp_name[0 .. strlen(type.tp_name)]; 960 } 961 962 bool isNumpyBool(PyObject* obj) { 963 switch(type_name(obj)) { 964 case "numpy.bool_": 965 return true; 966 default: 967 return false; 968 } 969 } 970 971 bool isNumpyInteger(PyObject* obj) { 972 switch(type_name(obj)) { 973 case "numpy.int8": 974 case "numpy.int16": 975 case "numpy.int32": 976 case "numpy.int64": 977 case "numpy.uint8": 978 case "numpy.uint16": 979 case "numpy.uint32": 980 case "numpy.uint64": 981 return true; 982 default: 983 return false; 984 } 985 } 986 987 bool isNumpyFloat(PyObject* obj) { 988 switch(type_name(obj)) { 989 case "numpy.float32": 990 case "numpy.float64": 991 return true; 992 default: 993 return false; 994 } 995 } 996 997 bool isNumpyComplexNumber(PyObject* obj) { 998 switch(type_name(obj)) { 999 case "numpy.complex32": 1000 case "numpy.complex64": 1001 return true; 1002 default: 1003 return false; 1004 } 1005 } 1006 1007 bool isNumpyNumber(PyObject* obj) { 1008 return isNumpyBool(obj) || isNumpyInteger(obj) || isNumpyFloat(obj); 1009 } 1010 1011 version(Python_2_6_Or_Later) { 1012 /// Convert a Python new-style buffer to a D object. 1013 /// 1014 /// Used by python_to_d. 1015 T python_buffer_to_d(T)(PyObject* o) 1016 if (isArray!T || IsStaticArrayPointer!T) { 1017 import std..string: format; 1018 import core.stdc..string : memcpy; 1019 1020 PydObject bob = new PydObject(borrowed(o)); 1021 auto buf = bob.buffer_view(); 1022 alias MatrixInfo!T.MatrixElementType ME; 1023 MatrixInfo!T.unqual _array; 1024 /+ 1025 if(buf.itemsize != ME.sizeof) 1026 could_not_convert!T(o, format("item size mismatch: %s vs %s", 1027 buf.itemsize, ME.sizeof)); 1028 +/ 1029 if(!match_format_type!ME(buf.format)) { 1030 could_not_convert!T(o, format("item type mismatch: '%s' vs %s", 1031 buf.format, ME.stringof)); 1032 } 1033 if(buf.has_nd) { 1034 if(!MatrixInfo!T.check(buf.shape)) 1035 could_not_convert!T(o, 1036 format("dimension mismatch: %s vs %s", 1037 buf.shape, MatrixInfo!T.dimstring)); 1038 if(buf.c_contiguous) { 1039 // woohoo! single memcpy 1040 static if(MatrixInfo!T.isRectArray && isStaticArray!T) { 1041 memcpy(_array.ptr, buf.buf.ptr, buf.buf.length); 1042 }else{ 1043 alias MatrixInfo!T.RectArrayType RectArrayType; 1044 static if(!isStaticArray!(RectArrayType)) { 1045 ubyte[] dbuf = new ubyte[](buf.buf.length); 1046 memcpy(dbuf.ptr, buf.buf.ptr, buf.buf.length); 1047 } 1048 size_t rectsize = ME.sizeof; 1049 size_t MErectsize = 1; 1050 foreach(i; MatrixInfo!T.rectArrayAt .. MatrixInfo!T.ndim) { 1051 rectsize *= buf.shape[i]; 1052 MErectsize *= buf.shape[i]; 1053 } 1054 static if(MatrixInfo!T.isRectArray) { 1055 static if(isPointer!T) 1056 _array = cast(typeof(_array)) dbuf.ptr; 1057 else { 1058 static assert(isDynamicArray!T); 1059 _array = cast(typeof(_array)) dbuf; 1060 } 1061 }else{ 1062 // rubbish. much pointer pointing 1063 size_t offset = 0; 1064 static if(isDynamicArray!T) { 1065 _array = new MatrixInfo!T.unqual(buf.shape[0]); 1066 } 1067 enum string xx = (MatrixInfo!T.matrixIter( 1068 "_array", "buf.shape", "_indices", 1069 MatrixInfo!T.rectArrayAt, q{ 1070 static if(isDynamicArray!(typeof($array_ixn))) { 1071 $array_ixn = new typeof($array_ixn)(buf.shape[$i+1]); 1072 } 1073 static if(is(typeof($array_ixn) == RectArrayType)) { 1074 // should be innermost loop 1075 assert(offset + rectsize <= buf.buf.length, 1076 "uh oh: overflow!"); 1077 alias typeof($array_ixn) rectarr; 1078 static if(isStaticArray!rectarr) { 1079 memcpy($array_ixn.ptr, buf.buf.ptr + offset, rectsize); 1080 }else{ 1081 static assert(isDynamicArray!rectarr); 1082 1083 $array_ixn = (cast(typeof($array_ixn.ptr))(dbuf.ptr + offset)) 1084 [0 .. MErectsize]; 1085 } 1086 offset += rectsize; 1087 } 1088 }, 1089 "")); 1090 mixin(xx); 1091 } 1092 } 1093 }else if(buf.fortran_contiguous) { 1094 // really rubbish. no memcpy. 1095 static if(isDynamicArray!T) { 1096 _array = new MatrixInfo!T.unqual(buf.shape[0]); 1097 }else static if(isPointer!T) { 1098 ubyte[] dubuf = new ubyte[](buf.buffer.len); 1099 _array = cast(typeof(_array)) dubuf.ptr; 1100 1101 } 1102 enum string xx = (MatrixInfo!T.matrixIter( 1103 "_array", "buf.shape", "_indices", 1104 MatrixInfo!T.ndim, q{ 1105 static if(isDynamicArray!(typeof($array_ixn))) { 1106 $array_ixn = new typeof($array_ixn)(buf.shape[$i+1]); 1107 }else static if(is(typeof($array_ixn) == ME)) { 1108 $array_ixn = buf.item!ME(cast(Py_ssize_t[]) _indices); 1109 } 1110 }, 1111 "")); 1112 mixin(xx); 1113 }else { 1114 // wut? 1115 could_not_convert!T(o,("todo: know what todo")); 1116 assert(0); 1117 } 1118 return cast(T) _array; 1119 }else if(buf.has_simple) { 1120 /* 1121 static if(isDynamicArray!T) { 1122 E[] array = new E[](buf.buf.length); 1123 }else static if(isStaticArray!T) { 1124 if(buf.buf.length != T.length) 1125 could_not_convert!T(o, 1126 format("length mismatch: %s vs %s", 1127 buf.buf.length, T.length)); 1128 E[T.length] array; 1129 } 1130 return cast(T) array; 1131 */ 1132 assert(0, "py jingo wat we do here?"); 1133 } 1134 return cast(T) _array; 1135 } 1136 } 1137 1138 /** 1139 Wrap a D input range as a python iterator object. 1140 1141 Does not work for UFCS ranges (e.g. arrays), classes 1142 */ 1143 auto wrap_range(Range)(Range range) if(is(Range == struct)) { 1144 static assert(!is(Range == RangeWrapper)); 1145 import core.memory; 1146 RangeWrapper wrap; 1147 // the hackery! the hackery! 1148 Range* keeper = cast(Range*) GC.calloc(Range.sizeof); 1149 std.algorithm.move(range, *keeper); 1150 wrap.range = cast(void*) keeper; 1151 wrap.tid = typeid(Range); 1152 wrap.empty = dg_wrapper(keeper, &Range.empty); 1153 wrap.popFront = dg_wrapper(keeper, &Range.popFront); 1154 auto front_dg = 1155 dg_wrapper(keeper, cast(ElementType!Range function()) &Range.front); 1156 wrap.front = delegate PyObject*() { 1157 return d_to_python(front_dg()); 1158 }; 1159 return wrap; 1160 } 1161 1162 /** 1163 Wrapper type wrapping a D input range as a python iterator object 1164 1165 Lives in reserved python module "pyd". 1166 */ 1167 struct RangeWrapper { 1168 void* range; 1169 void delegate() popFront; 1170 PyObject* delegate() front; 1171 bool delegate() empty; 1172 TypeInfo tid; 1173 1174 RangeWrapper* iter() { 1175 return &this; 1176 } 1177 PyObject* next() { 1178 if(this.empty()) { 1179 return null; 1180 }else { 1181 auto result = d_to_python(this.front()); 1182 this.popFront(); 1183 return result; 1184 } 1185 } 1186 } 1187 1188 /// Check T against format 1189 /// See_Also: 1190 /// <a href='http://docs.python.org/library/struct.html#struct-format-strings'> 1191 /// Struct Format Strings </a> 1192 bool match_format_type(T)(string format) { 1193 import std.exception: enforce; 1194 1195 alias T S; 1196 size_t S_size = S.sizeof; 1197 enforce(format.length > 0); 1198 1199 bool native_size = false; 1200 switch(format[0]) { 1201 case '@': 1202 // this (*&^& function is not defined 1203 //PyBuffer_SizeFromFormat() 1204 native_size = true; 1205 goto case; 1206 case '=','<','>','!': 1207 format = format[1 .. $]; 1208 default: 1209 break; 1210 } 1211 // by typeishness 1212 switch(format[0]) { 1213 case 'x', 's', 'p': 1214 // don't support these 1215 enforce(false, "unsupported format: " ~ format); 1216 case 'c': 1217 break; 1218 case 'b', 'h','i','l','q': 1219 if(!isSigned!S) return false; 1220 else break; 1221 case 'B', 'H', 'I', 'L','Q': 1222 if(!isUnsigned!S) return false; 1223 else break; 1224 case 'f','d': 1225 if(!isFloatingPoint!S) return false; 1226 else break; 1227 case '?': 1228 if(!isBoolean!S) return false; 1229 case 'Z': 1230 if (format.length > 1) { 1231 static if(is(S : Complex!F, F)) { 1232 S_size = F.sizeof; 1233 }else{ 1234 return false; 1235 } 1236 } 1237 format = format[1..$]; 1238 break; 1239 default: 1240 enforce(false, "unknown format: " ~ format); 1241 } 1242 1243 // by sizeishness 1244 if(native_size) { 1245 // grr 1246 assert(0, "todo"); 1247 }else{ 1248 switch(format[0]) { 1249 case 'c','b','B','?': 1250 return (S_size == 1); 1251 case 'h','H': 1252 return (S_size == 2); 1253 case 'i','I','l','L','f': 1254 return (S_size == 4); 1255 case 'q','Q','d': 1256 return (S_size == 8); 1257 default: 1258 enforce(false, "unknown format: " ~ format); 1259 assert(0); // seriously, d? 1260 1261 } 1262 } 1263 } 1264 1265 /// generate a struct format string from T 1266 template SimpleFormatType(T) { 1267 enum supported = 1268 (isFloatingPoint!T && (T.sizeof == 4 || T.sizeof == 8) || 1269 isIntegral!T); 1270 1271 PyObject* pyType() { 1272 //assert(supported); 1273 static if(supported) { 1274 version(Python_3_0_Or_Later) { 1275 alias to_python = d_to_python; 1276 }else{ 1277 // stinking py2 array won't take unicode 1278 alias to_python = d_to_python_bytes; 1279 } 1280 static if(isFloatingPoint!T && T.sizeof == 4) { 1281 return to_python("f"); 1282 }else static if(isFloatingPoint!T && T.sizeof == 8) { 1283 return to_python("d"); 1284 }else static if(isIntegral!T && T.sizeof == 1) { 1285 return to_python(isSigned!T ? "b" : "B"); 1286 }else static if(isIntegral!T && T.sizeof == 2) { 1287 return to_python(isSigned!T ? "h" : "H"); 1288 }else static if(isIntegral!T && T.sizeof == 4) { 1289 return to_python(isSigned!T ? "i" : "I"); 1290 }else static if(isIntegral!T && T.sizeof == 8) { 1291 return to_python(isSigned!T ? "q" : "Q"); 1292 } 1293 return null; 1294 }else{ 1295 assert(false); 1296 } 1297 } 1298 } 1299 1300 1301 /** 1302 Check that T is a pointer to a rectangular static array. 1303 */ 1304 template IsStaticArrayPointer(T) { 1305 template _Inner(S) { 1306 static if(isStaticArray!S) { 1307 enum _Inner = _Inner!(ElementType!S); 1308 } else static if(isArray!S || isPointer!S) { 1309 enum _Inner = false; 1310 }else { 1311 enum _Inner = true; 1312 } 1313 } 1314 static if(isPointer!T) { 1315 enum bool IsStaticArrayPointer = _Inner!(PointerTarget!T); 1316 }else{ 1317 enum bool IsStaticArrayPointer = false; 1318 } 1319 } 1320 1321 /** 1322 Some reflective information about multidimensional arrays 1323 1324 Handles dynamic arrays, static arrays, and pointers to static arrays. 1325 */ 1326 template MatrixInfo(T) if(isArray!T || IsStaticArrayPointer!T) { 1327 template ElementType2(_T) { 1328 static if(isSomeString!_T) { 1329 alias ElementType2=_T; 1330 }else{ 1331 alias ElementType2=ElementType!_T; 1332 } 1333 } 1334 1335 template _dim_list(T, dimi...) { 1336 static if(isSomeString!T) { 1337 alias dimi list; 1338 alias T elt; 1339 alias Unqual!T unqual; 1340 } else static if(isDynamicArray!T) { 1341 alias _dim_list!(ElementType2!T, dimi,-1) next; 1342 alias next.list list; 1343 alias next.elt elt; 1344 alias next.unqual[] unqual; 1345 }else static if(isStaticArray!T) { 1346 alias _dim_list!(ElementType2!T, dimi, cast(Py_ssize_t) T.length) next; 1347 alias next.list list; 1348 alias next.elt elt; 1349 alias next.unqual[T.length] unqual; 1350 }else { 1351 alias dimi list; 1352 alias T elt; 1353 alias Unqual!T unqual; 1354 } 1355 } 1356 1357 string tuple2string(T...)() { 1358 string s = "["; 1359 foreach(i, t; T) { 1360 if(t == -1) s ~= "*"; 1361 else s ~= to!string(t); 1362 if(i == T.length-1) { 1363 s ~= "]"; 1364 }else{ 1365 s ~= ","; 1366 } 1367 } 1368 return s; 1369 } 1370 1371 /** 1372 Build shape from t. Assumes all arrays in a dimension are initialized 1373 and of uniform length. 1374 */ 1375 Py_ssize_t[] build_shape(T t) { 1376 Py_ssize_t[] shape = new Py_ssize_t[](ndim); 1377 mixin(shape_builder_mixin("t", "shape")); 1378 return shape; 1379 } 1380 1381 string shape_builder_mixin(string arr_name, string shape_name) { 1382 static if(isPointer!T) { 1383 string s_ixn = "(*" ~ arr_name ~ ")"; 1384 }else{ 1385 string s_ixn = arr_name; 1386 } 1387 string s = ""; 1388 foreach(i; 0 .. ndim) { 1389 s ~= shape_name ~ "["~ to!string(i) ~"] = cast(Py_ssize_t)" ~ s_ixn ~ ".length;"; 1390 s_ixn ~= "[0]"; 1391 } 1392 return s; 1393 } 1394 1395 /** 1396 Ensures that T can store a matrix of _shape shape. 1397 */ 1398 bool check(Py_ssize_t[] shape) { 1399 if (shape.length != dim_list.length) return false; 1400 foreach(i, d; dim_list) { 1401 static if(dim_list[i] == -1) continue; 1402 else if(d != shape[i]) return false; 1403 } 1404 return true; 1405 } 1406 1407 /** 1408 Generate a mixin string of nested for loops that iterate over the 1409 first ndim dimensions of an array of type T (or, preferrably 1410 MatrixInfo!T.unqual). 1411 1412 Params: 1413 arr_name = name of array to iterate. 1414 shape_name = name of array of dimension lengths. 1415 index_name = name to use for index vector. Declared in a new nested scoped. 1416 ndim = number of dimensions to iterate over. 1417 pre_code = code to mixin each for loop before beginning the nested for loop. 1418 post_code = code to mix in to each for loop after finishing the nested for loop. 1419 */ 1420 1421 string matrixIter(string arr_name, string shape_name, 1422 string index_name, 1423 size_t ndim, 1424 string pre_code, string post_code) { 1425 string s_begin = "{\n"; 1426 string s_end = "}\n"; 1427 static if(isPointer!T) { 1428 string s_ixn = "(*" ~ arr_name ~ ")"; 1429 }else{ 1430 string s_ixn = arr_name; 1431 } 1432 1433 s_begin ~= "size_t[" ~ to!string(ndim) ~ "] " ~ index_name ~ ";\n"; 1434 foreach(i; 0 .. ndim) { 1435 string s_i = to!string(i); 1436 s_ixn ~= "["~ index_name ~ "[" ~ s_i ~ "]]"; 1437 string index = index_name~ "[" ~ s_i ~ "]"; 1438 string shape_i = shape_name ~ "[" ~ s_i ~ "]"; 1439 s_begin ~= "for("~index~" = 0;" ~index ~ " < " ~ shape_i ~ 1440 "; " ~ index ~ "++) {"; 1441 s_end ~= "}\n"; 1442 1443 string pre_code_i = replace(pre_code, "$array_ixn", s_ixn); 1444 pre_code_i = replace(pre_code_i, "$i", s_i); 1445 s_begin ~= pre_code_i; 1446 string post_code_i = replace(post_code, "$array_ixn", s_ixn); 1447 post_code_i = replace(post_code_i, "$i", s_i); 1448 s_end ~= post_code_i; 1449 } 1450 return s_begin ~ s_end; 1451 } 1452 1453 static if(isPointer!T && isStaticArray!(PointerTarget!T)) { 1454 alias _dim_list!(PointerTarget!T) _dim; 1455 /// T, with all nonmutable qualifiers stripped away. 1456 alias _dim.unqual* unqual; 1457 }else{ 1458 alias _dim_list!T _dim; 1459 alias _dim.unqual unqual; 1460 } 1461 /// tuple of dimensions of T. 1462 /// dim_list[0] will be the dimension furthest from the MatrixElementType 1463 /// i.e. for double[1][2][3], dim_list == (3, 2, 1). 1464 /// Lists -1 as dimension of dynamic arrays. 1465 alias _dim.list dim_list; 1466 /// number of dimensions of this matrix 1467 enum ndim = dim_list.length; 1468 /// T is a RectArray if: 1469 /// * it is any multidimensional static array (or a pointer to) 1470 /// * it is a 1 dimensional dynamic array 1471 enum bool isRectArray = staticIndexOf!(-1, dim_list) == -1 || dim_list.length == 1; 1472 //(1,2,3) -> rectArrayAt == 0 1473 //(-1,2,3) -> rectArrayAt == 1 == 3 - 2 == len - max(indexof_rev, 1) 1474 //(-1,-1,1) -> rectArrayAt == 2 == 3 - 1 == len - max(indexof_rev,1) 1475 //(-1,-1,-1) -> rectArrayAt == 2 == 3 - 1 == len - max(indexof_rev,1) 1476 //(2,2,-1) -> rectArrayAt == 2 1477 enum size_t indexof_rev = staticIndexOf!(-1, Reverse!dim_list); 1478 /// Highest dimension where it and all subsequent dimensions form a 1479 /// RectArray. 1480 enum size_t rectArrayAt = isRectArray ? 0 : dim_list.length - max(indexof_rev, 1); 1481 template _rect_type(S, size_t i) { 1482 static if(i == rectArrayAt) { 1483 alias S _rect_type; 1484 } else { 1485 alias _rect_type!(ElementType!S, i+1) _rect_type; 1486 } 1487 } 1488 /// unqualified highest dimension subtype of T forming RectArray 1489 alias _rect_type!(unqual, 0) RectArrayType; 1490 /// Pretty string of dimension list for T 1491 enum string dimstring = tuple2string!(dim_list)(); 1492 /// Matrix element type of T 1493 /// E.g. immutable(double) for T=immutable(double[4][4]) 1494 alias _dim.elt MatrixElementType; 1495 } 1496 1497 @property PyTypeObject* array_array_Type() { 1498 static PyTypeObject* m_type; 1499 if(!m_type) { 1500 PyObject* array = PyImport_ImportModule("array"); 1501 scope(exit) Py_XDECREF(array); 1502 m_type = cast(PyTypeObject*) PyObject_GetAttrString(array, "array"); 1503 } 1504 return m_type; 1505 } 1506 1507 alias python_to_d!(Object) python_to_d_Object; 1508 1509 void could_not_convert(T) (PyObject* o, string reason = "", 1510 string file = __FILE__, size_t line = __LINE__) { 1511 import std..string: format; 1512 // Pull out the name of the type of this Python object, and the 1513 // name of the D type. 1514 string py_typename, d_typename; 1515 PyObject* py_type, py_type_str; 1516 py_type = PyObject_Type(o); 1517 if (py_type is null) { 1518 py_typename = "<unknown>"; 1519 } else { 1520 py_type_str = PyObject_GetAttrString(py_type, cast(const(char)*) "__name__".ptr); 1521 Py_DECREF(py_type); 1522 if (py_type_str is null) { 1523 py_typename = "<unknown>"; 1524 } else { 1525 py_typename = python_to_d!string(py_type_str); 1526 Py_DECREF(py_type_str); 1527 } 1528 } 1529 d_typename = typeid(T).toString(); 1530 string because; 1531 if(reason != "") because = format(" because: %s", reason); 1532 throw new PydConversionException( 1533 format("Couldn't convert Python type '%s' to D type '%s'%s", 1534 py_typename, 1535 d_typename, 1536 because), 1537 file, line 1538 ); 1539 } 1540 1541 // stuff this down here until we can figure out what to do with it. 1542 // Python-header-file: Modules/arraymodule.c: 1543 1544 struct arraydescr{ 1545 int typecode; 1546 int itemsize; 1547 PyObject* function(arrayobject*, Py_ssize_t) getitem; 1548 int function(arrayobject*, Py_ssize_t, PyObject*) setitem; 1549 } 1550 1551 struct arrayobject { 1552 mixin PyObject_VAR_HEAD; 1553 ubyte* ob_item; 1554 Py_ssize_t allocated; 1555 arraydescr* ob_descr; 1556 PyObject* weakreflist; /* List of weak references */ 1557 } 1558 1559 template get_type(string _module, string type_name) { 1560 @property PyTypeObject* get_type() { 1561 static PyTypeObject* m_type; 1562 static bool inited = false; 1563 if(!inited) { 1564 inited = true; 1565 PyObject* py_module = PyImport_ImportModule(_module); 1566 if(py_module) { 1567 scope(exit) Py_XDECREF(py_module); 1568 m_type = cast(PyTypeObject*) PyObject_GetAttrString( 1569 py_module, type_name 1570 ); 1571 }else{ 1572 PyErr_Clear(); 1573 } 1574 } 1575 return m_type; 1576 } 1577 } 1578 1579 alias numpy_datetime64 = get_type!("numpy", "datetime64"); 1580 alias datetime_datetime = get_type!("datetime", "datetime"); 1581 1582 1583 bool is_numpy_datetime64(PyObject* py) { 1584 auto py_type = cast(PyTypeObject*) PyObject_Type(py); 1585 return (numpy_datetime64 !is null && py_type == numpy_datetime64); 1586 } 1587 1588 T python_to_d_numpy_datetime64(T)(PyObject* py) { 1589 PyObject* astype = PyObject_GetAttrString(py, "astype"); 1590 PyObject* args = PyTuple_FromItems(cast(PyObject*) datetime_datetime); 1591 scope(exit) Py_DECREF(args); 1592 PyObject* datetime = PyObject_CallObject(astype, args); 1593 scope(exit) Py_DECREF(datetime); 1594 return python_to_d!T(datetime); 1595 }