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