|
| 1 | +//metadoc Python copyrigth Aslak Gronflaten, 2006 |
| 2 | +//metadoc Python license BSD revised |
| 3 | +/*metadoc Python description |
| 4 | +This object provides access the world of python. |
| 5 | +*/ |
| 6 | +//metadoc Python credit Based on code by Steve Dekorte |
| 7 | +//metadoc Python category API |
| 8 | + |
| 9 | +#include "IoPython.h" |
| 10 | +#include "IoState.h" |
| 11 | +#include "IoNumber.h" |
| 12 | +#include "IoList.h" |
| 13 | +#include "IoMap.h" |
| 14 | +#include "IoDirectory.h" |
| 15 | + |
| 16 | +#include <stdlib.h> |
| 17 | +#include <stdio.h> |
| 18 | + |
| 19 | +#define DATA(self) ((PythonData *)IoObject_dataPointer(self)) |
| 20 | + |
| 21 | + |
| 22 | +static const char *protoId = "Python"; |
| 23 | + |
| 24 | +IoTag *IoPython_newTag(void *state) |
| 25 | +{ |
| 26 | + IoTag *tag = IoTag_newWithName_("Python"); |
| 27 | + IoTag_state_(tag, state); |
| 28 | + IoTag_cloneFunc_(tag, (IoTagCloneFunc *)IoPython_rawClone); |
| 29 | + IoTag_freeFunc_(tag, (IoTagFreeFunc *)IoPython_free); |
| 30 | + return tag; |
| 31 | +} |
| 32 | + |
| 33 | +IoPython *IoPython_proto(void *state) |
| 34 | +{ |
| 35 | + IoObject *self = NULL; |
| 36 | + |
| 37 | + Py_Initialize(); |
| 38 | + self = IoObject_new(state); |
| 39 | + IoObject_tag_(self, IoPython_newTag(state)); |
| 40 | + IoObject_setDataPointer_(self, PythonData_new()); |
| 41 | + fflush(stdout); |
| 42 | + IoState_registerProtoWithId_(state, self, protoId); |
| 43 | + |
| 44 | + { |
| 45 | + IoMethodTable methodTable[] = { |
| 46 | + {"credits", IoPython_credits}, |
| 47 | + {"forward", IoPython_forward}, |
| 48 | + {"import", IoPython_import}, |
| 49 | + {"invoke", IoPython_call}, |
| 50 | + {"print", IoPython_print}, |
| 51 | + {NULL, NULL}, |
| 52 | + }; |
| 53 | + IoObject_addMethodTable_(self, methodTable); |
| 54 | + } |
| 55 | + DATA(self)->data = (void *) 1; // Hack |
| 56 | + return self; |
| 57 | +} |
| 58 | + |
| 59 | +IoPython *IoPython_rawClone(IoPython *proto) |
| 60 | +{ |
| 61 | + IoPython *self = IoObject_rawClonePrimitive(proto); |
| 62 | + IoObject_setDataPointer_(self, PythonData_new()); |
| 63 | + return self; |
| 64 | +} |
| 65 | + |
| 66 | +/* ----------------------------------------------------------- */ |
| 67 | + |
| 68 | +IoPython *IoPython_new(void *state) |
| 69 | +{ |
| 70 | + IoObject *proto = IoState_protoWithId_(state, protoId); |
| 71 | + return IOCLONE(proto); |
| 72 | +} |
| 73 | + |
| 74 | +void IoPython_free(IoPython *self) |
| 75 | +{ |
| 76 | + PythonData_free(DATA(self)); |
| 77 | +} |
| 78 | +/* ----------------------------------------------------------- */ |
| 79 | + |
| 80 | +IoObject *wrap(IoPython *self, PyObject *o) { |
| 81 | + IoPython *ret = IoPython_new(IOSTATE); |
| 82 | + DATA(ret)->data = o; |
| 83 | + return ret; |
| 84 | +} |
| 85 | + |
| 86 | +IoObject *IoPython_credits(IoPython *self, IoObject *locals, IoMessage *m) |
| 87 | +{ |
| 88 | + return IOSYMBOL("Python/Io bridge by Aslak Gronflaten"); |
| 89 | +} |
| 90 | + |
| 91 | +IoObject *IoPython_print(IoPython *self, IoObject *locals, IoMessage *m) |
| 92 | +{ |
| 93 | + PyObject *obj = DATA(self)->data; |
| 94 | + if(obj != NULL) { |
| 95 | + PyObject_Print(obj, stdout, 0); |
| 96 | + } |
| 97 | + return self; |
| 98 | +} |
| 99 | + |
| 100 | +/** |
| 101 | + * Convert an Io object to PyObject. |
| 102 | + * At the moment, we can't pass in any objects, just those that can be translated, |
| 103 | + * until I build a python wrapper around an io object, reverse of what I did here. |
| 104 | + * TODO: Memory management!!! |
| 105 | + * |
| 106 | + * self: unused? (why is it here?) |
| 107 | + * obj: the Io object to map into a Python object |
| 108 | + * return NULL on failure -- callers should check for this case |
| 109 | + */ |
| 110 | +PyObject *convertIo(IoObject *self, IoObject *obj) { |
| 111 | + PyObject *ret = NULL; |
| 112 | + if(ISNIL(obj)) { |
| 113 | + ret = Py_None; |
| 114 | + } |
| 115 | + if(ISNUMBER(obj)) { |
| 116 | + ret = PyFloat_FromDouble(CNUMBER(obj)); |
| 117 | + Py_INCREF(ret); |
| 118 | + } else if(ISSEQ(obj)) { |
| 119 | + ret = PyUnicode_FromString(CSTRING(obj)); |
| 120 | + Py_INCREF(ret); |
| 121 | + } else if(ISLIST(obj)) { |
| 122 | + ret = PyList_New(IoList_rawSize(obj)); |
| 123 | + Py_INCREF(ret); |
| 124 | + //todo: check for NULL returns from the recursion (and on a null, free up the half-constructed list and return NULL ourselves) |
| 125 | + //otherwise you get "Bus error", probably when Python tries to complain that you've passed it an invalid data structure |
| 126 | + LIST_SAFEFOREACH(IoList_rawList(obj), i, v, PyList_SET_ITEM(ret, i, convertIo(self, v))); |
| 127 | + } else if(ISMAP(obj)) { |
| 128 | + IoList* keys = IoMap_rawKeys(obj); //XXX do I have to free this? |
| 129 | + ret = PyDict_New(); |
| 130 | + Py_INCREF(ret); |
| 131 | + //todo: check for NULL returns from the recursions |
| 132 | + LIST_SAFEFOREACH(IoList_rawList(keys), i, v, PyDict_SetItem(ret, convertIo(self, v), convertIo(self, IoMap_rawAt(obj, v)))); |
| 133 | + } else { |
| 134 | + printf("Unable to convert parameter `%s` to python.\n", IoObject_name(obj)); |
| 135 | + } |
| 136 | + |
| 137 | + return ret; |
| 138 | +} |
| 139 | + |
| 140 | +/** |
| 141 | + * Tries to convert the obj to an Io object, if possible. If not, return null. |
| 142 | + * Can't decref if this method is used on both pFunc and pValue |
| 143 | + */ |
| 144 | +IoObject *convertPy(IoObject *self, PyObject *obj) { |
| 145 | + //PyObject_Print(obj, stdout, 0); |
| 146 | + //PyObject *pType = PyObject_Type(obj); |
| 147 | + //PyObject_Print(pType, stdout, 0); |
| 148 | + IoObject *ret = NULL; // Return value |
| 149 | + |
| 150 | + //I've messed with the way the return value is structured in this code, it's not cleaned up yet, sorry -- nick |
| 151 | + |
| 152 | + if(obj == Py_None) { |
| 153 | + ret = IONIL(self); |
| 154 | + } else if(PyUnicode_Check(obj)) { |
| 155 | + // Convert to Io sequence and return. |
| 156 | + IoSeq *ret = IoSeq_newWithCString_(IOSTATE, PyUnicode_AsUTF8(obj)); |
| 157 | + return ret; |
| 158 | + // TODO:::: Memory management! Who's responsible here! (I am, that's who) |
| 159 | + } else if(PyFloat_Check(obj)) { |
| 160 | + ret = IoNumber_newWithDouble_(IOSTATE, PyFloat_AS_DOUBLE(obj)); |
| 161 | + //Py_DECREF(obj); |
| 162 | + } else if(PyLong_Check(obj)) { |
| 163 | + ret = IoNumber_newWithDouble_(IOSTATE, PyLong_AS_LONG(obj)); |
| 164 | + // Decref? |
| 165 | + } else if(PyList_Check(obj)) { |
| 166 | + // We have a list. So, make an Io list, and convert every element, and insert them. |
| 167 | + int i; |
| 168 | + int len = PyList_GET_SIZE(obj); |
| 169 | + ret = IoList_new(IOSTATE); |
| 170 | + for(i=0;i<len;i++) { |
| 171 | + PyObject *o = PyList_GET_ITEM(obj, i); |
| 172 | + IoObject *x = convertPy(self, o); |
| 173 | + // insert in list |
| 174 | + IoList_rawAppend_(ret, x); |
| 175 | + } |
| 176 | + } else if(PyTuple_Check(obj)) { |
| 177 | + int i; |
| 178 | + int len = PyTuple_GET_SIZE(obj); |
| 179 | + ret = IoList_new(IOSTATE); |
| 180 | + for(i=0;i<len;i++) { |
| 181 | + PyObject *o = PyTuple_GET_ITEM(obj, i); |
| 182 | + IoObject *x = convertPy(self, o); |
| 183 | + // insert in list |
| 184 | + IoList_rawAppend_(ret, x); |
| 185 | + } |
| 186 | + } else if(PyDict_Check(obj)) { |
| 187 | + // We have a dictionary. Make an Io map, and convert all values. |
| 188 | + // or should we.... Io's map can only have string keys... hardly a replacement, now is it. |
| 189 | + // Would be better to build a good wrapper around the python dict. |
| 190 | + } else if(PyCallable_Check(obj)) { |
| 191 | + //ret = IoState_doString_(IOSTATE, "method(return self invoke(\"\")"); |
| 192 | + // TODO: We should return a callable object here... Don't know how though. Yet. |
| 193 | + } else { |
| 194 | + ret = wrap(self, obj); |
| 195 | + } |
| 196 | + return ret; |
| 197 | +} |
| 198 | + |
| 199 | + |
| 200 | + |
| 201 | +IoObject *IoPython_call_int(IoPython *self, IoObject *locals, IoMessage *m, int argOffset, char *functionName) |
| 202 | +{ |
| 203 | + PyObject *pFunc = NULL; |
| 204 | + int argc = IoMessage_argCount(m); |
| 205 | + |
| 206 | + PyObject *pModule = DATA(self)->data; |
| 207 | + if(!pModule) { |
| 208 | + fprintf(stderr, "We have null pModule for function %s ", functionName); |
| 209 | + return IONIL(self); |
| 210 | + } |
| 211 | + if(!PyObject_HasAttrString(pModule, functionName)){ |
| 212 | + fprintf(stderr, "Module has no function %s ", functionName); |
| 213 | + return IONIL(self); |
| 214 | + } |
| 215 | + |
| 216 | + pFunc = PyObject_GetAttrString(pModule, functionName); |
| 217 | + /* pFunc is a new reference */ |
| 218 | + |
| 219 | + if (pFunc && PyCallable_Check(pFunc)) { |
| 220 | + PyObject *pArgs = PyTuple_New(argc - argOffset); // argc |
| 221 | + PyObject *pValue = NULL; |
| 222 | + int i; |
| 223 | + for(i = argOffset;i<argc;i++) { |
| 224 | + IoObject *param = IoMessage_locals_valueArgAt_(m, locals, i); |
| 225 | + PyObject *pyValue = convertIo(self, param); |
| 226 | + PyTuple_SetItem(pArgs, i-argOffset, pyValue); |
| 227 | + } |
| 228 | + pValue = PyObject_CallObject(pFunc, pArgs); |
| 229 | + Py_DECREF(pArgs); |
| 230 | + Py_XDECREF(pFunc); |
| 231 | + if (pValue != NULL) { |
| 232 | + return convertPy(self, pValue); |
| 233 | + } else { |
| 234 | + if (PyErr_Occurred()) |
| 235 | + PyErr_Print(); |
| 236 | + fprintf(stderr,"Call failed\n"); |
| 237 | + } |
| 238 | + } else if (PyErr_Occurred()) { |
| 239 | + fprintf(stderr, "Cannot find python function \"%s\"\n", functionName); |
| 240 | + PyErr_Print(); |
| 241 | + } |
| 242 | + else { |
| 243 | + return convertPy(self, pFunc); |
| 244 | + } |
| 245 | + |
| 246 | + //catchall just in case |
| 247 | + return IONIL(self); |
| 248 | +} |
| 249 | + |
| 250 | +IoObject *IoPython_forward(IoPython *self, IoObject *locals, IoMessage *m) |
| 251 | +{ |
| 252 | + IoSymbol *name = IoMessage_name(m); |
| 253 | + char *functionName = IoSeq_asCString(name); |
| 254 | + return IoPython_call_int(self, locals, m, 0, functionName); |
| 255 | +} |
| 256 | + |
| 257 | +IoObject *IoPython_call(IoPython *self, IoObject *locals, IoMessage *m) |
| 258 | +{ |
| 259 | + IoSeq *name = IoMessage_locals_seqArgAt_(m, locals, 0); |
| 260 | + char *functionName = IoSeq_asCString(name); |
| 261 | + return IoPython_call_int(self, locals, m, 1, functionName); |
| 262 | +} |
| 263 | + |
| 264 | + |
| 265 | +/** |
| 266 | + * Import a module, return reference to it as a Python object |
| 267 | + */ |
| 268 | +IoObject *IoPython_import(IoPython *self, IoObject *locals, IoMessage *m) |
| 269 | +{ |
| 270 | + IoSeq *name = IoMessage_locals_seqArgAt_(m, locals, 0); |
| 271 | + char *nameString = IoSeq_asCString(name); |
| 272 | + |
| 273 | + PyObject *pName, *pModule; |
| 274 | + pName = PyUnicode_FromString(nameString); |
| 275 | + /* Error checking of pName left out */ |
| 276 | + |
| 277 | + pModule = PyImport_Import(pName); |
| 278 | + |
| 279 | + if(!pModule) { |
| 280 | + fprintf(stderr, "Could not find module %s\n", nameString); |
| 281 | + return IONIL(self); |
| 282 | + } |
| 283 | + |
| 284 | + // Set slots (for easier introspection and use from io) |
| 285 | + /* |
| 286 | + PyObject *dict = PyModule_GetDict(pModule); |
| 287 | + PyObject *keys = PyDict_Keys(dict); |
| 288 | + int i; |
| 289 | + for(i = 0;i<PyList_Size(keys);i++) { |
| 290 | + PyObject *key = PyList_GetItem(keys, i); |
| 291 | + PyObject *value = PyDict_GetItem(dict, key); |
| 292 | + // TODO: Not allowed method vall IoSeq_newSymbolWithCString_ |
| 293 | + if(!PyCallable_Check(value)) {// don't want methods blocking the forward |
| 294 | + IoObject_setSlot_to_(self, IOSYMBOL(PyUnicode_AsUTF8(key)), convertPy(self, value)); |
| 295 | + } |
| 296 | + } |
| 297 | + */ |
| 298 | + // |
| 299 | + |
| 300 | + Py_DECREF(pName); |
| 301 | + |
| 302 | + // Now, we've got the module. Wrap it and return it. |
| 303 | + return wrap(self, pModule); |
| 304 | +} |
0 commit comments