interpreter.cc 8.26 KB
Newer Older
1
2
3
// -*- mode: c++ -*-
// Copyright 2011, Florent Lamiraux, CNRS.

4
5
6
7
8
9
#ifdef WIN32
#include <Windows.h>
#else
#include <dlfcn.h>
#endif

10
#include <iostream>
olivier stasse's avatar
olivier stasse committed
11
#include "dynamic-graph/debug.h"
12
13
#include "dynamic-graph/python/interpreter.hh"

Guilhem Saurel's avatar
format    
Guilhem Saurel committed
14
std::ofstream dg_debugfile("/tmp/dynamic-graph-traces.txt", std::ios::trunc& std::ios::out);
15

16
17
// Python initialization commands
namespace dynamicgraph {
olivier stasse's avatar
olivier stasse committed
18
namespace python {
19
20
static const std::string pythonPrefix[8] = {"from __future__ import print_function\n",
                                            "import traceback\n",
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
21
22
23
24
25
26
27
28
                                            "class StdoutCatcher:\n"
                                            "    def __init__(self):\n"
                                            "        self.data = ''\n"
                                            "    def write(self, stuff):\n"
                                            "        self.data = self.data + stuff\n"
                                            "    def fetch(self):\n"
                                            "        s = self.data[:]\n"
                                            "        self.data = ''\n"
Guilhem Saurel's avatar
Guilhem Saurel committed
29
30
31
32
33
34
35
36
                                            "        return s\n",
                                            "stdout_catcher = StdoutCatcher()\n",
                                            "stderr_catcher = StdoutCatcher()\n",
                                            "import sys\n",
                                            "sys.stdout = stdout_catcher",
                                            "sys.stderr = stderr_catcher"};

bool HandleErr(std::string& err, PyObject* globals_, int PythonInputType) {
olivier stasse's avatar
olivier stasse committed
37
  dgDEBUGIN(15);
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
38
39
  err = "";
  bool lres = false;
40

Guilhem Saurel's avatar
Guilhem Saurel committed
41
42
43
44
45
46
  if (PyErr_Occurred() != NULL) {
    bool is_syntax_error = PyErr_ExceptionMatches(PyExc_SyntaxError);
    PyErr_Print();
    PyObject* stderr_obj = PyRun_String("stderr_catcher.fetch()", Py_eval_input, globals_, globals_);
    err = obj_to_str(stderr_obj);
    Py_DECREF(stderr_obj);
47

48
    // Here if there is a syntax error and
49
50
51
    // and the interpreter input is set to Py_eval_input,
    // it is maybe a statement instead of an expression.
    // Therefore we indicate to re-evaluate the command.
Guilhem Saurel's avatar
Guilhem Saurel committed
52
    if (is_syntax_error && PythonInputType == Py_eval_input) {
olivier stasse's avatar
olivier stasse committed
53
      dgDEBUG(15) << "Detected a syntax error " << std::endl;
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
54
55
56
      lres = false;
    } else
      lres = true;
57

58
    PyErr_Clear();
59
  } else {
olivier stasse's avatar
olivier stasse committed
60
    dgDEBUG(15) << "no object generated but no error occured." << std::endl;
61
62
63
64
  }
  return lres;
}

Guilhem Saurel's avatar
format    
Guilhem Saurel committed
65
Interpreter::Interpreter() {
olivier stasse's avatar
olivier stasse committed
66
67
  // load python dynamic library
  // this is silly, but required to be able to import dl module.
68
#ifndef WIN32
69
  dlopen(PYTHON_LIBRARY, RTLD_LAZY | RTLD_GLOBAL);
70
#endif
olivier stasse's avatar
olivier stasse committed
71
  Py_Initialize();
72
#if PY_MAJOR_VERSION < 3 || PY_MINOR_VERSION < 7
Joseph Mirabel's avatar
Joseph Mirabel committed
73
  PyEval_InitThreads();
74
#endif
olivier stasse's avatar
olivier stasse committed
75
76
77
78
79
80
81
82
83
84
  mainmod_ = PyImport_AddModule("__main__");
  Py_INCREF(mainmod_);
  globals_ = PyModule_GetDict(mainmod_);
  assert(globals_);
  Py_INCREF(globals_);
  PyRun_SimpleString(pythonPrefix[0].c_str());
  PyRun_SimpleString(pythonPrefix[1].c_str());
  PyRun_SimpleString(pythonPrefix[2].c_str());
  PyRun_SimpleString(pythonPrefix[3].c_str());
  PyRun_SimpleString(pythonPrefix[4].c_str());
Guilhem Saurel's avatar
Guilhem Saurel committed
85
86
  PyRun_SimpleString(pythonPrefix[5].c_str());
  PyRun_SimpleString(pythonPrefix[6].c_str());
87
  PyRun_SimpleString(pythonPrefix[7].c_str());
88
89
  PyRun_SimpleString("import linecache");

Joseph Mirabel's avatar
Joseph Mirabel committed
90
91
  // Allow threads
  _pyState = PyEval_SaveThread();
olivier stasse's avatar
olivier stasse committed
92
}
93

Guilhem Saurel's avatar
format    
Guilhem Saurel committed
94
Interpreter::~Interpreter() {
Joseph Mirabel's avatar
Joseph Mirabel committed
95
  PyEval_RestoreThread(_pyState);
96
97
98
99
100
101

  // Ideally, we should call Py_Finalize but this is not really supported by
  // Python.
  // Instead, we merelly remove variables.
  // Code was taken here: https://github.com/numpy/numpy/issues/8097#issuecomment-356683953
  {
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
102
103
104
    PyObject* poAttrList = PyObject_Dir(mainmod_);
    PyObject* poAttrIter = PyObject_GetIter(poAttrList);
    PyObject* poAttrName;
105

Guilhem Saurel's avatar
format    
Guilhem Saurel committed
106
    while ((poAttrName = PyIter_Next(poAttrIter)) != NULL) {
Guilhem Saurel's avatar
Guilhem Saurel committed
107
      std::string oAttrName(obj_to_str(poAttrName));
108
109

      // Make sure we don't delete any private objects.
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
110
111
      if (oAttrName.compare(0, 2, "__") != 0 || oAttrName.compare(oAttrName.size() - 2, 2, "__") != 0) {
        PyObject* poAttr = PyObject_GetAttr(mainmod_, poAttrName);
112
113

        // Make sure we don't delete any module objects.
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
114
        if (poAttr && poAttr->ob_type != mainmod_->ob_type) PyObject_SetAttr(mainmod_, poAttrName, NULL);
115

Guilhem Saurel's avatar
Guilhem Saurel committed
116
        Py_DECREF(poAttr);
117
118
      }

Guilhem Saurel's avatar
Guilhem Saurel committed
119
      Py_DECREF(poAttrName);
120
121
    }

Guilhem Saurel's avatar
Guilhem Saurel committed
122
123
    Py_DECREF(poAttrIter);
    Py_DECREF(poAttrList);
124
125
126
127
  }

  Py_DECREF(mainmod_);
  Py_DECREF(globals_);
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
128
  // Py_Finalize();
olivier stasse's avatar
olivier stasse committed
129
}
130

Guilhem Saurel's avatar
format    
Guilhem Saurel committed
131
132
133
std::string Interpreter::python(const std::string& command) {
  std::string lerr(""), lout(""), lres("");
  python(command, lres, lout, lerr);
olivier stasse's avatar
olivier stasse committed
134
135
  return lres;
}
136

Guilhem Saurel's avatar
format    
Guilhem Saurel committed
137
void Interpreter::python(const std::string& command, std::string& res, std::string& out, std::string& err) {
olivier stasse's avatar
olivier stasse committed
138
139
140
141
  res = "";
  out = "";
  err = "";

142
  // Check if the command is not a python comment or empty.
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
143
  std::string::size_type iFirstNonWhite = command.find_first_not_of(" \t");
144
145
146
147
148
  // Empty command
  if (iFirstNonWhite == std::string::npos) return;
  // Command is a comment. Ignore it.
  if (command[iFirstNonWhite] == '#') return;

Joseph Mirabel's avatar
Joseph Mirabel committed
149
150
  PyEval_RestoreThread(_pyState);

olivier stasse's avatar
olivier stasse committed
151
  std::cout << command.c_str() << std::endl;
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
152
  PyObject* result = PyRun_String(command.c_str(), Py_eval_input, globals_, globals_);
olivier stasse's avatar
olivier stasse committed
153
  // Check if the result is null.
Guilhem Saurel's avatar
Guilhem Saurel committed
154
  if (result == NULL) {
olivier stasse's avatar
olivier stasse committed
155
156
    // Test if this is a syntax error (due to the evaluation of an expression)
    // else just output the problem.
Guilhem Saurel's avatar
Guilhem Saurel committed
157
    if (!HandleErr(err, globals_, Py_eval_input)) {
olivier stasse's avatar
olivier stasse committed
158
159
160
161
      // If this is a statement, re-parse the command.
      result = PyRun_String(command.c_str(), Py_single_input, globals_, globals_);

      // If there is still an error build the appropriate err string.
Guilhem Saurel's avatar
Guilhem Saurel committed
162
163
      if (result == NULL) HandleErr(err, globals_, Py_single_input);
      // If there is no error, make sure that the previous error message is erased.
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
164
165
      else
        err = "";
Guilhem Saurel's avatar
Guilhem Saurel committed
166
    } else
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
167
      dgDEBUG(15) << "Do not try a second time." << std::endl;
olivier stasse's avatar
olivier stasse committed
168
  }
169

olivier stasse's avatar
olivier stasse committed
170
  PyObject* stdout_obj = 0;
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
171
  stdout_obj = PyRun_String("stdout_catcher.fetch()", Py_eval_input, globals_, globals_);
Guilhem Saurel's avatar
Guilhem Saurel committed
172
173
  out = obj_to_str(stdout_obj);
  Py_DECREF(stdout_obj);
olivier stasse's avatar
olivier stasse committed
174
  // Local display for the robot (in debug mode or for the logs)
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
175
176
  if (out.size() != 0) std::cout << "Output:" << out << std::endl;
  if (err.size() != 0) std::cout << "Error:" << err << std::endl;
olivier stasse's avatar
olivier stasse committed
177
178
  // If python cannot build a string representation of result
  // then results is equal to NULL. This will trigger a SEGV
Guilhem Saurel's avatar
Guilhem Saurel committed
179
180
181
  dgDEBUG(15) << "For command: " << command << std::endl;
  if (result != NULL) {
    res = obj_to_str(result);
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
182
    dgDEBUG(15) << "Result is: " << res << std::endl;
Guilhem Saurel's avatar
Guilhem Saurel committed
183
    Py_DECREF(result);
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
184
  } else {
Guilhem Saurel's avatar
Guilhem Saurel committed
185
    dgDEBUG(15) << "Result is: empty" << std::endl;
olivier stasse's avatar
olivier stasse committed
186
  }
Guilhem Saurel's avatar
Guilhem Saurel committed
187
188
  dgDEBUG(15) << "Out is: " << out << std::endl;
  dgDEBUG(15) << "Err is :" << err << std::endl;
Joseph Mirabel's avatar
Joseph Mirabel committed
189
190
191

  _pyState = PyEval_SaveThread();

olivier stasse's avatar
olivier stasse committed
192
193
  return;
}
194

Guilhem Saurel's avatar
format    
Guilhem Saurel committed
195
PyObject* Interpreter::globals() { return globals_; }
196

Guilhem Saurel's avatar
format    
Guilhem Saurel committed
197
void Interpreter::runPythonFile(std::string filename) {
198
199
200
201
  std::string err = "";
  runPythonFile(filename, err);
}

Guilhem Saurel's avatar
format    
Guilhem Saurel committed
202
203
204
void Interpreter::runPythonFile(std::string filename, std::string& err) {
  FILE* pFile = fopen(filename.c_str(), "r");
  if (pFile == 0x0) {
205
206
207
208
    err = filename + " cannot be open";
    return;
  }

Joseph Mirabel's avatar
Joseph Mirabel committed
209
210
  PyEval_RestoreThread(_pyState);

211
  err = "";
Guilhem Saurel's avatar
Guilhem Saurel committed
212
213
214
  PyObject* run = PyRun_File(pFile, filename.c_str(), Py_file_input, globals_, globals_);
  if (run == NULL) {
    HandleErr(err, globals_, Py_file_input);
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
215
    std::cerr << err << std::endl;
olivier stasse's avatar
olivier stasse committed
216
  }
Francois Keith's avatar
Francois Keith committed
217
  Py_DecRef(run);
Joseph Mirabel's avatar
Joseph Mirabel committed
218
219

  _pyState = PyEval_SaveThread();
Guilhem Saurel's avatar
Guilhem Saurel committed
220
  fclose(pFile);
olivier stasse's avatar
olivier stasse committed
221
}
222

Guilhem Saurel's avatar
format    
Guilhem Saurel committed
223
void Interpreter::runMain(void) {
Joseph Mirabel's avatar
Joseph Mirabel committed
224
  PyEval_RestoreThread(_pyState);
Guilhem Saurel's avatar
Guilhem Saurel committed
225
#if PY_MAJOR_VERSION >= 3
Guilhem Saurel's avatar
Guilhem Saurel committed
226
227
  const Py_UNICODE* argv[] = {L"dg-embedded-pysh"};
  Py_Main(1, const_cast<Py_UNICODE**>(argv));
Guilhem Saurel's avatar
Guilhem Saurel committed
228
229
#else
  const char* argv[] = {"dg-embedded-pysh"};
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
230
  Py_Main(1, const_cast<char**>(argv));
Guilhem Saurel's avatar
Guilhem Saurel committed
231
#endif
Joseph Mirabel's avatar
Joseph Mirabel committed
232
  _pyState = PyEval_SaveThread();
olivier stasse's avatar
olivier stasse committed
233
234
}

Guilhem Saurel's avatar
format    
Guilhem Saurel committed
235
236
237
std::string Interpreter::processStream(std::istream& stream, std::ostream& os) {
  char line[10000];
  sprintf(line, "%s", "\n");
olivier stasse's avatar
olivier stasse committed
238
239
  std::string command;
  std::streamsize maxSize = 10000;
240
#if 0
olivier stasse's avatar
olivier stasse committed
241
242
243
244
  while (line != std::string("")) {
    stream.getline(line, maxSize, '\n');
    command += std::string(line) + std::string("\n");
  };
245
#else
olivier stasse's avatar
olivier stasse committed
246
247
248
  os << "dg> ";
  stream.getline(line, maxSize, ';');
  command += std::string(line);
249
#endif
olivier stasse's avatar
olivier stasse committed
250
251
  return command;
}
252

Guilhem Saurel's avatar
format    
Guilhem Saurel committed
253
254
}  // namespace python
}  // namespace dynamicgraph