interpreter.cc
Go to the documentation of this file.
1 // -*- mode: c++ -*-
2 // Copyright 2011, Florent Lamiraux, CNRS.
3 
4 #ifdef WIN32
5 #include <Windows.h>
6 #else
7 #include <dlfcn.h>
8 #endif
9 
10 #include <iostream>
11 
12 #include "dynamic-graph/debug.h"
14 
15 std::ofstream dg_debugfile("/tmp/dynamic-graph-traces.txt",
16  std::ios::trunc& std::ios::out);
17 
18 // Python initialization commands
19 namespace dynamicgraph {
20 namespace python {
21 static const std::string pythonPrefix[8] = {
22  "from __future__ import print_function\n",
23  "import traceback\n",
24  "class StdoutCatcher:\n"
25  " def __init__(self):\n"
26  " self.data = ''\n"
27  " def write(self, stuff):\n"
28  " self.data = self.data + stuff\n"
29  " def fetch(self):\n"
30  " s = self.data[:]\n"
31  " self.data = ''\n"
32  " return s\n",
33  "stdout_catcher = StdoutCatcher()\n",
34  "stderr_catcher = StdoutCatcher()\n",
35  "import sys\n",
36  "sys.stdout = stdout_catcher",
37  "sys.stderr = stderr_catcher"};
38 
39 bool HandleErr(std::string& err, PyObject* globals_, int PythonInputType) {
40  dgDEBUGIN(15);
41  err = "";
42  bool lres = false;
43 
44  if (PyErr_Occurred() != NULL) {
45  bool is_syntax_error = PyErr_ExceptionMatches(PyExc_SyntaxError);
46  PyErr_Print();
47  PyObject* stderr_obj = PyRun_String("stderr_catcher.fetch()", Py_eval_input,
48  globals_, globals_);
49  err = obj_to_str(stderr_obj);
50  Py_DECREF(stderr_obj);
51 
52  // Here if there is a syntax error and
53  // and the interpreter input is set to Py_eval_input,
54  // it is maybe a statement instead of an expression.
55  // Therefore we indicate to re-evaluate the command.
56  if (is_syntax_error && PythonInputType == Py_eval_input) {
57  dgDEBUG(15) << "Detected a syntax error " << std::endl;
58  lres = false;
59  } else
60  lres = true;
61 
62  PyErr_Clear();
63  } else {
64  dgDEBUG(15) << "no object generated but no error occured." << std::endl;
65  }
66  return lres;
67 }
68 
70  // load python dynamic library
71  // this is silly, but required to be able to import dl module.
72 #ifndef WIN32
73  dlopen(PYTHON_LIBRARY, RTLD_LAZY | RTLD_GLOBAL);
74 #endif
75  Py_Initialize();
76 #if PY_MAJOR_VERSION < 3 || PY_MINOR_VERSION < 7
77  PyEval_InitThreads();
78 #endif
79  mainmod_ = PyImport_AddModule("__main__");
80  Py_INCREF(mainmod_);
81  globals_ = PyModule_GetDict(mainmod_);
82  assert(globals_);
83  Py_INCREF(globals_);
84  PyRun_SimpleString(pythonPrefix[0].c_str());
85  PyRun_SimpleString(pythonPrefix[1].c_str());
86  PyRun_SimpleString(pythonPrefix[2].c_str());
87  PyRun_SimpleString(pythonPrefix[3].c_str());
88  PyRun_SimpleString(pythonPrefix[4].c_str());
89  PyRun_SimpleString(pythonPrefix[5].c_str());
90  PyRun_SimpleString(pythonPrefix[6].c_str());
91  PyRun_SimpleString(pythonPrefix[7].c_str());
92  PyRun_SimpleString("import linecache");
93 
94  // Allow threads
95  _pyState = PyEval_SaveThread();
96 }
97 
99  PyEval_RestoreThread(_pyState);
100 
101  // Ideally, we should call Py_Finalize but this is not really supported by
102  // Python.
103  // Instead, we merelly remove variables.
104  // Code was taken here:
105  // https://github.com/numpy/numpy/issues/8097#issuecomment-356683953
106  {
107  PyObject* poAttrList = PyObject_Dir(mainmod_);
108  PyObject* poAttrIter = PyObject_GetIter(poAttrList);
109  PyObject* poAttrName;
110 
111  while ((poAttrName = PyIter_Next(poAttrIter)) != NULL) {
112  std::string oAttrName(obj_to_str(poAttrName));
113 
114  // Make sure we don't delete any private objects.
115  if (oAttrName.compare(0, 2, "__") != 0 ||
116  oAttrName.compare(oAttrName.size() - 2, 2, "__") != 0) {
117  PyObject* poAttr = PyObject_GetAttr(mainmod_, poAttrName);
118 
119  // Make sure we don't delete any module objects.
120  if (poAttr && poAttr->ob_type != mainmod_->ob_type)
121  PyObject_SetAttr(mainmod_, poAttrName, NULL);
122 
123  Py_DECREF(poAttr);
124  }
125 
126  Py_DECREF(poAttrName);
127  }
128 
129  Py_DECREF(poAttrIter);
130  Py_DECREF(poAttrList);
131  }
132 
133  Py_DECREF(mainmod_);
134  Py_DECREF(globals_);
135  // Py_Finalize();
136 }
137 
138 std::string Interpreter::python(const std::string& command) {
139  std::string lerr(""), lout(""), lres("");
140  python(command, lres, lout, lerr);
141  return lres;
142 }
143 
144 void Interpreter::python(const std::string& command, std::string& res,
145  std::string& out, std::string& err) {
146  res = "";
147  out = "";
148  err = "";
149 
150  // Check if the command is not a python comment or empty.
151  std::string::size_type iFirstNonWhite = command.find_first_not_of(" \t");
152  // Empty command
153  if (iFirstNonWhite == std::string::npos) return;
154  // Command is a comment. Ignore it.
155  if (command[iFirstNonWhite] == '#') return;
156 
157  PyEval_RestoreThread(_pyState);
158 
159  std::cout << command.c_str() << std::endl;
160  PyObject* result =
161  PyRun_String(command.c_str(), Py_eval_input, globals_, globals_);
162  // Check if the result is null.
163  if (result == NULL) {
164  // Test if this is a syntax error (due to the evaluation of an expression)
165  // else just output the problem.
166  if (!HandleErr(err, globals_, Py_eval_input)) {
167  // If this is a statement, re-parse the command.
168  result =
169  PyRun_String(command.c_str(), Py_single_input, globals_, globals_);
170 
171  // If there is still an error build the appropriate err string.
172  if (result == NULL) HandleErr(err, globals_, Py_single_input);
173  // If there is no error, make sure that the previous error message is
174  // erased.
175  else
176  err = "";
177  } else
178  dgDEBUG(15) << "Do not try a second time." << std::endl;
179  }
180 
181  PyObject* stdout_obj = 0;
182  stdout_obj =
183  PyRun_String("stdout_catcher.fetch()", Py_eval_input, globals_, globals_);
184  out = obj_to_str(stdout_obj);
185  Py_DECREF(stdout_obj);
186  // Local display for the robot (in debug mode or for the logs)
187  if (out.size() != 0) std::cout << "Output:" << out << std::endl;
188  if (err.size() != 0) std::cout << "Error:" << err << std::endl;
189  // If python cannot build a string representation of result
190  // then results is equal to NULL. This will trigger a SEGV
191  dgDEBUG(15) << "For command: " << command << std::endl;
192  if (result != NULL) {
193  res = obj_to_str(result);
194  dgDEBUG(15) << "Result is: " << res << std::endl;
195  Py_DECREF(result);
196  } else {
197  dgDEBUG(15) << "Result is: empty" << std::endl;
198  }
199  dgDEBUG(15) << "Out is: " << out << std::endl;
200  dgDEBUG(15) << "Err is :" << err << std::endl;
201 
202  _pyState = PyEval_SaveThread();
203 
204  return;
205 }
206 
207 PyObject* Interpreter::globals() { return globals_; }
208 
209 void Interpreter::runPythonFile(std::string filename) {
210  std::string err = "";
211  runPythonFile(filename, err);
212 }
213 
214 void Interpreter::runPythonFile(std::string filename, std::string& err) {
215  FILE* pFile = fopen(filename.c_str(), "r");
216  if (pFile == 0x0) {
217  err = filename + " cannot be open";
218  return;
219  }
220 
221  PyEval_RestoreThread(_pyState);
222 
223  err = "";
224  PyObject* run =
225  PyRun_File(pFile, filename.c_str(), Py_file_input, globals_, globals_);
226  if (run == NULL) {
227  HandleErr(err, globals_, Py_file_input);
228  std::cerr << err << std::endl;
229  }
230  Py_DecRef(run);
231 
232  _pyState = PyEval_SaveThread();
233  fclose(pFile);
234 }
235 
237  PyEval_RestoreThread(_pyState);
238 #if PY_MAJOR_VERSION >= 3
239  const Py_UNICODE* argv[] = {L"dg-embedded-pysh"};
240  Py_Main(1, const_cast<Py_UNICODE**>(argv));
241 #else
242  const char* argv[] = {"dg-embedded-pysh"};
243  Py_Main(1, const_cast<char**>(argv));
244 #endif
245  _pyState = PyEval_SaveThread();
246 }
247 
248 std::string Interpreter::processStream(std::istream& stream, std::ostream& os) {
249  char line[10000];
250  sprintf(line, "%s", "\n");
251  std::string command;
252  std::streamsize maxSize = 10000;
253 #if 0
254  while (line != std::string("")) {
255  stream.getline(line, maxSize, '\n');
256  command += std::string(line) + std::string("\n");
257  };
258 #else
259  os << "dg> ";
260  stream.getline(line, maxSize, ';');
261  command += std::string(line);
262 #endif
263  return command;
264 }
265 
266 } // namespace python
267 } // namespace dynamicgraph
std::ofstream dg_debugfile("/tmp/dynamic-graph-traces.txt", std::ios::trunc &std::ios::out)
PyObject * globals_
Pointer to the dictionary of global variables.
Definition: interpreter.hh:53
std::string python(const std::string &command)
Method to start python interperter.
Definition: interpreter.cc:138
static const std::string pythonPrefix[8]
Definition: interpreter.cc:21
bool HandleErr(std::string &err, PyObject *globals_, int PythonInputType)
Definition: interpreter.cc:39
PyObject * globals()
Return a pointer to the dictionary of global variables.
Definition: interpreter.cc:207
std::string obj_to_str(PyObject *o)
Definition: python-compat.cc:4
#define dgDEBUG(level)
void runPythonFile(std::string filename)
Method to exectue a python script.
Definition: interpreter.cc:209
PyThreadState * _pyState
The Python thread state.
Definition: interpreter.hh:51
L
std::string processStream(std::istream &stream, std::ostream &os)
Process input stream to send relevant blocks to python.
Definition: interpreter.cc:248
#define dgDEBUGIN(level)


dynamic-graph-python
Author(s): Nicolas Mansard, Olivier Stasse
autogenerated on Sun Jun 25 2023 02:55:50