root/trunk/infrastructure/pyd/make_object.d

Revision 121, 16.5 kB (checked in by KirkMcDonald, 1 year ago)

Since the D 2.0 series has been found lacking at the moment, Pyd once again compiles under the 1.0 series. (Specifically 1.016 or later; tested with 1.020.)

Line 
1 /*
2 Copyright 2006, 2007 Kirk McDonald
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy of
5 this software and associated documentation files (the "Software"), to deal in
6 the Software without restriction, including without limitation the rights to
7 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 of the Software, and to permit persons to whom the Software is furnished to do
9 so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
21 */
22
23 /**
24  * This module contains some useful type conversion functions. There are two
25  * interesting operations involved here:
26  *
27  * PyObject* -> D type
28  *
29  * D type -> PyObject*
30  *
31  * The former is handled by d_type, the latter by _py. The py function is
32  * provided as a convenience to directly convert a D type into an instance of
33  * PydObject.
34  */
35 module pyd.make_object;
36
37 import python;
38
39 //import std.string;
40 //import std.stdio;
41
42 import pyd.pydobject;
43 import pyd.class_wrap;
44 import pyd.func_wrap;
45 import pyd.exception;
46 import pyd.lib_abstract :
47     objToStr,
48     toString,
49     ParameterTypeTuple,
50     ReturnType
51 ;
52
53 package template isArray(T) {
54     const bool isArray = is(typeof(T.init[0])[] == T);
55 }
56
57 // This relies on the fact that, for a static array type T,
58 //      typeof(T.init) != T
59 // But, rather, T.init is the type it is an array of. (For the dynamic array
60 // template above, this type is extracted with typeof(T.init[0])).
61 // Because this is only true for static arrays, it would work just as well to
62 // say "!is(typeof(T.init) == T)"; however, this template has the advantage of
63 // being easily fixable should this behavior for static arrays change.
64 package template isStaticArray(T) {
65     const bool isStaticArray = is(typeof(T.init)[(T).sizeof / typeof(T.init).sizeof] == T);
66 }
67
68 package template isAA(T) {
69     const bool isAA = is(typeof(T.init.values[0])[typeof(T.init.keys[0])] == T);
70 }
71
72 class to_conversion_wrapper(dg_t) {
73     alias ParameterTypeTuple!(dg_t)[0] T;
74     alias ReturnType!(dg_t) Intermediate;
75     dg_t dg;
76     this(dg_t fn) { dg = fn; }
77     PyObject* opCall(T t) {
78         static if (is(Intermediate == PyObject*)) {
79             return dg(t);
80         } else {
81             return _py(dg(t));
82         }
83     }
84 }
85 class from_conversion_wrapper(dg_t) {
86     alias ParameterTypeTuple!(dg_t)[0] Intermediate;
87     alias ReturnType!(dg_t) T;
88     dg_t dg;
89     this(dg_t fn) { dg = fn; }
90     T opCall(PyObject* o) {
91         static if (is(Intermediate == PyObject*)) {
92             return dg(o);
93         } else {
94             return dg(d_type!(Intermediate)(o));
95         }
96     }
97 }
98
99 template to_converter_registry(From) {
100     PyObject* delegate(From) dg=null;
101 }
102 template from_converter_registry(To) {
103     To delegate(PyObject*) dg=null;
104 }
105
106 void d_to_python(dg_t) (dg_t dg) {
107     static if (is(dg_t == delegate) && is(ReturnType!(dg_t) == PyObject*)) {
108         to_converter_registry!(ParameterTypeTuple!(dg_t)[0]).dg = dg;
109     } else {
110         auto o = new to_conversion_wrapper!(dg_t)(dg);
111         to_converter_registry!(typeof(o).T).dg = &o.opCall;
112     }
113 }
114 void python_to_d(dg_t) (dg_t dg) {
115     static if (is(dg_t == delegate) && is(ParameterTypeTuple!(dg_t)[0] == PyObject*)) {
116         from_converter_registry!(ReturnType!(dg_t)).dg = dg;
117     } else {
118         auto o = new from_conversion_wrapper!(dg_t)(dg);
119         from_converter_registry!(typeof(o).T).dg = &o.opCall;
120     }
121 }
122
123 /**
124  * Returns a new (owned) reference to a Python object based on the passed
125  * argument. If the passed argument is a PyObject*, this "steals" the
126  * reference. (In other words, it returns the PyObject* without changing its
127  * reference count.) If the passed argument is a PydObject, this returns a new
128  * reference to whatever the PydObject holds a reference to.
129  *
130  * If the passed argument can't be converted to a PyObject, a Python
131  * RuntimeError will be raised and this function will return null.
132  */
133 PyObject* _py(T) (T t) {
134     static if (!is(T == PyObject*) && is(typeof(t is null))) {
135         if (t is null) {
136             Py_INCREF(Py_None);
137             return Py_None;
138         }
139     }
140     static if (is(T : bool)) {
141         PyObject* temp = (t) ? Py_True : Py_False;
142         Py_INCREF(temp);
143         return temp;
144     } else static if (is(T : C_long)) {
145         return PyInt_FromLong(t);
146     } else static if (is(T : C_longlong)) {
147         return PyLong_FromLongLong(t);
148     } else static if (is(T : double)) {
149         return PyFloat_FromDouble(t);
150     } else static if (is(T : idouble)) {
151         return PyComplex_FromDoubles(0.0, t.im);
152     } else static if (is(T : cdouble)) {
153         return PyComplex_FromDoubles(t.re, t.im);
154     } else static if (is(T : string)) {
155         return PyString_FromString((t ~ \0).ptr);
156     } else static if (is(T : wchar[])) {
157         return PyUnicode_FromWideChar(t, t.length);
158     // Converts any array (static or dynamic) to a Python list
159     } else static if (isArray!(T) || isStaticArray!(T)) {
160         PyObject* lst = PyList_New(t.length);
161         PyObject* temp;
162         if (lst is null) return null;
163         for(int i=0; i<t.length; ++i) {
164             temp = _py(t[i]);
165             if (temp is null) {
166                 Py_DECREF(lst);
167                 return null;
168             }
169             // Steals the reference to temp
170             PyList_SET_ITEM(lst, i, temp);
171         }
172         return lst;
173     // Converts any associative array to a Python dict
174     } else static if (isAA!(T)) {
175         PyObject* dict = PyDict_New();
176         PyObject* ktemp, vtemp;
177         int result;
178         if (dict is null) return null;
179         foreach(k, v; t) {
180             ktemp = _py(k);
181             vtemp = _py(v);
182             if (ktemp is null || vtemp is null) {
183                 if (ktemp !is null) Py_DECREF(ktemp);
184                 if (vtemp !is null) Py_DECREF(vtemp);
185                 Py_DECREF(dict);
186                 return null;
187             }
188             result = PyDict_SetItem(dict, ktemp, vtemp);
189             Py_DECREF(ktemp);
190             Py_DECREF(vtemp);
191             if (result == -1) {
192                 Py_DECREF(dict);
193                 return null;
194             }
195         }
196         return dict;
197     } else static if (is(T == delegate) || is(T == function)) {
198         PydWrappedFunc_Ready!(T)();
199         return WrapPyObject_FromObject(t);
200     } else static if (is(T : PydObject)) {
201         PyObject* temp = t.ptr();
202         Py_INCREF(temp);
203         return temp;
204     // The function expects to be passed a borrowed reference and return an
205     // owned reference. Thus, if passed a PyObject*, this will increment the
206     // reference count.
207     } else static if (is(T : PyObject*)) {
208         Py_INCREF(t);
209         return t;
210     // Convert wrapped type to a PyObject*
211     } else static if (is(T == class)) {
212         // But only if it actually is a wrapped type. :-)
213         PyTypeObject** type = t.classinfo in wrapped_classes;
214         if (type) {
215             return WrapPyObject_FromTypeAndObject(*type, t);
216         }
217         // If it's not a wrapped type, fall through to the exception.
218     // If converting a struct by value, create a copy and wrap that
219     } else static if (is(T == struct)) {
220         if (is_wrapped!(T*)) {
221             T* temp = new T;
222             *temp = t;
223             return WrapPyObject_FromObject(temp);
224         }
225     // If converting a struct by reference, wrap the thing directly
226     } else static if (is(typeof(*t) == struct)) {
227         if (is_wrapped!(T)) {
228             if (t is null) {
229                 Py_INCREF(Py_None);
230                 return Py_None;
231             }
232             return WrapPyObject_FromObject(t);
233         }
234     }
235     // No conversion found, check runtime registry
236     if (to_converter_registry!(T).dg) {
237         return to_converter_registry!(T).dg(t);
238     }
239     PyErr_SetString(PyExc_RuntimeError, ("D conversion function _py failed with type " ~ objToStr(typeid(T))).ptr);
240     return null;
241 }
242
243 /**
244  * Helper function for creating a PyTuple from a series of D items.
245  */
246 PyObject* PyTuple_FromItems(T ...)(T t) {
247     PyObject* tuple = PyTuple_New(t.length);
248     PyObject* temp;
249     if (tuple is null) return null;
250     foreach(i, arg; t) {
251         temp = _py(arg);
252         if (temp is null) {
253             Py_DECREF(tuple);
254             return null;
255         }
256         PyTuple_SetItem(tuple, i, temp);
257     }
258     return tuple;
259 }
260
261 /**
262  * Constructs an object based on the type of the argument passed in.
263  *
264  * For example, calling py(10) would return a PydObject holding the value 10.
265  *
266  * Calling this with a PydObject will return back a reference to the very same
267  * PydObject.
268  */
269 PydObject py(T) (T t) {
270     static if(is(T : PydObject)) {
271         return t;
272     } else {
273         return new PydObject(_py(t));
274     }
275 }
276
277 /**
278  * An exception class used by d_type.
279  */
280 class PydConversionException : Exception {
281     this(string msg) { super(msg); }
282 }
283
284 /**
285  * This converts a PyObject* to a D type. The template argument is the type to
286  * convert to. The function argument is the PyObject* to convert. For instance:
287  *
288  *$(D_CODE PyObject* i = PyInt_FromLong(20);
289  *int n = _d_type!(int)(i);
290  *assert(n == 20);)
291  *
292  * This throws a PydConversionException if the PyObject can't be converted to
293  * the given D type.
294  */
295 T d_type(T) (PyObject* o) {
296     // This ordering is very important. If the check for bool came first,
297     // then all integral types would be converted to bools (they would be
298     // 0 or 1), because bool can be implicitly converted to any integral
299     // type.
300     //
301     // This also means that:
302     //  (1) Conversion to PydObject will construct an object and return that.
303     //  (2) Any integral type smaller than a C_long (which is usually just
304     //      an int, meaning short and byte) will use the bool conversion.
305     //  (3) Conversion to a float shouldn't work.
306     static if (is(PyObject* : T)) {
307         return o;
308     } else static if (is(PydObject : T)) {
309         return new PydObject(o, true);
310     } else static if (is(T == void)) {
311         if (o != Py_None) could_not_convert!(T)(o);
312         Py_INCREF(Py_None);
313         return Py_None;
314     } else static if (is(T == class)) {
315         // We can only convert to a class if it has been wrapped, and of course
316         // we can only convert the object if it is the wrapped type.
317         if (
318             is_wrapped!(T) &&
319             PyObject_IsInstance(o, cast(PyObject*)&wrapped_class_type!(T)) &&
320             cast(T)((cast(wrapped_class_object!(Object)*)o).d_obj) !is null
321         ) {
322             return WrapPyObject_AsObject!(T)(o);
323         }
324         // Otherwise, throw up an exception.
325         //could_not_convert!(T)(o);
326     } else static if (is(T == struct)) { // struct by value
327         if (is_wrapped!(T*) && PyObject_TypeCheck(o, &wrapped_class_type!(T*))) {
328             return *WrapPyObject_AsObject!(T*)(o);
329         }// else could_not_convert!(T)(o);
330     } else static if (is(typeof(*(T.init)) == struct)) { // pointer to struct   
331         if (is_wrapped!(T) && PyObject_TypeCheck(o, &wrapped_class_type!(T))) {
332             return WrapPyObject_AsObject!(T)(o);
333         }// else could_not_convert!(T)(o);
334     } else static if (is(T == delegate)) {
335         // Get the original wrapped delegate out if this is a wrapped delegate
336         if (is_wrapped!(T) && PyObject_TypeCheck(o, &wrapped_class_type!(T))) {
337             return WrapPyObject_AsObject!(T)(o);
338         // Otherwise, wrap the PyCallable with a delegate
339         } else if (PyCallable_Check(o)) {
340             return PydCallable_AsDelegate!(T)(o);
341         }// else could_not_convert!(T)(o);
342     } else static if (is(T == function)) {
343         // We can only make it a function pointer if we originally wrapped a
344         // function pointer.
345         if (is_wrapped!(T) && PyObject_TypeCheck(o, &wrapped_class_type!(T))) {
346             return WrapPyObject_AsObject!(T)(o);
347         }// else could_not_convert!(T)(o);
348     /+
349     } else static if (is(wchar[] : T)) {
350         wchar[] temp;
351         temp.length = PyUnicode_GetSize(o);
352         PyUnicode_AsWideChar(cast(PyUnicodeObject*)o, temp, temp.length);
353         return temp;
354     +/
355     } else static if (is(string : T) || is(char[] : T)) {
356         c_str result;
357         PyObject* repr;
358         // If it's a string, convert it
359         if (PyString_Check(o) || PyUnicode_Check(o)) {
360             result = PyString_AsString(o);
361         // If it's something else, convert its repr
362         } else {
363             repr = PyObject_Repr(o);
364             if (repr is null) handle_exception();
365             result = PyString_AsString(repr);
366             Py_DECREF(repr);
367         }
368         if (result is null) handle_exception();
369         version (D_Version2) {
370             static if (is(string : T)) {
371                 return .toString(result);
372             } else {
373                 return .toString(result).dup;
374             }
375         } else {
376             return .toString(result).dup;
377         }
378     } else static if (is(T E : E[])) {
379         // Dynamic arrays
380         PyObject* iter = PyObject_GetIter(o);
381         if (iter is null) {
382             PyErr_Clear();
383             could_not_convert!(T)(o);
384         }
385         scope(exit) Py_DECREF(iter);
386         int len = PyObject_Length(o);
387         if (len == -1) {
388             PyErr_Clear();
389             could_not_convert!(T)(o);
390         }
391         T array;
392         array.length = len;
393         int i = 0;
394         PyObject* item = PyIter_Next(iter);
395         while (item) {
396             try {
397                 array[i] = d_type!(E)(item);
398             } catch(PydConversionException e) {
399                 Py_DECREF(item);
400                 // We re-throw the original conversion exception, rather than
401                 // complaining about being unable to convert to an array. The
402                 // partially constructed array is left to the GC.
403                 throw e;
404             }
405             ++i;
406             Py_DECREF(item);
407             item = PyIter_Next(iter);
408         }
409         return array;
410     } else static if (is(cdouble : T)) {
411         double real_ = PyComplex_RealAsDouble(o);
412         handle_exception();
413         double imag = PyComplex_ImagAsDouble(o);
414         handle_exception();
415         return real_ + imag * 1i;
416     } else static if (is(double : T)) {
417         double res = PyFloat_AsDouble(o);
418         handle_exception();
419         return res;
420     } else static if (is(C_longlong : T)) {
421         if (!PyNumber_Check(o)) could_not_convert!(T)(o);
422         C_longlong res = PyLong_AsLongLong(o);
423         handle_exception();
424         return res;
425     } else static if (is(C_long : T)) {
426         if (!PyNumber_Check(o)) could_not_convert!(T)(o);
427         C_long res = PyInt_AsLong(o);
428         handle_exception();
429         return res;
430     } else static if (is(bool : T)) {
431         if (!PyNumber_Check(o)) could_not_convert!(T)(o);
432         int res = PyObject_IsTrue(o);
433         handle_exception();
434         return res == 1;
435     }/+ else {
436         could_not_convert!(T)(o);
437     }+/
438     if (from_converter_registry!(T).dg) {
439         return from_converter_registry!(T).dg(o);
440     }
441     could_not_convert!(T)(o);
442 }
443
444 alias d_type!(Object) d_type_Object;
445
446 private
447 void could_not_convert(T) (PyObject* o) {
448     // Pull out the name of the type of this Python object, and the
449     // name of the D type.
450     string py_typename, d_typename;
451     PyObject* py_type, py_type_str;
452     py_type = PyObject_Type(o);
453     if (py_type is null) {
454         py_typename = "<unknown>";
455     } else {
456         py_type_str = PyObject_GetAttrString(py_type, "__name__");
457         Py_DECREF(py_type);
458         if (py_type_str is null) {
459             py_typename = "<unknown>";
460         } else {
461             py_typename = .toString(PyString_AsString(py_type_str));
462             Py_DECREF(py_type_str);
463         }
464     }
465     d_typename = objToStr(typeid(T));
466     throw new PydConversionException(
467         "Couldn't convert Python type '" ~
468         py_typename ~
469         "' to D type '" ~
470         d_typename ~
471         "'"
472     );
473 }
Note: See TracBrowser for help on using the browser.