interp_python.cc
1 /* This is a component of LinuxCNC 2 * Copyright 2011, 2012 Michael Haberler <git@mah.priv.at> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 */ 18 // Support for embedding Python in the RS274NGC interpreter 19 // with access to Interp and Canon 20 // 21 // NB: all this is executed at readahead time 22 // 23 // Michael Haberler 4/2011 24 // 25 // if you get a segfault like described 26 // here: https://bugs.launchpad.net/ubuntu/+source/mesa/+bug/259219 27 // or here: https://www.libavg.de/wiki/LinuxInstallIssues#glibc_invalid_pointer : 28 // 29 // try this before starting milltask and axis in emc: 30 // LD_PRELOAD=/usr/lib/libstdc++.so.6 $EMCTASK ... 31 // LD_PRELOAD=/usr/lib/libstdc++.so.6 $EMCDISPLAY ... 32 // 33 // this is actually a bug in libgl1-mesa-dri and it looks 34 // it has been fixed in mesa - 7.10.1-0ubuntu2 35 36 #define BOOST_PYTHON_MAX_ARITY 4 37 #include "python_plugin.hh" 38 #include "interp_python.hh" 39 #include <boost/python/extract.hpp> 40 #include <boost/python/import.hpp> 41 #include <boost/python/str.hpp> 42 namespace bp = boost::python; 43 44 #include <unistd.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <math.h> 48 #include <string.h> 49 #include <ctype.h> 50 #include <sys/types.h> 51 #include <sys/stat.h> 52 #include <exception> 53 54 #include "rs274ngc.hh" 55 #include "interp_return.hh" 56 #include "interp_internal.hh" 57 #include "rs274ngc_interp.hh" 58 #include "units.h" 59 60 extern PythonPlugin *python_plugin; 61 62 #define PYCHK(bad, fmt, ...) \ 63 do { \ 64 if (bad) { \ 65 logPy(fmt, ## __VA_ARGS__); \ 66 ERM(fmt, ## __VA_ARGS__); \ 67 goto error; \ 68 } \ 69 } while(0) 70 71 #define IS_STRING(x) (PyObject_IsInstance(x.ptr(), (PyObject*)&PyString_Type)) 72 #define IS_INT(x) (PyObject_IsInstance(x.ptr(), (PyObject*)&PyInt_Type)) 73 74 // decode a Python exception into a string. 75 std::string handle_pyerror() 76 { 77 using namespace boost::python; 78 using namespace boost; 79 80 PyObject *exc,*val,*tb; 81 object formatted_list, formatted; 82 PyErr_Fetch(&exc,&val,&tb); 83 handle<> hexc(exc),hval(allow_null(val)),htb(allow_null(tb)); 84 object traceback(import("traceback")); 85 if (!tb) { 86 object format_exception_only(traceback.attr("format_exception_only")); 87 formatted_list = format_exception_only(hexc,hval); 88 } else { 89 object format_exception(traceback.attr("format_exception")); 90 formatted_list = format_exception(hexc,hval,htb); 91 } 92 formatted = str("\n").join(formatted_list); 93 return extract<std::string>(formatted); 94 } 95 96 int Interp::py_reload() 97 { 98 if (PYUSABLE) { 99 CHKS((python_plugin->initialize() == PLUGIN_EXCEPTION), 100 "py_reload:\n%s", python_plugin->last_exception().c_str()); 101 } 102 return INTERP_OK; 103 } 104 105 // determine wether [module.]funcname is callable 106 bool Interp::is_pycallable(setup_pointer settings, 107 const char *module, 108 const char *funcname) 109 { 110 if (!PYUSABLE) 111 return false; 112 113 return python_plugin->is_callable(module,funcname); 114 } 115 116 // all parameters to/results from Python calls go through the callframe, which looks a bit awkward 117 // the reason is not to expose boost.python through the interpreter public interface 118 int Interp::pycall(setup_pointer settings, 119 context_pointer frame, 120 const char *module, 121 const char *funcname, 122 int calltype) 123 { 124 bp::object retval, function; 125 std::string msg; 126 bool py_exception = false; 127 int status = INTERP_OK; 128 PyObject *res_str; 129 130 if (_setup.loggingLevel > 4) 131 logPy("pycall(%s.%s) \n", module ? module : "", funcname); 132 133 CHKS(!PYUSABLE, "pycall(%s): Pyhton plugin not initialized",funcname); 134 frame->pystuff.impl->py_return_type = 0; 135 136 switch (calltype) { 137 case PY_EXECUTE: // just run a string 138 python_plugin->run_string(funcname, retval); 139 CHKS(python_plugin->plugin_status() == PLUGIN_EXCEPTION, 140 "run_string(%s):\n%s", funcname, 141 python_plugin->last_exception().c_str()); 142 break; 143 // default: 144 // switch (frame->entry_at) { //FIXTHIS terminally ugly 145 case PY_FINISH_OWORDCALL: 146 case PY_FINISH_PROLOG: 147 case PY_FINISH_BODY: 148 case PY_FINISH_EPILOG: 149 logPy("pycall: call generator.next()" ); 150 151 // check inputs here, since _read() may not be called 152 read_inputs(&_setup); 153 // handler continuation if a generator was used 154 try { 155 retval = frame->pystuff.impl->generator_next(); 156 } 157 catch (bp::error_already_set) { 158 if (PyErr_Occurred()) { 159 // StopIteration is raised when the generator executes 'return' 160 // instead of another 'yield INTERP_EXECUTE_FINISH 161 // Technically this means a normal end of the handler and hence we 162 // treat it as INTERP_OK indicating this handler is now done 163 if (PyErr_ExceptionMatches(PyExc_StopIteration)) { 164 frame->pystuff.impl->py_return_type = RET_STOPITERATION; 165 bp::handle_exception(); 166 PyErr_Clear(); 167 logPy("pycall: call generator - StopIteration exception"); 168 return INTERP_OK; 169 } else { 170 msg = handle_pyerror(); 171 bp::handle_exception(); 172 PyErr_Clear(); 173 logPy("pycall: call generator - exception: %s",msg.c_str()); 174 175 ERS("exception during generator call: %s", msg.c_str()); 176 } 177 } else 178 Error("calling generator: duh"); 179 } 180 break; 181 default: 182 python_plugin->call(module,funcname, frame->pystuff.impl->tupleargs,frame->pystuff.impl->kwargs,retval); 183 CHKS(python_plugin->plugin_status() == PLUGIN_EXCEPTION, 184 "pycall(%s):\n%s", funcname, 185 python_plugin->last_exception().c_str()); 186 } 187 188 try { 189 status = INTERP_OK; 190 191 switch (calltype) { 192 case PY_OWORDCALL: 193 case PY_PROLOG: 194 case PY_BODY: 195 case PY_EPILOG: 196 case PY_FINISH_OWORDCALL: 197 case PY_FINISH_PROLOG: 198 case PY_FINISH_BODY: 199 case PY_FINISH_EPILOG: 200 201 // these may return values in several flavours: 202 // - an int (INTERP_OK, INTERP_ERROR, INTERP_EXECUTE_FINISH...) 203 // - a double (Python oword subroutine) 204 // - a generator object, in case the handler contained a yield statement 205 if (retval.ptr() != Py_None) { 206 if (PyGen_Check(retval.ptr())) { 207 208 // a generator was returned. This must have been the first time call to a handler 209 // which contains a yield. Extract next() method. 210 frame->pystuff.impl->generator_next = bp::getattr(retval, "next"); 211 212 // and call it for the first time. 213 // Expect execution up to first 'yield INTERP_EXECUTE_FINISH'. 214 frame->pystuff.impl->py_returned_int = bp::extract<int>(frame->pystuff.impl->generator_next()); 215 frame->pystuff.impl->py_return_type = RET_YIELD; 216 217 } else if (PyString_Check(retval.ptr())) { 218 // returning a string sets the interpreter error message and aborts 219 char *msg = bp::extract<char *>(retval); 220 ERM("%s", msg); 221 frame->pystuff.impl->py_return_type = RET_ERRORMSG; 222 status = INTERP_ERROR; 223 } else if (PyInt_Check(retval.ptr())) { 224 frame->pystuff.impl->py_returned_int = bp::extract<int>(retval); 225 frame->pystuff.impl->py_return_type = RET_INT; 226 logPy("Python call %s.%s returned int: %d", module, funcname, frame->pystuff.impl->py_returned_int); 227 } else if (PyFloat_Check(retval.ptr())) { 228 frame->pystuff.impl->py_returned_double = bp::extract<double>(retval); 229 frame->pystuff.impl->py_return_type = RET_DOUBLE; 230 logPy("Python call %s.%s returned float: %f", module, funcname, frame->pystuff.impl->py_returned_double); 231 } else { 232 // not a generator, int, or float - strange 233 PyObject *res_str = PyObject_Str(retval.ptr()); 234 Py_XDECREF(res_str); 235 ERM("Python call %s.%s returned '%s' - expected generator, int, or float value, got %s", 236 module, funcname, 237 PyString_AsString(res_str), 238 retval.ptr()->ob_type->tp_name); 239 status = INTERP_ERROR; 240 } 241 } else { 242 logPy("call: O <%s> call returned None",funcname); 243 frame->pystuff.impl->py_return_type = RET_NONE; 244 } 245 break; 246 247 case PY_INTERNAL: 248 case PY_PLUGIN_CALL: 249 // a plain int (INTERP_OK, INTERP_ERROR, INTERP_EXECUTE_FINISH...) is expected 250 // must have returned an int 251 if ((retval.ptr() != Py_None) && 252 (PyInt_Check(retval.ptr()))) { 253 254 // FIXME check new return value convention 255 status = frame->pystuff.impl->py_returned_int = bp::extract<int>(retval); 256 frame->pystuff.impl->py_return_type = RET_INT; 257 logPy("pycall(%s): PY_INTERNAL/PY_PLUGIN_CALL: return code=%d", funcname,status); 258 } else { 259 logPy("pycall(%s): PY_INTERNAL: expected an int return code", funcname); 260 res_str = PyObject_Str(retval.ptr()); 261 ERM("Python internal function '%s' expected tuple or int return value, got '%s' (%s)", 262 funcname, 263 PyString_AsString(res_str), 264 retval.ptr()->ob_type->tp_name); 265 Py_XDECREF(res_str); 266 status = INTERP_ERROR; 267 } 268 break; 269 case PY_EXECUTE: 270 break; 271 272 default: ; 273 } 274 } 275 catch (bp::error_already_set) { 276 if (PyErr_Occurred()) { 277 msg = handle_pyerror(); 278 } 279 py_exception = true; 280 bp::handle_exception(); 281 PyErr_Clear(); 282 } 283 if (py_exception) { 284 ERM("pycall: %s.%s:\n%s", module ? module:"", funcname, msg.c_str()); 285 status = INTERP_ERROR; 286 } 287 return status; 288 } 289 290 // called by (py, ....) or ';py,...' comments 291 int Interp::py_execute(const char *cmd, bool as_file) 292 { 293 bp::object retval; 294 295 logPy("py_execute(%s)",cmd); 296 297 CHKS(!PYUSABLE, "py_execute(%s): Python plugin not initialized",cmd); 298 299 python_plugin->run_string(cmd, retval, as_file); 300 CHKS((python_plugin->plugin_status() == PLUGIN_EXCEPTION), 301 "py_execute(%s)%s:\n%s", cmd, 302 as_file ? " as file" : "", python_plugin->last_exception().c_str()); 303 return INTERP_OK; 304 } 305 306 pycontext::pycontext() : impl(new pycontext_impl) {} 307 pycontext::~pycontext() { delete impl; } 308 pycontext::pycontext(const pycontext &other) 309 : impl(new pycontext_impl(*other.impl)) {} 310 pycontext &pycontext::operator=(const pycontext &other) { 311 delete impl; 312 impl = new pycontext_impl(*other.impl); 313 return *this; 314 }