root/trunk/infrastructure/pyd/pydobject.d

Revision 100, 23.9 kB (checked in by KirkMcDonald, 1 year ago)

* Class wrapping API replaced.
* Inheritance "shim" classes now automatically generated.
* Requires DMD 1.005 or newer. Breaks GDC support for the moment.
* Docs not yet updated to reflect changes.
* Copyright notices updated to 2007.

Line 
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 module pyd.pydobject;
23
24 //private import std.c.stdio;
25 import python;
26 import pyd.exception;
27 import pyd.make_object;
28 //import std.string;
29
30 /**
31  * Wrapper class for a Python/C API PyObject.
32  *
33  * Nearly all of these member functions may throw a PythonException if the
34  * underlying Python API raises a Python exception.
35  *
36  * Authors: $(LINK2 mailto:kirklin.mcdonald@gmail.com, Kirk McDonald)
37  * Date: June 18, 2006
38  * See_Also:
39  *     $(LINK2 http://docs.python.org/api/api.html, The Python/C API)
40  */
41 class PydObject {
42 protected:
43     PyObject* m_ptr;
44 public:
45     /**
46      * Wrap around a passed PyObject*.
47      * Params:
48      *      o = The PyObject to wrap.
49      *      borrowed = Whether o is a _borrowed reference. Instances
50      *                 of PydObject always own their references.
51      *                 Therefore, Py_INCREF will be called if borrowed is
52      *                 $(D_KEYWORD true).
53      */
54     this(PyObject* o, bool borrowed=false) {
55         if (o is null) handle_exception();
56         // PydObject always owns its references
57         if (borrowed) Py_INCREF(o);
58         m_ptr = o;
59     }
60
61     /// The default constructor constructs an instance of the Py_None PydObject.
62     this() { this(Py_None, true); }
63
64     /// Destructor. Calls Py_DECREF on owned PyObject reference.
65     ~this() {
66         Py_DECREF(m_ptr);
67     }
68
69     /**
70      * Returns a borrowed reference to the PyObject.
71      */
72     PyObject* ptr() { return m_ptr; }
73    
74     /*
75      * Prints PyObject to a C FILE* object.
76      * Params:
77      *      fp = The file object to _print to. std.c.stdio.stdout by default.
78      *      raw = If $(D_KEYWORD true), prints the "str" representation of the
79      *            PydObject, and uses the "repr" otherwise. Defaults to
80      *            $(D_KEYWORD false).
81      * Bugs: This does not seem to work, raising an AccessViolation. Meh.
82      *       Use toString.
83      */
84     /+
85     void print(FILE* fp=stdout, bool raw=false) {
86         if (PyObject_Print(m_ptr, fp, raw ? Py_PRINT_RAW : 0) == -1)
87             handle_exception();
88     }
89     +/
90
91     /// Same as _hasattr(this, attr_name) in Python.
92     bool hasattr(char[] attr_name) {
93         return PyObject_HasAttrString(m_ptr, (attr_name ~ \0).ptr) == 1;
94     }
95
96     /// Same as _hasattr(this, attr_name) in Python.
97     bool hasattr(PydObject attr_name) {
98         return PyObject_HasAttr(m_ptr, attr_name.m_ptr) == 1;
99     }
100
101     /// Same as _getattr(this, attr_name) in Python.
102     PydObject getattr(char[] attr_name) {
103         return new PydObject(PyObject_GetAttrString(m_ptr, (attr_name ~ \0).ptr));
104     }
105
106     /// Same as _getattr(this, attr_name) in Python.
107     PydObject getattr(PydObject attr_name) {
108         return new PydObject(PyObject_GetAttr(m_ptr, attr_name.m_ptr));
109     }
110
111     /**
112      * Same as _setattr(this, attr_name, v) in Python.
113      */
114     void setattr(char[] attr_name, PydObject v) {
115         if (PyObject_SetAttrString(m_ptr, (attr_name ~ \0).ptr, v.m_ptr) == -1)
116             handle_exception();
117     }
118
119     /**
120      * Same as _setattr(this, attr_name, v) in Python.
121      */
122     void setattr(PydObject attr_name, PydObject v) {
123         if (PyObject_SetAttr(m_ptr, attr_name.m_ptr, v.m_ptr) == -1)
124             handle_exception();
125     }
126
127     /**
128      * Same as del this.attr_name in Python.
129      */
130     void delattr(char[] attr_name) {
131         if (PyObject_DelAttrString(m_ptr, (attr_name ~ \0).ptr) == -1)
132             handle_exception();
133     }
134
135     /**
136      * Same as del this.attr_name in Python.
137      */
138     void delattr(PydObject attr_name) {
139         if (PyObject_DelAttr(m_ptr, attr_name.m_ptr) == -1)
140             handle_exception();
141     }
142
143     /**
144      * Exposes Python object comparison to D. Same as cmp(this, rhs) in Python.
145      */
146     int opCmp(PydObject rhs) {
147         // This function happily maps exactly to opCmp
148         int res = PyObject_Compare(m_ptr, rhs.m_ptr);
149         // Check for possible error
150         handle_exception();
151         return res;
152     }
153
154     /**
155      * Exposes Python object equality check to D.
156      */
157     bool opEquals(PydObject rhs) {
158         int res = PyObject_Compare(m_ptr, rhs.m_ptr);
159         handle_exception();
160         return res == 0;
161     }
162    
163     /// Same as _repr(this) in Python.
164     PydObject repr() {
165         return new PydObject(PyObject_Repr(m_ptr));
166     }
167
168     /// Same as _str(this) in Python.
169     PydObject str() {
170         return new PydObject(PyObject_Str(m_ptr));
171     }
172     version (Tango) {
173         /// Allows PydObject to be formatted.
174         char[] toUtf8() {
175             return d_type!(char[])(m_ptr);
176         }
177     } else {
178         /// Allows use of PydObject in writef via %s
179         char[] toString() {
180             return d_type!(char[])(m_ptr);
181         }
182     }
183    
184     /// Same as _unicode(this) in Python.
185     PydObject unicode() {
186         return new PydObject(PyObject_Unicode(m_ptr));
187     }
188
189     /// Same as isinstance(this, cls) in Python.
190     bool isInstance(PydObject cls) {
191         int res = PyObject_IsInstance(m_ptr, cls.m_ptr);
192         if (res == -1) handle_exception();
193         return res == 1;
194     }
195
196     /// Same as issubclass(this, cls) in Python. Only works if this is a class.
197     bool isSubclass(PydObject cls) {
198         int res = PyObject_IsSubclass(m_ptr, cls.m_ptr);
199         if (res == -1) handle_exception();
200         return res == 1;
201     }
202
203     /// Same as _callable(this) in Python.
204     bool callable() {
205         return PyCallable_Check(m_ptr) == 1;
206     }
207    
208     /**
209      * Calls the PydObject with the PyTuple args.
210      * Params:
211      *      args = Should be a PydTuple of the arguments to pass. Omit to
212      *             call with no arguments.
213      * Returns: Whatever the function PydObject returns.
214      */
215     PydObject unpackCall(PydObject args=null) {
216         return new PydObject(PyObject_CallObject(m_ptr, args is null ? null : args.m_ptr));
217     }
218    
219     /**
220      * Calls the PydObject with positional and keyword arguments.
221      * Params:
222      *      args = Positional arguments. Should be a PydTuple. Pass an empty
223      *             PydTuple for no positional arguments.
224      *      kw = Keyword arguments. Should be a PydDict.
225      * Returns: Whatever the function PydObject returns.
226      */
227     PydObject unpackCall(PydObject args, PydObject kw) {
228         return new PydObject(PyObject_Call(m_ptr, args.m_ptr, kw.m_ptr));
229     }
230
231     /**
232      * Calls the PydObject with any convertible D items.
233      */
234     PydObject opCall(T ...) (T t) {
235         PyObject* tuple = PyTuple_FromItems(t);
236         if (tuple is null) handle_exception();
237         PyObject* result = PyObject_CallObject(m_ptr, tuple);
238         Py_DECREF(tuple);
239         if (result is null) handle_exception();
240         return new PydObject(result);
241     }
242
243     /**
244      *
245      */
246     PydObject methodUnpack(char[] name, PydObject args=null) {
247         // Get the method PydObject
248         PyObject* m = PyObject_GetAttrString(m_ptr, (name ~ \0).ptr);
249         PyObject* result;
250         // If this method doesn't exist (or other error), throw exception
251         if (m is null) handle_exception();
252         // Call the method, and decrement the refcounts on the temporaries.
253         result = PyObject_CallObject(m, args is null ? null : args.m_ptr);
254         Py_DECREF(m);
255         // Return the result.
256         return new PydObject(result);
257     }
258
259     PydObject methodUnpack(char[] name, PydObject args, PydObject kw) {
260         // Get the method PydObject
261         PyObject* m = PyObject_GetAttrString(m_ptr, (name ~ \0).ptr);
262         PyObject* result;
263         // If this method doesn't exist (or other error), throw exception.
264         if (m is null) handle_exception();
265         // Call the method, and decrement the refcounts on the temporaries.
266         result = PyObject_Call(m, args.m_ptr, kw.m_ptr);
267         Py_DECREF(m);
268         // Return the result.
269         return new PydObject(result);
270     }
271
272     /**
273      * Calls a method of the object with any convertible D items.
274      */
275     PydObject method(T ...) (char[] name, T t) {
276         PyObject* mthd = PyObject_GetAttrString(m_ptr, (name ~ \0).ptr);
277         if (mthd is null) handle_exception();
278         PyObject* tuple = PyTuple_FromItems(t);
279         if (tuple is null) {
280             Py_DECREF(mthd);
281             handle_exception();
282         }
283         PyObject* result = PyObject_CallObject(mthd, tuple);
284         Py_DECREF(mthd);
285         Py_DECREF(tuple);
286         if (result is null) handle_exception();
287         return new PydObject(result);
288     }
289
290     /// Same as _hash(this) in Python.
291     int hash() {
292         int res = PyObject_Hash(m_ptr);
293         if (res == -1) handle_exception();
294         return res;
295     }
296
297     T toDItem(T)() {
298         return d_type!(T)(m_ptr);
299     }
300
301     /// Same as "not not this" in Python.
302     bool toBool() {
303         return d_type!(bool)(m_ptr);
304     }
305
306     /// Same as "_not this" in Python.
307     bool not() {
308         int res = PyObject_Not(m_ptr);
309         if (res == -1) handle_exception();
310         return res == 1;
311     }
312
313     /**
314      * Gets the _type of this PydObject. Same as _type(this) in Python.
315      * Returns: The _type PydObject of this PydObject.
316      */
317     PydObject type() {
318         return new PydObject(PyObject_Type(m_ptr));
319     }
320
321     /**
322      * The _length of this PydObject. Same as _len(this) in Python.
323      */
324     int length() {
325         int res = PyObject_Length(m_ptr);
326         if (res == -1) handle_exception();
327         return res;
328     }
329     /// Same as length()
330     int size() { return length(); }
331
332     /// Same as _dir(this) in Python.
333     PydObject dir() {
334         return new PydObject(PyObject_Dir(m_ptr));
335     }
336
337     //----------
338     // Indexing
339     //----------
340     /// Equivalent to o[_key] in Python.
341     PydObject opIndex(PydObject key) {
342         return new PydObject(PyObject_GetItem(m_ptr, key.m_ptr));
343     }
344     /**
345      * Equivalent to o['_key'] in Python; usually only makes sense for
346      * mappings.
347      */
348     PydObject opIndex(char[] key) {
349         return new PydObject(PyMapping_GetItemString(m_ptr, (key ~ \0).ptr));
350     }
351     /// Equivalent to o[_i] in Python; usually only makes sense for sequences.
352     PydObject opIndex(int i) {
353         return new PydObject(PySequence_GetItem(m_ptr, i));
354     }
355
356     /// Equivalent to o[_key] = _value in Python.
357     void opIndexAssign(PydObject value, PydObject key) {
358         if (PyObject_SetItem(m_ptr, key.m_ptr, value.m_ptr) == -1)
359             handle_exception();
360     }
361     /**
362      * Equivalent to o['_key'] = _value in Python. Usually only makes sense for
363      * mappings.
364      */
365     void opIndexAssign(PydObject value, char[] key) {
366         if (PyMapping_SetItemString(m_ptr, (key ~ \0).ptr, value.m_ptr) == -1)
367             handle_exception();
368     }
369     /**
370      * Equivalent to o[_i] = _value in Python. Usually only makes sense for
371      * sequences.
372      */
373     void opIndexAssign(PydObject value, int i) {
374         if (PySequence_SetItem(m_ptr, i, value.m_ptr) == -1)
375             handle_exception();
376     }
377
378     /// Equivalent to del o[_key] in Python.
379     void delItem(PydObject key) {
380         if (PyObject_DelItem(m_ptr, key.m_ptr) == -1)
381             handle_exception();
382     }
383     /**
384      * Equivalent to del o['_key'] in Python. Usually only makes sense for
385      * mappings.
386      */
387     void delItem(char[] key) {
388         if (PyMapping_DelItemString(m_ptr, (key ~ \0).ptr) == -1)
389             handle_exception();
390     }
391     /**
392      * Equivalent to del o[_i] in Python. Usually only makes sense for
393      * sequences.
394      */
395     void delItem(int i) {
396         if (PySequence_DelItem(m_ptr, i) == -1)
397             handle_exception();
398     }
399
400     //---------
401     // Slicing
402     //---------
403     /// Equivalent to o[_i1:_i2] in Python.
404     PydObject opSlice(int i1, int i2) {
405         return new PydObject(PySequence_GetSlice(m_ptr, i1, i2));
406     }
407     /// Equivalent to o[:] in Python.
408     PydObject opSlice() {
409         return this.opSlice(0, this.length());
410     }
411     /// Equivalent to o[_i1:_i2] = _v in Python.
412     void opSliceAssign(PydObject v, int i1, int i2) {
413         if (PySequence_SetSlice(m_ptr, i1, i1, v.m_ptr) == -1)
414             handle_exception();
415     }
416     /// Equivalent to o[:] = _v in Python.
417     void opSliceAssign(PydObject v) {
418         this.opSliceAssign(v, 0, this.length());
419     }
420     /// Equivalent to del o[_i1:_i2] in Python.
421     void delSlice(int i1, int i2) {
422         if (PySequence_DelSlice(m_ptr, i1, i2) == -1)
423             handle_exception();
424     }
425     /// Equivalent to del o[:] in Python.
426     void delSlice() {
427         this.delSlice(0, this.length());
428     }
429
430     //-----------
431     // Iteration
432     //-----------
433
434     /**
435      * Iterates over the items in a collection, be they the items in a
436      * sequence, keys in a dictionary, or some other iteration defined for the
437      * PydObject's type.
438      */
439     int opApply(int delegate(inout PydObject) dg) {
440         PyObject* iterator = PyObject_GetIter(m_ptr);
441         PyObject* item;
442         int result = 0;
443         PydObject o;
444
445         if (iterator == null) {
446             handle_exception();
447         }
448
449         item = PyIter_Next(iterator);
450         while (item) {
451             o = new PydObject(item);
452             result = dg(o);
453             Py_DECREF(item);
454             if (result) break;
455             item = PyIter_Next(iterator);
456         }
457         Py_DECREF(iterator);
458
459         // Just in case an exception occured
460         handle_exception();
461
462         return result;
463     }
464
465     /**
466      * Iterate over (key, value) pairs in a dictionary. If the PydObject is not
467      * a dict, this simply does nothing. (It iterates over no items.) You
468      * should not attempt to modify the dictionary while iterating through it,
469      * with the exception of modifying values. Adding or removing items while
470      * iterating through it is an especially bad idea.
471      */
472     int opApply(int delegate(inout PydObject, inout PydObject) dg) {
473         PyObject* key, value;
474         version(Python_2_5_Or_Later) {
475             Py_ssize_t pos = 0;
476         } else {
477             int pos = 0;
478         }
479         int result = 0;
480         PydObject k, v;
481
482         while (PyDict_Next(m_ptr, &pos, &key, &value)) {
483             k = new PydObject(key, true);
484             v = new PydObject(value, true);
485             result = dg(k, v);
486             if (result) break;
487         }
488
489         return result;
490     }
491
492     //------------
493     // Arithmetic
494     //------------
495     ///
496     PydObject opAdd(PydObject o) {
497         return new PydObject(PyNumber_Add(m_ptr, o.m_ptr));
498     }
499     ///
500     PydObject opSub(PydObject o) {
501         return new PydObject(PyNumber_Subtract(m_ptr, o.m_ptr));
502     }
503     ///
504     PydObject opMul(PydObject o) {
505         return new PydObject(PyNumber_Multiply(m_ptr, o.m_ptr));
506     }
507     /// Sequence repetition
508     PydObject opMul(int count) {
509         return new PydObject(PySequence_Repeat(m_ptr, count));
510     }
511     ///
512     PydObject opDiv(PydObject o) {
513         return new PydObject(PyNumber_Divide(m_ptr, o.m_ptr));
514     }
515     ///
516     PydObject floorDiv(PydObject o) {
517         return new PydObject(PyNumber_FloorDivide(m_ptr, o.m_ptr));
518     }
519     ///
520     PydObject opMod(PydObject o) {
521         return new PydObject(PyNumber_Remainder(m_ptr, o.m_ptr));
522     }
523     ///
524     PydObject divmod(PydObject o) {
525         return new PydObject(PyNumber_Divmod(m_ptr, o.m_ptr));
526     }
527     ///
528     PydObject pow(PydObject o1, PydObject o2=null) {
529         return new PydObject(PyNumber_Power(m_ptr, o1.m_ptr, (o2 is null) ? Py_None : o2.m_ptr));
530     }
531     ///
532     PydObject opPos() {
533         return new PydObject(PyNumber_Positive(m_ptr));
534     }
535     ///
536     PydObject opNeg() {
537         return new PydObject(PyNumber_Negative(m_ptr));
538     }
539     ///
540     PydObject abs() {
541         return new PydObject(PyNumber_Absolute(m_ptr));
542     }
543     ///
544     PydObject opCom() {
545         return new PydObject(PyNumber_Invert(m_ptr));
546     }
547     ///
548     PydObject opShl(PydObject o) {
549         return new PydObject(PyNumber_Lshift(m_ptr, o.m_ptr));
550     }
551     ///
552     PydObject opShr(PydObject o) {
553         return new PydObject(PyNumber_Rshift(m_ptr, o.m_ptr));
554     }
555     ///
556     PydObject opAnd(PydObject o) {
557         return new PydObject(PyNumber_And(m_ptr, o.m_ptr));
558     }
559     ///
560     PydObject opXor(PydObject o) {
561         return new PydObject(PyNumber_Xor(m_ptr, o.m_ptr));
562     }
563     ///
564     PydObject opOr(PydObject o) {
565         return new PydObject(PyNumber_Or(m_ptr, o.m_ptr));
566     }
567
568     //---------------------
569     // In-place arithmetic
570     //---------------------
571     private extern(C)
572     alias PyObject* function(PyObject*, PyObject*) op_t;
573
574     // A useful wrapper for most of the in-place operators
575     private PydObject
576     inplace(op_t op, PydObject rhs) {
577         if (PyType_HasFeature(m_ptr.ob_type, Py_TPFLAGS_HAVE_INPLACEOPS)) {
578             op(m_ptr, rhs.m_ptr);
579             handle_exception();
580         } else {
581             PyObject* result = op(m_ptr, rhs.m_ptr);
582             if (result is null) handle_exception();
583             Py_DECREF(m_ptr);
584             m_ptr = result;
585         }
586         return this;
587     }
588     ///
589     PydObject opAddAssign(PydObject o) {
590         return inplace(&PyNumber_InPlaceAdd, o);
591     }
592     ///
593     PydObject opSubAssign(PydObject o) {
594         return inplace(&PyNumber_InPlaceSubtract, o);
595     }
596     ///
597     PydObject opMulAssign(PydObject o) {
598         return inplace(&PyNumber_InPlaceMultiply, o);
599     }
600     /// In-place sequence repetition
601     PydObject opMulAssign(int count) {
602         if (PyType_HasFeature(m_ptr.ob_type, Py_TPFLAGS_HAVE_INPLACEOPS)) {
603             PySequence_InPlaceRepeat(m_ptr, count);
604             handle_exception();
605         } else {
606             PyObject* result = PySequence_InPlaceRepeat(m_ptr, count);
607             if (result is null) handle_exception();
608             Py_DECREF(m_ptr);
609             m_ptr = result;
610         }
611         return this;
612     }
613     ///
614     PydObject opDivAssign(PydObject o) {
615         return inplace(&PyNumber_InPlaceDivide, o);
616     }
617     ///
618     PydObject floorDivAssign(PydObject o) {
619         return inplace(&PyNumber_InPlaceFloorDivide, o);
620     }
621     ///
622     PydObject opModAssign(PydObject o) {
623         return inplace(&PyNumber_InPlaceRemainder, o);
624     }
625     ///
626     PydObject powAssign(PydObject o1, PydObject o2=null) {
627         if (PyType_HasFeature(m_ptr.ob_type, Py_TPFLAGS_HAVE_INPLACEOPS)) {
628             PyNumber_InPlacePower(m_ptr, o1.m_ptr, (o2 is null) ? Py_None : o2.m_ptr);
629             handle_exception();
630         } else {
631             PyObject* result = PyNumber_InPlacePower(m_ptr, o1.m_ptr, (o2 is null) ? Py_None : o2.m_ptr);
632             if (result is null) handle_exception();
633             Py_DECREF(m_ptr);
634             m_ptr = result;
635         }
636         return this;
637     }
638     ///
639     PydObject opShlAssign(PydObject o) {
640         return inplace(&PyNumber_InPlaceLshift, o);
641     }
642     ///
643     PydObject opShrAssign(PydObject o) {
644         return inplace(&PyNumber_InPlaceRshift, o);
645     }
646     /