/ src / emc / rs274ngc / interp_python.cc
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  }