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