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 // Returns T if T is copyable, T* otherwise 31 auto to(T)(PyObject* value) @trusted if(isUserAggregate!T && is(T == struct)) { 32 import std.traits: Unqual, isCopyable; 33 34 static if(isCopyable!T) { 35 alias RetType = T; 36 Unqual!T ret; 37 toStructImpl(value, &ret); 38 } else { 39 alias RetType = T*; 40 auto ret = new Unqual!T; 41 toStructImpl(value, ret); 42 } 43 44 // might need to be cast to `const` or `immutable` 45 return cast(RetType) ret; 46 } 47 48 49 private void toStructImpl(T)(PyObject* value, T* ret) { 50 import python.type: PythonClass; 51 52 auto pyclass = cast(PythonClass!T*) value; 53 54 static foreach(i; 0 .. typeof(*ret).tupleof.length) { 55 (*ret).tupleof[i] = pyclass.getField!i.to!(typeof(T.tupleof[i])); 56 } 57 } 58 59 60 T to(T)(PyObject* value) @trusted if(isUserAggregate!T && !is(T == struct)) { 61 import python.type: PythonClass, userAggregateInit, gFactory; 62 import std.traits: Unqual; 63 import std.string: fromStringz; 64 import std.conv: text; 65 66 auto pyclass = cast(PythonClass!T*) value; 67 const runtimeType = value.ob_type.tp_name.fromStringz.text; 68 auto creator = runtimeType in gFactory; 69 return creator 70 ? cast(T) (*creator)(value) 71 : userAggregateInit!T; 72 } 73 74 75 T to(T)(PyObject* value) if(isPointer!T && isUserAggregate!(PointerTarget!T)) { 76 import std.traits: ReturnType; 77 auto ret = new Unqual!(PointerTarget!T); 78 toStructImpl(value, ret); 79 return ret; 80 } 81 82 83 T to(T)(PyObject* value) if(is(Unqual!T == DateTime)) { 84 import python.raw; 85 86 return DateTime(pyDateTimeYear(value), 87 pyDateTimeMonth(value), 88 pyDateTimeDay(value), 89 pyDateTimeHour(value), 90 pyDateTimeMinute(value), 91 pyDateTimeSecond(value)); 92 93 } 94 95 96 T to(T)(PyObject* value) if(is(Unqual!T == Date)) { 97 import python.raw; 98 99 return Date(pyDateTimeYear(value), 100 pyDateTimeMonth(value), 101 pyDateTimeDay(value)); 102 } 103 104 105 T to(T)(PyObject* value) if(isArray!T && !isSomeString!T) 106 in(pyListCheck(value) || pyTupleCheck(value)) 107 { 108 import python.raw: PyList_Size, PyList_GetItem, PyTuple_Size, PyTuple_GetItem; 109 import std.range: ElementEncodingType; 110 import std.traits: Unqual; 111 import std.exception: enforce; 112 import std.conv: text; 113 114 Unqual!T ret; 115 116 static if(__traits(compiles, ret.length = 1)) { 117 const valueLength = { 118 if(pyListCheck(value)) 119 return PyList_Size(value); 120 else if(pyTupleCheck(value)) 121 return PyTuple_Size(value); 122 else 123 assert(0); 124 }(); 125 assert(valueLength >= 0, text("Invalid length ", valueLength)); 126 ret.length = valueLength; 127 } 128 129 foreach(i, ref elt; ret) { 130 auto pythonItem = { 131 if(pyListCheck(value)) 132 return PyList_GetItem(value, i); 133 else if(pyTupleCheck(value)) 134 return PyTuple_GetItem(value, i); 135 else 136 assert(0); 137 }(); 138 elt = pythonItem.to!(ElementEncodingType!T); 139 } 140 141 return ret; 142 } 143 144 145 T to(T)(PyObject* value) if(isSomeString!T) { 146 147 import python.raw: pyUnicodeGetSize, pyUnicodeCheck, 148 pyBytesAsString, pyObjectUnicode, pyUnicodeAsUtf8String, Py_ssize_t; 149 import std.conv: to; 150 151 value = pyObjectUnicode(value); 152 153 const length = pyUnicodeGetSize(value); 154 if(length == 0) return T.init; 155 156 auto str = pyUnicodeAsUtf8String(value); 157 if(str is null) throw new Exception("Tried to convert a non-string Python value to D string"); 158 159 auto ptr = pyBytesAsString(str); 160 assert(length == 0 || ptr !is null); 161 162 auto slice = ptr[0 .. length]; 163 164 return slice.to!T; 165 } 166 167 168 T to(T)(PyObject* value) if(is(Unqual!T == bool)) { 169 import python.raw: pyTrue; 170 return value is pyTrue; 171 } 172 173 174 T to(T)(PyObject* value) if(isAssociativeArray!T) 175 { 176 import python.raw: pyDictCheck, PyDict_Keys, PyList_Size, PyList_GetItem, PyDict_GetItem; 177 178 assert(pyDictCheck(value)); 179 180 // this enum is to get K and V whilst avoiding auto-decoding, which is why we're not using 181 // std.traits 182 enum _ = is(T == V[K], V, K); 183 alias KeyType = Unqual!K; 184 alias ValueType = Unqual!V; 185 186 ValueType[KeyType] ret; 187 188 auto keys = PyDict_Keys(value); 189 190 foreach(i; 0 .. PyList_Size(keys)) { 191 auto k = PyList_GetItem(keys, i); 192 auto v = PyDict_GetItem(value, k); 193 auto dk = k.to!KeyType; 194 auto dv = v.to!ValueType; 195 196 ret[dk] = dv; 197 } 198 199 return ret; 200 } 201 202 203 T to(T)(PyObject* value) if(isTuple!T) 204 in(pyTupleCheck(value)) 205 in(PyTuple_Size(value) == T.length) 206 do 207 { 208 import python.raw: pyTupleCheck, PyTuple_Size, PyTuple_GetItem; 209 210 T ret; 211 212 static foreach(i; 0 .. T.length) { 213 ret[i] = PyTuple_GetItem(value, i).to!(typeof(ret[i])); 214 } 215 216 return ret; 217 } 218 219 220 T to(T)(PyObject* value) if(isSomeChar!T) { 221 auto str = value.to!string; 222 return str[0]; 223 } 224 225 226 T to(T)(PyObject* value) if(isDelegate!T) 227 in(pyCallableCheck(value)) 228 { 229 import python.raw: PyObject_CallObject; 230 import python.conv.d_to_python: toPython; 231 import python.conv.python_to_d: to; 232 import std.traits: ReturnType, Parameters, Unqual; 233 import std.meta: staticMap; 234 import std.typecons: Tuple; 235 236 alias UnqualParams = staticMap!(Unqual, Parameters!T); 237 238 return (UnqualParams dArgs) { 239 Tuple!UnqualParams dArgsTuple; 240 static foreach(i; 0 .. UnqualParams.length) { 241 dArgsTuple[i] = dArgs[i]; 242 } 243 auto pyArgs = dArgsTuple.toPython; 244 auto pyResult = PyObject_CallObject(value, pyArgs); 245 return pyResult.to!(ReturnType!T); 246 }; 247 } 248 249 T to(T)(PyObject* value) if(isFunctionPointer!T) 250 { 251 throw new Exception("Can't handle function pointers yet"); 252 }