1 module python.conv.python_to_d; 2 3 4 import python.raw: PyObject, pyListCheck, pyTupleCheck, PyTuple_Size, pyCallableCheck; 5 import python.type: isUserAggregate, isTuple; 6 import std.traits: Unqual, isIntegral, isFloatingPoint, isAggregateType, isArray, 7 isStaticArray, isAssociativeArray, isPointer, PointerTarget, isSomeChar, isSomeString, 8 isDelegate, isFunctionPointer; 9 import std.range: isInputRange; 10 import std.datetime: DateTime, Date; 11 12 13 T to(T)(PyObject* value) @trusted if(isIntegral!T) { 14 import python.raw: PyLong_AsLong; 15 16 const ret = PyLong_AsLong(value); 17 if(ret > T.max || ret < T.min) throw new Exception("Overflow"); 18 19 return cast(T) ret; 20 } 21 22 23 T to(T)(PyObject* value) @trusted if(isFloatingPoint!T) { 24 import python.raw: PyFloat_AsDouble; 25 auto ret = PyFloat_AsDouble(value); 26 return cast(T) ret; 27 } 28 29 30 T to(T)(PyObject* value) @trusted if(isUserAggregate!T) { 31 import python.type: PythonClass, userAggregateInit; 32 33 auto pyclass = cast(PythonClass!T*) value; 34 auto ret = userAggregateInit!(Unqual!T); 35 36 static foreach(i; 0 .. T.tupleof.length) { 37 ret.tupleof[i] = pyclass.getField!i.to!(typeof(T.tupleof[i])); 38 } 39 40 // might need to be cast to `const` or `immutable` 41 return cast(T) ret; 42 } 43 44 45 T to(T)(PyObject* value) if(isPointer!T && isUserAggregate!(PointerTarget!T)) { 46 auto ret = new Unqual!(PointerTarget!T); 47 *ret = to!(PointerTarget!T)(value); 48 return ret; 49 } 50 51 52 T to(T)(PyObject* value) if(is(Unqual!T == DateTime)) { 53 import python.raw; 54 55 return DateTime(pyDateTimeYear(value), 56 pyDateTimeMonth(value), 57 pyDateTimeDay(value), 58 pyDateTimeHour(value), 59 pyDateTimeMinute(value), 60 pyDateTimeSecond(value)); 61 62 } 63 64 65 T to(T)(PyObject* value) if(is(Unqual!T == Date)) { 66 import python.raw; 67 68 return Date(pyDateTimeYear(value), 69 pyDateTimeMonth(value), 70 pyDateTimeDay(value)); 71 } 72 73 74 T to(T)(PyObject* value) if(isArray!T && !isSomeString!T) 75 in(pyListCheck(value) || pyTupleCheck(value)) 76 { 77 import python.raw: PyList_Size, PyList_GetItem, PyTuple_Size, PyTuple_GetItem; 78 import std.range: ElementEncodingType; 79 import std.traits: Unqual; 80 import std.exception: enforce; 81 import std.conv: text; 82 83 Unqual!T ret; 84 85 static if(__traits(compiles, ret.length = 1)) { 86 const valueLength = { 87 if(pyListCheck(value)) 88 return PyList_Size(value); 89 else if(pyTupleCheck(value)) 90 return PyTuple_Size(value); 91 else 92 assert(0); 93 }(); 94 assert(valueLength >= 0, text("Invalid length ", valueLength)); 95 ret.length = valueLength; 96 } 97 98 foreach(i, ref elt; ret) { 99 auto pythonItem = { 100 if(pyListCheck(value)) 101 return PyList_GetItem(value, i); 102 else if(pyTupleCheck(value)) 103 return PyTuple_GetItem(value, i); 104 else 105 assert(0); 106 }(); 107 elt = pythonItem.to!(ElementEncodingType!T); 108 } 109 110 return ret; 111 } 112 113 114 T to(T)(PyObject* value) if(isSomeString!T) { 115 import python.raw: pyUnicodeGetSize, pyUnicodeCheck, 116 pyBytesAsString, pyObjectUnicode, pyUnicodeAsUtf8String, Py_ssize_t; 117 import std.range: ElementEncodingType; 118 import std.conv: to; 119 120 value = pyObjectUnicode(value); 121 122 const length = pyUnicodeGetSize(value); 123 124 auto ptr = pyBytesAsString(pyUnicodeAsUtf8String(value)); 125 assert(length == 0 || ptr !is null); 126 127 auto slice = ptr[0 .. length]; 128 129 return slice.to!T; 130 } 131 132 133 T to(T)(PyObject* value) if(is(Unqual!T == bool)) { 134 import python.raw: pyTrue; 135 return value is pyTrue; 136 } 137 138 139 T to(T)(PyObject* value) if(isAssociativeArray!T) 140 { 141 import python.raw: pyDictCheck, PyDict_Keys, PyList_Size, PyList_GetItem, PyDict_GetItem; 142 143 assert(pyDictCheck(value)); 144 145 // this enum is to get K and V whilst avoiding auto-decoding, which is why we're not using 146 // std.traits 147 enum _ = is(T == V[K], V, K); 148 alias KeyType = Unqual!K; 149 alias ValueType = Unqual!V; 150 151 ValueType[KeyType] ret; 152 153 auto keys = PyDict_Keys(value); 154 155 foreach(i; 0 .. PyList_Size(keys)) { 156 auto k = PyList_GetItem(keys, i); 157 auto v = PyDict_GetItem(value, k); 158 auto dk = k.to!KeyType; 159 auto dv = v.to!ValueType; 160 161 ret[dk] = dv; 162 } 163 164 return ret; 165 } 166 167 168 T to(T)(PyObject* value) if(isTuple!T) 169 in(pyTupleCheck(value)) 170 in(PyTuple_Size(value) == T.length) 171 do 172 { 173 import python.raw: pyTupleCheck, PyTuple_Size, PyTuple_GetItem; 174 175 T ret; 176 177 static foreach(i; 0 .. T.length) { 178 ret[i] = PyTuple_GetItem(value, i).to!(typeof(ret[i])); 179 } 180 181 return ret; 182 } 183 184 185 T to(T)(PyObject* value) if(isSomeChar!T) { 186 auto str = value.to!string; 187 return str[0]; 188 } 189 190 191 T to(T)(PyObject* value) if(isDelegate!T) 192 in(pyCallableCheck(value)) 193 { 194 import python.raw: PyObject_CallObject; 195 import python.conv.d_to_python: toPython; 196 import python.conv.python_to_d: to; 197 import std.traits: ReturnType, Parameters, Unqual; 198 import std.meta: staticMap; 199 import std.typecons: Tuple; 200 201 alias UnqualParams = staticMap!(Unqual, Parameters!T); 202 203 return (UnqualParams dArgs) { 204 Tuple!UnqualParams dArgsTuple; 205 static foreach(i; 0 .. UnqualParams.length) { 206 dArgsTuple[i] = dArgs[i]; 207 } 208 auto pyArgs = dArgsTuple.toPython; 209 auto pyResult = PyObject_CallObject(value, pyArgs); 210 return pyResult.to!(ReturnType!T); 211 }; 212 } 213 214 T to(T)(PyObject* value) if(isFunctionPointer!T) 215 { 216 throw new Exception("Can't handle function pointers yet"); 217 }