1 /* 2 Copyright (c) 2006 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 Contains utilities for operating on generic python objects. 25 */ 26 module pyd.pydobject; 27 28 import deimos.python.Python; 29 import pyd.def; 30 import pyd.exception; 31 import pyd.make_object; 32 import std.exception: enforce; 33 import std.conv; 34 import util.conv; 35 36 37 /** 38 * Wrapper class for a Python/C API PyObject. 39 * 40 * Nearly all of these member functions may throw a PythonException if the 41 * underlying Python API raises a Python exception. 42 * 43 * Authors: $(LINK2 mailto:kirklin.mcdonald@gmail.com, Kirk McDonald) 44 * Date: June 18, 2006 45 * See_Also: 46 * $(LINK2 http://docs.python.org/api/api.html, The Python/C API) 47 */ 48 class PydObject { 49 protected: 50 PyObject* m_ptr; 51 public: 52 /** 53 * Wrap an owned PyObject*. 54 * This should typically only be used in conjuction with functions 55 * in the deimos API that return PyObject* (they return new references). 56 * Otherwise, wrap the incoming PyObject* with borrowed. 57 */ 58 this(PyObject* o) { 59 if (o is null) handle_exception(); 60 m_ptr = o; 61 } 62 63 /** 64 * Own a borrowed PyObject* and wrap it. 65 */ 66 this(Borrowed!PyObject* o) { 67 if (o is null) handle_exception(); 68 // PydObject always owns its references 69 m_ptr = Py_INCREF(o); 70 } 71 72 /// Constructs an instance of the Py_None PydObject. 73 this() { 74 m_ptr = Py_INCREF(Py_None()); 75 } 76 77 /// Destructor. Calls Py_DECREF on PyObject reference. 78 ~this() { 79 if (m_ptr && !Py_Finalize_called) Py_DECREF(m_ptr); 80 m_ptr = null; 81 } 82 83 version(Python_2_6_Or_Later) { 84 /** 85 exposes a lowish-level wrapper of the new-style buffer interface 86 87 See_also: 88 <a href="http://docs.python.org/c-api/buffer.html"> 89 Buffers and MemoryView Objects </a> 90 */ 91 class BufferView { 92 Py_buffer buffer; 93 /// supports PyBUF_SIMPLE. $(BR) 94 /// should always be true. 95 bool has_simple = false; 96 /// supports PyBUF_ND. $(BR) 97 /// i.e. buffer supplies ndim, shape. 98 bool has_nd = false; 99 /// supports PyBUF_STRIDES. $(BR) 100 /// i.e. buffer supplies strides. 101 bool has_strides = false; 102 /// supports PyBUF_INDIRECT. $(BR) 103 /// i.e. buffer supplies suboffsets. 104 bool has_indirect = false; 105 /// supports PyBUF_C_CONTIGUOUS. $(BR) 106 /// buffer is row-major. 107 bool c_contiguous = false; 108 /// supports PyBUF_F_CONTIGUOUS. $(BR) 109 /// buffer is column-major 110 bool fortran_contiguous = false; 111 112 private @property m_ptr() { 113 return this.outer.m_ptr; 114 } 115 116 /** 117 construct buffer view. Probe for capabilities this object supports. 118 */ 119 this() { 120 enforce(PyObject_CheckBuffer(m_ptr)); 121 122 // probe buffer for capabilities 123 if(PyObject_GetBuffer(m_ptr, &buffer, PyBUF_SIMPLE) == 0) { 124 has_simple = true; 125 }else{ 126 PyErr_Clear(); 127 } 128 if(PyObject_GetBuffer(m_ptr, &buffer, 129 PyBUF_STRIDES|PyBUF_C_CONTIGUOUS) == 0) { 130 has_nd = true; 131 has_strides = true; 132 c_contiguous = true; 133 }else{ 134 PyErr_Clear(); 135 if(PyObject_GetBuffer(m_ptr, &buffer, 136 PyBUF_STRIDES|PyBUF_F_CONTIGUOUS) == 0) { 137 has_nd = true; 138 has_strides = true; 139 fortran_contiguous = true; 140 }else{ 141 PyErr_Clear(); 142 if(PyObject_GetBuffer(m_ptr, &buffer, 143 PyBUF_STRIDES) == 0) { 144 has_nd = true; 145 has_strides = true; 146 }else{ 147 PyErr_Clear(); 148 if(PyObject_GetBuffer(m_ptr, &buffer, 149 PyBUF_ND) == 0) { 150 has_nd = true; 151 }else{ 152 PyErr_Clear(); 153 } 154 } 155 } 156 } 157 if(has_strides) { 158 if(PyObject_GetBuffer(m_ptr, &buffer, PyBUF_INDIRECT) == 0) { 159 has_indirect = true; 160 }else{ 161 PyErr_Clear(); 162 } 163 } 164 165 int flags = PyBUF_FORMAT | 166 (has_nd ? PyBUF_ND : 0) | 167 (has_strides ? PyBUF_STRIDES : 0) | 168 (c_contiguous ? PyBUF_C_CONTIGUOUS : 0) | 169 (fortran_contiguous ? PyBUF_F_CONTIGUOUS : 0) | 170 (has_indirect ? PyBUF_INDIRECT : 0); 171 if(PyObject_GetBuffer(m_ptr, &buffer, flags) != 0) { 172 handle_exception(); 173 } 174 } 175 176 /** 177 Construct buffer view. Don't probe for capabilities; assume 178 object supports capabilities implied by flags. 179 */ 180 this(int flags) { 181 enforce(PyObject_CheckBuffer(m_ptr)); 182 has_simple = true; 183 has_nd = (PyBUF_ND & flags) == PyBUF_ND; 184 has_strides = (PyBUF_STRIDES & flags) == PyBUF_STRIDES; 185 c_contiguous = (PyBUF_C_CONTIGUOUS & flags) == PyBUF_C_CONTIGUOUS; 186 fortran_contiguous = (PyBUF_F_CONTIGUOUS & flags) == PyBUF_F_CONTIGUOUS; 187 has_indirect = (PyBUF_INDIRECT & flags) == PyBUF_INDIRECT; 188 189 if(PyObject_GetBuffer(m_ptr, &buffer, flags) != 0) { 190 handle_exception(); 191 } 192 } 193 194 /** 195 Get the raw bytes of this buffer 196 */ 197 @property ubyte[] buf() { 198 enforce(has_simple); 199 enforce(buffer.len >= 0); 200 return (cast(ubyte*) buffer.buf)[0 .. buffer.len]; 201 } 202 203 /// _ 204 @property bool readonly() { 205 return cast(bool) buffer.readonly; 206 } 207 208 /** 209 Get the struct-style _format of the element type of this buffer. 210 211 See_Also: 212 <a href='http://docs.python.org/library/struct.html#struct-format-strings'> 213 Struct Format Strings </a> 214 */ 215 216 @property string format() { 217 return to!string(buffer.format); 218 } 219 220 /// Get number of dimensions of this buffer. 221 @property int ndim() { 222 if(!has_nd) return 0; 223 return buffer.ndim; 224 } 225 226 /// _ 227 @property Py_ssize_t[] shape() { 228 if(!has_nd || !buffer.shape) return []; 229 return buffer.shape[0 .. ndim]; 230 } 231 232 /// _ 233 @property Py_ssize_t[] strides() { 234 if(!has_strides || !buffer.strides) return []; 235 return buffer.strides[0 .. ndim]; 236 } 237 /// _ 238 @property Py_ssize_t[] suboffsets() { 239 if(!has_indirect || !buffer.suboffsets) return []; 240 return buffer.suboffsets[0 .. ndim]; 241 } 242 243 /// _ 244 @property itemsize() { 245 return buffer.itemsize; 246 } 247 248 /// _ 249 T item(T)(Py_ssize_t[] indices...) { 250 enforce(itemsize == T.sizeof); 251 return *cast(T*) item_ptr(indices); 252 } 253 /// _ 254 void set_item(T)(T value, Py_ssize_t[] indices...) { 255 import std.traits; 256 enforce(itemsize == T.sizeof); 257 auto ptr = cast(Unqual!T*) item_ptr(indices); 258 *ptr = value; 259 } 260 261 void* item_ptr(Py_ssize_t[] indices...) { 262 if(has_strides) enforce(indices.length == ndim); 263 else enforce(indices.length == 1); 264 if(has_strides) { 265 void* ptr = buffer.buf; 266 foreach(i, index; indices) { 267 ptr += strides[i] * index; 268 if(has_indirect && suboffsets != [] && 269 suboffsets[i] >= 0) { 270 ptr += suboffsets[i]; 271 } 272 } 273 return ptr; 274 }else { 275 return buffer.buf+indices[0]; 276 } 277 } 278 } 279 280 281 /** 282 Get a BufferView of this object. 283 Will fail if this does not support the new buffer interface. 284 */ 285 BufferView buffer_view() { 286 return new this.BufferView(); 287 } 288 /** 289 Get a BufferView of this object without probing for capabilities. 290 Will fail if this does not support the new buffer interface. 291 */ 292 BufferView buffer_view(int flags) { 293 return new this.BufferView(flags); 294 } 295 } 296 297 /** 298 * Returns a borrowed reference to the PyObject. 299 */ 300 @property Borrowed!PyObject* ptr() { return borrowed(m_ptr); } 301 302 /* 303 * Prints PyObject to a C FILE* object. 304 * Params: 305 * fp = The file object to _print to. core.stdc.stdio.stdout by default. 306 * raw = If $(D_KEYWORD true), prints the "str" representation of the 307 * PydObject, and uses the "repr" otherwise. Defaults to 308 * $(D_KEYWORD false). 309 * Bugs: This does not seem to work, raising an AccessViolation. Meh. 310 * Use toString. 311 */ 312 /+ 313 void print(FILE* fp=stdout, bool raw=false) { 314 if (PyObject_Print(m_ptr, fp, raw ? Py_PRINT_RAW : 0) == -1) 315 handle_exception(); 316 } 317 +/ 318 319 /// Equivalent to _hasattr(this, attr_name) in Python. 320 bool hasattr(string attr_name) { 321 return PyObject_HasAttrString(m_ptr, zcc(attr_name)) == 1; 322 } 323 324 /// Equivalent to _hasattr(this, attr_name) in Python. 325 bool hasattr(PydObject attr_name) { 326 return PyObject_HasAttr(m_ptr, attr_name.m_ptr) == 1; 327 } 328 329 /// Equivalent to _getattr(this, attr_name) in Python. 330 PydObject getattr(string attr_name) { 331 return new PydObject(PyObject_GetAttrString(m_ptr, zcc(attr_name))); 332 } 333 334 /// Equivalent to _getattr(this, attr_name) in Python. 335 PydObject getattr(PydObject attr_name) { 336 return new PydObject(PyObject_GetAttr(m_ptr, attr_name.m_ptr)); 337 } 338 339 /** 340 * Equivalent to _setattr(this, attr_name, v) in Python. 341 */ 342 void setattr(string attr_name, PydObject v) { 343 if (PyObject_SetAttrString(m_ptr, zcc(attr_name), v.m_ptr) == -1) 344 handle_exception(); 345 } 346 347 /** 348 * Equivalent to _setattr(this, attr_name, v) in Python. 349 */ 350 void setattr(PydObject attr_name, PydObject v) { 351 if (PyObject_SetAttr(m_ptr, attr_name.m_ptr, v.m_ptr) == -1) 352 handle_exception(); 353 } 354 355 /** 356 * Equivalent to del this.attr_name in Python. 357 */ 358 void delattr(string attr_name) { 359 if (PyObject_DelAttrString(m_ptr, zcc(attr_name)) == -1) 360 handle_exception(); 361 } 362 363 /** 364 * Equivalent to del this.attr_name in Python. 365 */ 366 void delattr(PydObject attr_name) { 367 if (PyObject_DelAttr(m_ptr, attr_name.m_ptr) == -1) 368 handle_exception(); 369 } 370 371 /** 372 * Exposes Python object comparison to D. Equivalent to cmp(this, rhs) in Python. 373 */ 374 override int opCmp(Object o) { 375 PydObject rhs = cast(PydObject) o; 376 if (!rhs) return -1; 377 version(Python_3_0_Or_Later) { 378 int res = PyObject_RichCompareBool(m_ptr, rhs.m_ptr, Py_LT); 379 if(res == -1) handle_exception(); 380 if(res == 1) return -1; 381 res = PyObject_RichCompareBool(m_ptr, rhs.m_ptr, Py_EQ); 382 if(res == -1) handle_exception(); 383 if(res == 1) return 0; 384 return 1; 385 }else{ 386 // This function happily maps exactly to opCmp 387 // EMN: but goes away in python 3. 388 int res = PyObject_Compare(m_ptr, rhs.m_ptr); 389 // Check for possible error 390 handle_exception(); 391 return res; 392 } 393 } 394 395 /** 396 * Exposes Python object equality check to D. 397 */ 398 override bool opEquals(Object o) { 399 PydObject rhs = cast(PydObject) o; 400 if (!rhs) return false; 401 int res = PyObject_RichCompareBool(m_ptr, rhs.m_ptr, Py_EQ); 402 if(res == -1) handle_exception(); 403 return res == 1; 404 } 405 406 /// Equivalent to _repr(this) in Python. 407 PydObject repr() { 408 return new PydObject(PyObject_Repr(m_ptr)); 409 } 410 411 /// Equivalent to _str(this) in Python. 412 PydObject str() { 413 return new PydObject(PyObject_Str(m_ptr)); 414 } 415 /// Allows use of PydObject in writeln via %s 416 override string toString() { 417 return python_to_d!(string)(m_ptr); 418 } 419 420 version(Python_3_0_Or_Later) { 421 }else{ 422 /// Equivalent to _unicode(this) in Python. 423 PydObject unicode() { 424 return new PydObject(PyObject_Unicode(m_ptr)); 425 } 426 } 427 428 /// Equivalent to _bytes(this) in Python. 429 PydObject bytes() { 430 return new PydObject(PyObject_Bytes(m_ptr)); 431 } 432 433 /// Equivalent to isinstance(this, cls) in Python. 434 bool isinstance(PydObject cls) { 435 int res = PyObject_IsInstance(m_ptr, cls.m_ptr); 436 if (res == -1) handle_exception(); 437 return res == 1; 438 } 439 440 /// Equivalent to issubclass(this, cls) in Python. Only works if this is a class. 441 bool issubclass(PydObject cls) { 442 int res = PyObject_IsSubclass(m_ptr, cls.m_ptr); 443 if (res == -1) handle_exception(); 444 return res == 1; 445 } 446 447 /// Equivalent to _callable(this) in Python. 448 bool callable() { 449 return PyCallable_Check(m_ptr) == 1; 450 } 451 452 /** 453 * Calls the PydObject with args. 454 * Params: 455 * args = Should be a tuple of the arguments to pass. Omit to 456 * call with no arguments. 457 * Returns: Whatever this function object returns. 458 */ 459 PydObject unpack_call(PydObject args=null) { 460 return new PydObject(PyObject_CallObject(m_ptr, args is null ? null : args.m_ptr)); 461 } 462 463 /** 464 * Calls the PydObject with positional and keyword arguments. 465 * Params: 466 * args = Positional arguments. Should be a tuple. Pass an empty 467 * tuple for no positional arguments. 468 * kw = Keyword arguments. Should be a dict. 469 * Returns: Whatever this function object returns. 470 */ 471 PydObject unpack_call(PydObject args, PydObject kw) { 472 return new PydObject(PyObject_Call(m_ptr, args.m_ptr, kw.m_ptr)); 473 } 474 475 /** 476 * Calls the PydObject with any convertible D items. 477 */ 478 PydObject opCall(T ...) (T t) { 479 PyObject* tuple = PyTuple_FromItems(t); 480 if (tuple is null) handle_exception(); 481 PyObject* result = PyObject_CallObject(m_ptr, tuple); 482 Py_DECREF(tuple); 483 if (result is null) handle_exception(); 484 return new PydObject(result); 485 } 486 487 /** 488 * Calls the PydObject method with args. 489 * Params: 490 * name = name of method to call 491 * args = Should be a tuple of the arguments to pass. Omit to 492 * call with no arguments. 493 * Returns: Whatever this object's method returns. 494 */ 495 PydObject method_unpack(string name, PydObject args=null) { 496 // Get the method PydObject 497 PyObject* m = PyObject_GetAttrString(m_ptr, zcc(name)); 498 PyObject* result; 499 // If this method doesn't exist (or other error), throw exception 500 if (m is null) handle_exception(); 501 // Call the method, and decrement the refcounts on the temporaries. 502 result = PyObject_CallObject(m, args is null ? null : args.m_ptr); 503 Py_DECREF(m); 504 // Return the result. 505 return new PydObject(result); 506 } 507 508 /** 509 * Calls the PydObject method with positional and keyword arguments. 510 * Params: 511 * name = name of method to call. 512 * args = Positional arguments. Should be a tuple. Pass an empty 513 * tuple for no positional arguments. 514 * kw = Keyword arguments. Should be a dict. 515 * Returns: Whatever this object's method returns. 516 */ 517 PydObject method_unpack(string name, PydObject args, PydObject kw) { 518 // Get the method PydObject 519 PyObject* m = PyObject_GetAttrString(m_ptr, zcc(name)); 520 PyObject* result; 521 // If this method doesn't exist (or other error), throw exception. 522 if (m is null) handle_exception(); 523 // Call the method, and decrement the refcounts on the temporaries. 524 result = PyObject_Call(m, args.m_ptr, kw.m_ptr); 525 Py_DECREF(m); 526 // Return the result. 527 return new PydObject(result); 528 } 529 530 /** 531 * Calls a method of the object with any convertible D items. 532 */ 533 PydObject method(T ...) (string name, T t) { 534 PyObject* mthd = PyObject_GetAttrString(m_ptr, zcc(name)); 535 if (mthd is null) handle_exception(); 536 PyObject* tuple = PyTuple_FromItems(t); 537 if (tuple is null) { 538 Py_DECREF(mthd); 539 handle_exception(); 540 } 541 PyObject* result = PyObject_CallObject(mthd, tuple); 542 Py_DECREF(mthd); 543 Py_DECREF(tuple); 544 if (result is null) handle_exception(); 545 return new PydObject(result); 546 } 547 548 /// Equivalent to _hash(this) in Python. 549 hash_t hash() { 550 hash_t res = PyObject_Hash(m_ptr); 551 if (res == -1) handle_exception(); 552 return res; 553 } 554 555 /// Convert this object to instance of T. 556 T to_d(T)() { 557 return python_to_d!(T)(m_ptr); 558 } 559 560 /// Equivalent to "_not this" in Python. 561 bool not() { 562 int res = PyObject_Not(m_ptr); 563 if (res == -1) handle_exception(); 564 return res == 1; 565 } 566 567 /** 568 * Gets the _type of this PydObject. Equivalent to _type(this) in Python. 569 * Returns: The _type PydObject of this PydObject. 570 */ 571 PydObject type() { 572 return new PydObject(PyObject_Type(m_ptr)); 573 } 574 575 /** 576 * The _length of this PydObject. Equivalent to _len(this) in Python. 577 */ 578 Py_ssize_t length() { 579 Py_ssize_t res = PyObject_Length(m_ptr); 580 if (res == -1) handle_exception(); 581 return res; 582 } 583 /// Equivalent to length() 584 Py_ssize_t size() { return length(); } 585 586 /// Equivalent to _dir(this) in Python. 587 PydObject dir() { 588 return new PydObject(PyObject_Dir(m_ptr)); 589 } 590 591 //---------- 592 // Indexing 593 //---------- 594 /// Equivalent to o[_key] in Python. 595 PydObject opIndex(PydObject key) { 596 return new PydObject(PyObject_GetItem(m_ptr, key.m_ptr)); 597 } 598 /** 599 * Equivalent to o['_key'] in Python; usually only makes sense for 600 * mappings. 601 */ 602 PydObject opIndex(string key) { 603 // wtf? PyMapping_GetItemString fails on dicts 604 if(PyDict_Check(m_ptr)) { 605 return new PydObject(PyDict_GetItemString(m_ptr, zc(key))); 606 }else{ 607 return new PydObject(PyMapping_GetItemString(m_ptr, zc(key))); 608 } 609 } 610 /// Equivalent to o[_i] in Python; usually only makes sense for sequences. 611 PydObject opIndex(Py_ssize_t i) { 612 return new PydObject(PySequence_GetItem(m_ptr, i)); 613 } 614 615 /// Equivalent to o[_key] = _value in Python. 616 void opIndexAssign(T,S)(T value, S key) { 617 static if (is(T == PydObject)) { 618 alias value v; 619 }else{ 620 auto v = py(value); 621 } 622 static if (is(S : int)) { 623 if (PySequence_SetItem(m_ptr, key, v.m_ptr) == -1) 624 handle_exception(); 625 return; 626 }else static if (is(S == PydObject)) { 627 alias key k; 628 }else{ 629 auto k = py(key); 630 } 631 632 static if(!(is(S : int))) { 633 if (PyObject_SetItem(m_ptr, k.m_ptr, v.m_ptr) == -1) 634 handle_exception(); 635 } 636 } 637 /// Equivalent to del o[_key] in Python. 638 void del_item(PydObject key) { 639 if (PyObject_DelItem(m_ptr, key.m_ptr) == -1) 640 handle_exception(); 641 } 642 /** 643 * Equivalent to del o['_key'] in Python. Usually only makes sense for 644 * mappings. 645 */ 646 void del_item(string key) { 647 if (PyMapping_DelItemString(m_ptr, zc(key)) == -1) 648 handle_exception(); 649 } 650 /** 651 * Equivalent to del o[_i] in Python. Usually only makes sense for 652 * sequences. 653 */ 654 void del_item(int i) { 655 if (PySequence_DelItem(m_ptr, i) == -1) 656 handle_exception(); 657 } 658 659 //--------- 660 // Slicing 661 //--------- 662 /// Equivalent to o[_i1:_i2] in Python. 663 PydObject opSlice(Py_ssize_t i1, Py_ssize_t i2) { 664 return new PydObject(PySequence_GetSlice(m_ptr, i1, i2)); 665 } 666 /// Equivalent to o[:] in Python. 667 PydObject opSlice() { 668 return this.opSlice(0, this.length()); 669 } 670 /// Equivalent to o[_i1:_i2] = _v in Python. 671 void opSliceAssign(PydObject v, Py_ssize_t i1, Py_ssize_t i2) { 672 if (PySequence_SetSlice(m_ptr, i1, i1, v.m_ptr) == -1) 673 handle_exception(); 674 } 675 /// Equivalent to o[:] = _v in Python. 676 void opSliceAssign(PydObject v) { 677 this.opSliceAssign(v, 0, this.length()); 678 } 679 /// Equivalent to del o[_i1:_i2] in Python. 680 void del_slice(Py_ssize_t i1, Py_ssize_t i2) { 681 if (PySequence_DelSlice(m_ptr, i1, i2) == -1) 682 handle_exception(); 683 } 684 /// Equivalent to del o[:] in Python. 685 void del_slice() { 686 this.del_slice(0, this.length()); 687 } 688 689 //----------- 690 // Iteration 691 //----------- 692 693 /** 694 * Iterates over the items in a collection, be they the items in a 695 * sequence, keys in a dictionary, or some other iteration defined for the 696 * PydObject's type. 697 */ 698 int opApply(int delegate(ref PydObject) dg) { 699 PyObject* iterator = PyObject_GetIter(m_ptr); 700 PyObject* item; 701 int result = 0; 702 PydObject o; 703 704 if (iterator == null) { 705 handle_exception(); 706 } 707 708 item = PyIter_Next(iterator); 709 while (item) { 710 o = new PydObject(item); 711 result = dg(o); 712 Py_DECREF(item); 713 if (result) break; 714 item = PyIter_Next(iterator); 715 } 716 Py_DECREF(iterator); 717 718 // Just in case an exception occured 719 handle_exception(); 720 721 return result; 722 } 723 724 /** 725 * Iterate over (key, value) pairs in a dictionary. If the PydObject is not 726 * a dict, this simply does nothing. (It iterates over no items.) You 727 * should not attempt to modify the dictionary while iterating through it, 728 * with the exception of modifying values. Adding or removing items while 729 * iterating through it is an especially bad idea. 730 */ 731 int opApply(int delegate(ref PydObject, ref PydObject) dg) { 732 Borrowed!PyObject* key, value; 733 Py_ssize_t pos = 0; 734 int result = 0; 735 PydObject k, v; 736 737 while (PyDict_Next(m_ptr, &pos, &key, &value)) { 738 k = new PydObject(key); 739 v = new PydObject(value); 740 result = dg(k, v); 741 if (result) break; 742 } 743 744 return result; 745 } 746 747 //------------ 748 // Arithmetic 749 //------------ 750 /// Forwards to appropriate Python binary operator overload. 751 /// 752 /// Note the result of / in python 3 (and python 2, if CO_FUTURE_DIVISION 753 /// is set) is interpreted as "true division", otherwise it is integer 754 /// division for integer arguments. 755 /// 756 /// See_Also: 757 /// <a href="http://www.python.org/dev/peps/pep-0238/"> PEP 238 </a> 758 PydObject opBinary(string op, T)(T o) if(op != "in") { 759 static if((is(T : int) || is(T == PydObject)) && op == "*") { 760 if(PySequence_Check(m_ptr)) { 761 static if(is(T == PydObject)) { 762 int j = python_to_d!int(o.m_ptr); 763 }else{ 764 alias o j; 765 } 766 return new PydObject(PySequence_Repeat(m_ptr, j)); 767 } 768 } 769 static if (!is(T == PydObject)) { 770 PydObject rhs = py(o); 771 }else{ 772 alias o rhs; 773 } 774 static if(op == "+") { 775 return new PydObject(PyNumber_Add(m_ptr, rhs.m_ptr)); 776 }else static if(op == "-") { 777 return new PydObject(PyNumber_Subtract(m_ptr, rhs.m_ptr)); 778 }else static if(op == "*") { 779 return new PydObject(PyNumber_Multiply(m_ptr, rhs.m_ptr)); 780 }else static if(op == "/") { 781 version(Python_3_0_Or_Later) { 782 return new PydObject(PyNumber_TrueDivide(m_ptr, rhs.m_ptr)); 783 }else{ 784 return new PydObject(PyNumber_Divide(m_ptr, rhs.m_ptr)); 785 } 786 }else static if(op == "%") { 787 return new PydObject(PyNumber_Remainder(m_ptr, rhs.m_ptr)); 788 }else static if(op == "^^") { 789 return new PydObject(PyNumber_Power(m_ptr, rhs.m_ptr, Py_INCREF(Py_None()))); 790 }else static if(op == "<<") { 791 return new PydObject(PyNumber_Lshift(m_ptr, rhs.m_ptr)); 792 }else static if(op == ">>") { 793 return new PydObject(PyNumber_Rshift(m_ptr, rhs.m_ptr)); 794 }else static if(op == "&") { 795 return new PydObject(PyNumber_And(m_ptr, rhs.m_ptr)); 796 }else static if(op == "^") { 797 return new PydObject(PyNumber_Xor(m_ptr, rhs.m_ptr)); 798 }else static if(op == "|") { 799 return new PydObject(PyNumber_Or(m_ptr, rhs.m_ptr)); 800 }else static if(op == "~") { 801 return new PydObject(PySequence_Concat(m_ptr, rhs.m_ptr)); 802 }else static assert(false, "operator " ~ op ~" not supported"); 803 } 804 805 /// Forwards to appropriate Python unary operator overload. 806 PydObject opUnary(string op)() { 807 static if(op == "+") { 808 return new PydObject(PyNumber_Positive(m_ptr)); 809 }else static if(op == "-") { 810 return new PydObject(PyNumber_Negative(m_ptr)); 811 }else static if(op == "~") { 812 return new PydObject(PyNumber_Invert(m_ptr)); 813 } 814 } 815 /// Forwards to PyNumber_FloorDivide for numbers, and method otherwise. 816 /// See_Also: 817 /// <a href="http://docs.python.org/c-api/number.html#PyNumber_FloorDivide"> 818 /// PyNumber_FloorDivide </a> 819 PydObject floor_div(PydObject o) { 820 if(PyNumber_Check(m_ptr)) { 821 return new PydObject(PyNumber_FloorDivide(m_ptr, o.m_ptr)); 822 }else{ 823 return this.method("floor_div", o); 824 } 825 } 826 /// Forwards to PyNumber_TrueDivide for numbers, and method otherwise. 827 /// See_Also: 828 /// <a href="http://docs.python.org/c-api/number.html#PyNumber_TrueDivide"> 829 /// PyNumber_TrueDivide </a> 830 PydObject true_div(PydObject o) { 831 if(PyNumber_Check(m_ptr)) { 832 return new PydObject(PyNumber_TrueDivide(m_ptr, o.m_ptr)); 833 }else{ 834 return this.method("true_div", o); 835 } 836 } 837 /// Equivalent to _divmod(this, o) for numbers, and this._divmod(o) 838 /// otherwise. 839 /// See_Also: 840 /// <a href="http://docs.python.org/library/functions.html#divmod"> 841 /// _divmod </a> 842 PydObject divmod(PydObject o) { 843 if(PyNumber_Check(m_ptr)) { 844 return new PydObject(PyNumber_Divmod(m_ptr, o.m_ptr)); 845 }else{ 846 return this.method("divmod", o); 847 } 848 } 849 /// Equivalent to _pow(this, exp, mod) for numbers, and this._pow(exp,mod) 850 /// otherwise. 851 /// See_Also: 852 /// <a href="http://docs.python.org/library/functions.html#pow"> 853 /// _pow </a> 854 PydObject pow(PydObject exp, PydObject mod=null) { 855 if(PyNumber_Check(m_ptr)) { 856 return new PydObject(PyNumber_Power(m_ptr, exp.m_ptr, (mod is null) ? null : mod.m_ptr)); 857 }else{ 858 return this.method("pow", exp, mod); 859 } 860 } 861 /// Equivalent to _abs(this) for numbers, and this._abs() 862 /// otherwise. 863 /// See_Also: 864 /// <a href="http://docs.python.org/library/functions.html#abs"> 865 /// _abs </a> 866 PydObject abs() { 867 if(PyNumber_Check(m_ptr)) { 868 return new PydObject(PyNumber_Absolute(m_ptr)); 869 }else{ 870 return this.method("abs"); 871 } 872 } 873 874 //--------------------- 875 // In-place arithmetic 876 //--------------------- 877 /// Forwards to appropriate python in-place operator overload. 878 PydObject opOpAssign(string op, T)(T o) { 879 static if((is(T : int) || is(T == PydObject)) && op == "*") { 880 if(PySequence_Check(m_ptr)) { 881 static if(is(T == PydObject)) { 882 int j = python_to_d!int(o.m_ptr); 883 }else{ 884 alias o j; 885 } 886 887 PyObject* result = PySequence_InPlaceRepeat(m_ptr, j); 888 if (result is null) handle_exception(); 889 Py_DECREF(m_ptr); 890 m_ptr = result; 891 return this; 892 } 893 } 894 static if (!is(T == PydObject)) { 895 PydObject rhs = py(o); 896 }else{ 897 alias o rhs; 898 } 899 static if(op == "+") { 900 alias PyNumber_InPlaceAdd Op; 901 }else static if(op == "-") { 902 alias PyNumber_InPlaceSubtract Op; 903 }else static if(op == "*") { 904 alias PyNumber_InPlaceMultiply Op; 905 }else static if(op == "/") { 906 version(Python_3_0_Or_Later) { 907 alias PyNumber_InPlaceTrueDivide Op; 908 }else{ 909 alias PyNumber_InPlaceDivide Op; 910 } 911 }else static if(op == "%") { 912 alias PyNumber_InPlaceRemainder Op; 913 }else static if(op == "^^") { 914 alias PyNumber_InPlacePower Op; 915 }else static if(op == "<<") { 916 alias PyNumber_InPlaceLshift Op; 917 }else static if(op == ">>") { 918 alias PyNumber_InPlaceRshift Op; 919 }else static if(op == "&") { 920 alias PyNumber_InPlaceAnd Op; 921 }else static if(op == "^") { 922 alias PyNumber_InPlaceXor Op; 923 }else static if(op == "|") { 924 alias PyNumber_InPlaceOr Op; 925 }else static if(op == "~") { 926 alias PySequence_InPlaceConcat Op; 927 }else static assert(false, "operator " ~ op ~" not supported"); 928 929 //EMN: not seeming to be working the way we want it 930 /+ 931 if (PyType_HasFeature(m_ptr.ob_type, Py_TPFLAGS_HAVE_INPLACEOPS)) { 932 Op(m_ptr, count); 933 handle_exception(); 934 } else { 935 +/ 936 PyObject* result = Op(m_ptr, rhs.m_ptr); 937 if (result is null) handle_exception(); 938 Py_DECREF(m_ptr); 939 m_ptr = result; 940 //} 941 return this; 942 } 943 944 //----------------- 945 // Type conversion 946 //----------------- 947 version(Python_3_0_Or_Later) { 948 }else{ 949 /// Converts any Python number to int. 950 PydObject as_int() { 951 return new PydObject(PyNumber_Int(m_ptr)); 952 } 953 } 954 /// Converts any Python number to long. 955 PydObject as_long() { 956 return new PydObject(PyNumber_Long(m_ptr)); 957 } 958 /// Converts any Python number to float. 959 PydObject as_float() { 960 return new PydObject(PyNumber_Float(m_ptr)); 961 } 962 963 //------------------ 964 // Sequence methods 965 //------------------ 966 967 // Sequence concatenation 968 // see opBinary, opOpAssign 969 970 /// Equivalent to 'this.count(v)' in Python. 971 Py_ssize_t count(PydObject v) { 972 if(PySequence_Check(m_ptr)) { 973 Py_ssize_t result = PySequence_Count(m_ptr, v.m_ptr); 974 if (result == -1) handle_exception(); 975 return result; 976 }else { 977 return this.method("count", v).to_d!Py_ssize_t(); 978 } 979 } 980 /// Equivalent to 'this.index(v)' in Python 981 Py_ssize_t index(PydObject v) { 982 if(PySequence_Check(m_ptr)) { 983 Py_ssize_t result = PySequence_Index(m_ptr, v.m_ptr); 984 if (result == -1) handle_exception(); 985 return result; 986 }else { 987 return this.method("index", v).to_d!Py_ssize_t(); 988 } 989 } 990 /// Converts any iterable PydObject to a list 991 PydObject as_list() { 992 return new PydObject(PySequence_List(m_ptr)); 993 } 994 /// Converts any iterable PydObject to a tuple 995 PydObject as_tuple() { 996 return new PydObject(PySequence_Tuple(m_ptr)); 997 } 998 // Added by list: 999 /// Equivalent to 'this._insert(i,item)' in python. 1000 void insert(int i, PydObject item) { 1001 if(PyList_Check(m_ptr)) { 1002 if(PyList_Insert(m_ptr, i, item.m_ptr) == -1) { 1003 handle_exception(); 1004 } 1005 }else{ 1006 this.method("insert")(i,item); 1007 } 1008 } 1009 // Added by list: 1010 /// Equivalent to 'this._append(item)' in python. 1011 void append(PydObject item) { 1012 if(PyList_Check(m_ptr)) { 1013 if(PyList_Append(m_ptr, item.m_ptr) == -1) { 1014 handle_exception(); 1015 } 1016 }else{ 1017 this.method("append", item); 1018 } 1019 } 1020 // Added by list: 1021 /// Equivalent to 'this._sort()' in Python. 1022 void sort() { 1023 if(PyList_Check(m_ptr)) { 1024 if(PyList_Sort(m_ptr) == -1) { 1025 handle_exception(); 1026 } 1027 }else{ 1028 this.method("sort"); 1029 } 1030 } 1031 // Added by list: 1032 /// Equivalent to 'this.reverse()' in Python. 1033 void reverse() { 1034 if(PyList_Check(m_ptr)) { 1035 if(PyList_Reverse(m_ptr) == -1) { 1036 handle_exception(); 1037 } 1038 }else{ 1039 this.method("reverse"); 1040 } 1041 } 1042 1043 //----------------- 1044 // Mapping methods 1045 //----------------- 1046 /// Equivalent to "v in this" in Python. 1047 bool opBinaryRight(string op,T)(T v) if(op == "in" && is(T == PydObject)){ 1048 int result = PySequence_Contains(m_ptr, v.m_ptr); 1049 if (result == -1) handle_exception(); 1050 return result == 1; 1051 } 1052 /// ditto 1053 bool opBinaryRight(string op,T)(T key) if(op == "in" && is(T == string)){ 1054 if(!PySequence_Check(m_ptr) && (PyDict_Check(m_ptr) || PyMapping_Check(m_ptr))) { 1055 return this.has_key(key); 1056 }else{ 1057 PydObject v = py(key); 1058 int result = PySequence_Contains(m_ptr, v.m_ptr); 1059 if (result == -1) handle_exception(); 1060 return result == 1; 1061 } 1062 } 1063 /// Equivalent to 'key in this' in Python. 1064 bool has_key(string key) { 1065 int result = PyMapping_HasKeyString(m_ptr, zc(key)); 1066 if (result == -1) handle_exception(); 1067 return result == 1; 1068 } 1069 /// ditto 1070 bool has_key(PydObject key) { 1071 return this.opBinaryRight!("in",PydObject)(key); 1072 } 1073 /// Equivalent to 'this._keys()' in Python. 1074 PydObject keys() { 1075 // wtf? PyMapping_Keys fails on dicts 1076 if(PyDict_Check(m_ptr)) { 1077 return new PydObject(PyDict_Keys(m_ptr)); 1078 }else if(PyMapping_Keys(m_ptr)) { 1079 return new PydObject(PyMapping_Keys(m_ptr)); 1080 }else{ 1081 return this.method("keys"); 1082 } 1083 } 1084 /// Equivalent to 'this._values()' in Python. 1085 PydObject values() { 1086 // wtf? PyMapping_Values fails on dicts 1087 if(PyDict_Check(m_ptr)) { 1088 return new PydObject(PyDict_Values(m_ptr)); 1089 }else if(PyMapping_Check(m_ptr)) { 1090 return new PydObject(PyMapping_Values(m_ptr)); 1091 }else{ 1092 return this.method("values"); 1093 } 1094 } 1095 /// Equivalent to 'this._items()' in Python. 1096 PydObject items() { 1097 // wtf? PyMapping_Items fails on dicts 1098 if(PyDict_Check(m_ptr)) { 1099 return new PydObject(PyDict_Items(m_ptr)); 1100 }else if(PyMapping_Check(m_ptr)) { 1101 return new PydObject(PyMapping_Items(m_ptr)); 1102 }else { 1103 return this.method("items"); 1104 } 1105 } 1106 1107 // Added by dict 1108 /// For dicts, wraps PyDict_Clear. Otherwise forwards to method. 1109 /// See_Also: 1110 /// <a href="http://docs.python.org/c-api/dict.html#PyDict_Clear"> 1111 /// PyDict_Clear </a> 1112 void clear() { 1113 if(PyDict_Check(m_ptr)) { 1114 PyDict_Clear(m_ptr); 1115 }else{ 1116 this.method("clear"); 1117 } 1118 } 1119 1120 // Added by dict 1121 /// For dicts, wraps PyDict_Copy. Otherwise forwards to method. 1122 /// See_Also: 1123 /// <a href="http://docs.python.org/c-api/dict.html#PyDict_Copy"> 1124 /// PyDict_Copy </a> 1125 PydObject copy() { 1126 if(PyDict_Check(m_ptr)) { 1127 return new PydObject(PyDict_Copy(m_ptr)); 1128 }else{ 1129 return this.method("copy"); 1130 } 1131 } 1132 1133 // Added by dict 1134 /// For dicts, wraps PyDict_Merge. Otherwise forwards to method. 1135 /// See_Also: 1136 /// <a href="http://docs.python.org/c-api/dict.html#PyDict_Merge"> 1137 /// PyDict_Merge </a> 1138 void merge(PydObject o, bool override_=true) { 1139 if(PyDict_Check(m_ptr)) { 1140 int res = PyDict_Merge(m_ptr,o.m_ptr,override_); 1141 if(res == -1) handle_exception(); 1142 }else{ 1143 this.method("merge", o, override_); 1144 } 1145 } 1146 1147 1148 // Added by module 1149 /// For module objects, wraps PyModule_GetDict (essentially a dir() 1150 /// operation in Python). Otherwise forwards to method. 1151 /// See_Also: 1152 /// <a href="http://docs.python.org/c-api/module.html#PyModule_GetDict"> 1153 /// PyModule_GetDict </a> 1154 PydObject getdict() { 1155 if(PyModule_Check(m_ptr)) { 1156 return new PydObject(PyModule_GetDict(m_ptr)); 1157 }else{ 1158 return this.method("getdict"); 1159 } 1160 } 1161 1162 /// Forwards to getattr 1163 @property auto opDispatch(string nom)() if(nom != "popFront") { 1164 return this.getattr(nom); 1165 } 1166 /// Forwards to setattr 1167 @property void opDispatch(string nom, T)(T val) { 1168 static if(is(T == PydObject)) { 1169 alias val value; 1170 }else{ 1171 auto value = py(val); 1172 } 1173 this.setattr(nom,value); 1174 } 1175 /// Forwards to method. 1176 auto opDispatch(string nom, T...)(T ts) if(nom != "popFront") { 1177 return this.getattr(nom).opCall(ts); 1178 } 1179 1180 } 1181 1182 /// Convenience wrapper for Py_None 1183 @property PydObject None() { 1184 static PydObject _None; 1185 enforce(Py_IsInitialized()); 1186 if(!_None) _None = new PydObject(); 1187 return _None; 1188 } 1189 1190 /** 1191 Wrap a python iterator in a D input range. 1192 Params: 1193 E = element type of this range. converts elements of iterator to E. 1194 */ 1195 struct PydInputRange(E = PydObject) { 1196 PyObject* iter; 1197 PyObject* _front = null; 1198 /// _ 1199 this(PyObject* obj) { 1200 iter = PyObject_GetIter(obj); 1201 if (iter is null) { 1202 handle_exception(); 1203 } 1204 popFront(); 1205 } 1206 /// _ 1207 this(Borrowed!PyObject* bobj) { 1208 PyObject* obj = Py_INCREF(bobj); 1209 iter = PyObject_GetIter(obj); 1210 if (iter is null) { 1211 handle_exception(); 1212 } 1213 popFront(); 1214 } 1215 this(this) { 1216 Py_INCREF(iter); 1217 } 1218 ~this() { 1219 Py_XDECREF(iter); 1220 } 1221 1222 /// _ 1223 @property front() { 1224 return python_to_d!E(_front); 1225 } 1226 1227 /// _ 1228 @property empty() { 1229 return _front is null; 1230 } 1231 1232 /// _ 1233 void popFront() { 1234 Py_XDECREF(_front); 1235 _front = PyIter_Next(iter); 1236 } 1237 1238 } 1239