1 module python.embedded; 2 import python.raw; 3 import std.exception: enforce; 4 //#include <python3.7m/pythonrun.h> 5 6 /+ 7 Capabilities of pyd embedded interpreter: 8 - run python code in D 9 - run D code in python 10 - declare python functions and use in D 11 - access and manipulate python globals in D 12 - wrap D classes/structs and use in python or D 13 - wrap python class instances in D 14 - wrap D ranges and iterate through them in python 15 - wrap python iterators as D ranges 16 - inheritance and multithreading 17 +/ 18 19 //import deimos.python.Python; 20 import python.raw : PyObject, PyFrameObject; 21 import python.type; 22 import std.algorithm: findSplit; 23 import std.string: strip, outdent; 24 import std.traits; 25 26 char* zc()(string s) { 27 if(s.length && s[$-1] == 0) return s.dup.ptr; 28 return ((cast(char[])s) ~ "\0").ptr; 29 } 30 31 PyObject* pyImportModule(string name) 32 { 33 import std.string : toStringz; 34 import std.exception : enforce; 35 enforce(pyIsInitialized(), "python not initialized"); 36 return PyImport_ImportModule(name.toStringz); 37 } 38 39 bool pyIsInitialized() 40 { 41 return true; 42 } 43 44 45 /+ 46 Encapsulate a context within the Python interpreter. 47 This will preserve local variables and changes to the Python interpreter 48 made by 'from ___future__ import feature' across calls to this.py_eval and 49 this.py_stmts. 50 Otherwise, will not segregate global changes made to the Python interpreter. 51 +/ 52 final class InterpContext 53 { 54 /// dict object: global variables in this context 55 PyObject* globals; 56 /// dict object: local variables in this context 57 PyObject* locals; 58 PyCompilerFlags flags; 59 PyFrameObject* frame; 60 61 /** 62 */ 63 this() 64 { 65 import std.string : toStringz; 66 enforce(pyIsInitialized(), "python not initialized"); 67 locals = PyDict_New(); 68 globals = PyDict_New(); 69 PyDict_SetItemString(globals, "__builtins__",PyEval_GetBuiltins()); 70 } 71 72 void pushDummyFrame() 73 { 74 auto threadstate = PyThreadState_Get(); 75 if (threadstate.frame is null) 76 { 77 PyCodeObject* code = PyCode_NewEmpty("<d>","<d>", 0); 78 frame = PyFrame_New(threadstate, code, 79 cast(PyObject*)(globals), 80 cast(PyObject*)(locals)); 81 threadstate.frame = frame; 82 } 83 } 84 85 void popDummyFrame() 86 { 87 auto threadstate = PyThreadState_Get(); 88 if(threadstate.frame == frame) { 89 threadstate.frame = null; 90 } 91 } 92 93 /** 94 Evaluate a python expression once within this context and return the 95 result. 96 Params: 97 python = a python expression 98 Returns: 99 the result of expression 100 */ 101 T py_eval(T = PyObject*)( string pythonExpression, string file = __FILE__, size_t line = __LINE__) 102 { 103 import std.string : toStringz; 104 auto pres = PyRun_StringFlags( 105 pythonExpression.toStringz, 106 Py_eval_input, 107 cast(PyObject*) globals, 108 cast(PyObject*) locals, 109 null); //&flags); 110 if(pres) { 111 return pres; 112 }else{ 113 import std.stdio; 114 stderr.writefln("file: %s, line: %s",file,line); 115 //handle_exception(file,line); 116 assert(0); 117 } 118 } 119 /** 120 Evaluate one or more python statements once. 121 Params: 122 python = python statements 123 */ 124 auto evalStatements(string pythonStatements, string file = __FILE__, size_t line = __LINE__) 125 { 126 import std.string: outdent, toStringz; 127 128 auto pres = PyRun_StringFlags( 129 outdent(pythonStatements).toStringz, 130 Py_file_input, 131 cast(PyObject*) globals, 132 cast(PyObject*) locals, 133 &flags); 134 if(pres) { 135 auto ret = pres.toString(); 136 pyDecRef(pres); 137 return ret; 138 }else{ 139 //handle_exception(file,line); 140 assert(0); 141 } 142 } 143 144 @property PyObject opDispatch(string id)() { 145 return this.locals[id]; 146 } 147 148 @property void opDispatch(string id, T)(T t) { 149 static if(is(T == PyObject)) { 150 alias t s; 151 }else{ 152 PyObject s = py(t); 153 } 154 this.locals[id] = py(s); 155 } 156 } 157 158 /++ 159 Wraps a python function (specified as a string) as a D function roughly of 160 signature func_t 161 Params: 162 python = a python function 163 pythonModule = context in which to run expression. must be a python module name. 164 func_t = type of d function 165 +/ 166 ReturnType!func_t py_def(func_t)(string pythonFunction, string pythonModule, ParameterTypeTuple!func_t args, string file = __FILE__, size_t line = __LINE__) 167 { 168 import std.string : toStringz; 169 alias ReturnType!func_t R; 170 alias ParameterTypeTuple!func_t Args; 171 enum afterdef = findSplit(pythonFunction, "def")[2]; 172 enum ereparen = findSplit(afterdef, "(")[0]; 173 enum name = strip(ereparen) ~ "\0"; 174 static PyObject func; 175 static PythonException exc; 176 static string errmsg; 177 static bool once = true; 178 enforce(pyIsInitialized(), "python not initialized"); 179 if(once) 180 { 181 once = false; 182 auto globals = py_import(pythonModule).getdict(); 183 auto globals_ptr = pyIncRef(globals); 184 scope(exit) pyDecRef(globals_ptr); 185 auto locals = py((string[string]).init); 186 auto locals_ptr = pyIncRef(locals); 187 scope(exit) pyDecRef(locals_ptr); 188 if("__builtins__" !in globals) 189 { 190 globals = PyDict_New(); 191 PyDict_SetItemString(globals, "__builtins__",PyEval_GetBuiltins()); 192 } 193 auto pres = PyRun_String( 194 pythonFunction.toStringz, 195 Py_file_input, globals_ptr, locals_ptr); 196 if(pres) 197 { 198 auto res = new PyObject(pres); 199 func = locals[name]; 200 } else 201 { 202 try 203 { 204 handle_exception(); 205 } catch(PythonException e) 206 { 207 exc = e; 208 } 209 } 210 } 211 if(!func) 212 { 213 throw exc; 214 } 215 return func(args).to_d!R(); 216 } 217 218 /++ 219 Evaluate a python expression once and return the result. 220 Params: 221 python = a python expression 222 pythonModule = context in which to run expression. either a python module name, or "". 223 +/ 224 T evalExpression(T = PyObject*)(string pythonExpression, string pythonModule= "", string file = __FILE__, size_t line = __LINE__) 225 { 226 import std.string : toStringz; 227 enforce(pyIsInitialized(), "python not initialized"); 228 PyObject* locals = null; 229 // FIXME 230 pythonModule=""; 231 locals = (pythonModule == "") ? PyDict_New() : pyImportModule(pythonModule); // .getdict(); 232 pyIncRef(locals); 233 //if ("__builtins__" !in locals) { 234 PyDict_SetItemString(locals, "__builtins__",PyEval_GetBuiltins()); 235 //} 236 auto result = PyRun_StringFlags( pythonExpression.toStringz, Py_eval_input, locals, locals,null); 237 scope(exit) Py_DecRef(locals); 238 return result; 239 /+ 240 //scope(exit) Py_XDecRef(locals); 241 if(result) 242 { 243 //auto res = new PyObject(result); 244 assert(0); 245 // return res.to_d!T(); 246 }else 247 { 248 // handle_exception(file,line); 249 assert(0); 250 } +/ 251 } 252 253 /++ 254 Evaluate one or more python statements once. 255 Params: 256 python = python statements 257 modl = context in which to run expression. either a python module name, or "". 258 +/ 259 auto evalStatements(string pythonStatements, string module_ = "",string file = __FILE__, size_t line = __LINE__) 260 { 261 import std.string : toStringz; 262 enforce(pyIsInitialized(), "python not initialized"); 263 PyObject* locals; 264 //PyObject* locals_ptr; 265 if (module_ == "") { 266 locals = PyDict_New(); 267 }else { 268 //locals = pyImportModule(module_).getdict(); 269 } 270 pyIncRef(locals); 271 //locals_ptr = locals; 272 // locals_ptr = pyIncRef(locals); // locals.ptr); 273 //if("__builtins__" !in locals) { 274 //auto builtins = new PyObject(PyEval_GetBuiltins()); 275 // locals["__builtins__"] = builtins; 276 //} 277 auto pres = PyRun_StringFlags( 278 outdent(pythonStatements).toStringz, 279 Py_file_input, locals, locals,null); 280 return pres; 281 /+ 282 scope(exit) pyDecRef(locals); 283 if(pres) { 284 pyDecRef(pres); 285 }else{ 286 //handle_exception(file,line); 287 assert(0); 288 } +/ 289 } 290 alias py_stmts = evalStatements; 291 292 void evalSimpleString(string s) 293 { 294 import std.string : toStringz; 295 PyRun_SimpleStringFlags(s.toStringz,null); 296 } 297 void initializePython() 298 { 299 Py_Initialize(); 300 } 301 302 string toString(PyObject* o) 303 { 304 import std.string : fromStringz; 305 char* s; 306 PyArg_Parse(o, "s",&s); 307 return s.fromStringz.idup; 308 } 309 310 PyObject* getMember(PyObject* parent, string memberName) 311 { 312 import std.string: toStringz; 313 return PyObject_GetAttrString(parent,memberName.toStringz); 314 } 315 316 PyObject* getModuleMember(string moduleName, string memberName) 317 { 318 auto pmod = pyImportModule(moduleName); 319 auto ret = getMember(pmod,memberName); 320 Py_DecRef(pmod); 321 return ret; 322 } 323 324 PyObject* callObject(PyObject* o, PyObject* args) 325 { 326 return null; // PyEval_CallObject(o,args); 327 } 328 329 PyObject* construct(string moduleName, string className) 330 { 331 auto pythonClass = getModuleMember(moduleName, className); 332 auto pythonArgs = Py_BuildValue("()"); 333 auto result = callObject(pythonClass,pythonArgs); 334 Py_DecRef(pythonClass); 335 Py_DecRef(pythonArgs); 336 return result; 337 } 338 339 PyObject* pyBuildValue(Args...)(string formatString, Args args) 340 { 341 import std.string : toStringz; 342 return Py_BuildValue(formatString.toStringz,args); 343 } 344 345 auto pyParseArgs(Args...)(PyObject* o, string formatString) 346 { 347 import std.string : toStringz; 348 import std.typecons: Tuple, tuple; 349 import std.algorithm : map; 350 import std.array : array; 351 Tuple!Args args; 352 auto argsPtr = args.map!(arg => &arg).array; 353 PyArg_Parse(formatString,argsPtr); 354 return args; 355 } 356