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>
Guilhem Saurel's avatar
Guilhem Saurel committed
11

olivier stasse's avatar
olivier stasse committed
12
#include "dynamic-graph/debug.h"
13
14
#include "dynamic-graph/python/interpreter.hh"

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

17
18
// Python initialization commands
namespace dynamicgraph {
olivier stasse's avatar
olivier stasse committed
19
namespace python {
20
21
static const std::string pythonPrefix[8] = {"from __future__ import print_function\n",
                                            "import traceback\n",
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
22
23
24
25
26
27
28
29
                                            "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
30
31
32
33
34
35
36
37
                                            "        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
38
  dgDEBUGIN(15);
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
39
40
  err = "";
  bool lres = false;
41

Guilhem Saurel's avatar
Guilhem Saurel committed
42
43
44
45
46
47
  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);
48

49
    // Here if there is a syntax error and
50
51
52
    // 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
53
    if (is_syntax_error && PythonInputType == Py_eval_input) {
olivier stasse's avatar
olivier stasse committed
54
      dgDEBUG(15) << "Detected a syntax error " << std::endl;
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
55
56
57
      lres = false;
    } else
      lres = true;
58

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

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

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

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

  // 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
103
104
105
    PyObject* poAttrList = PyObject_Dir(mainmod_);
    PyObject* poAttrIter = PyObject_GetIter(poAttrList);
    PyObject* poAttrName;
106

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

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

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

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

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

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

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

Guilhem Saurel's avatar
format    
Guilhem Saurel committed
132
133
134
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
135
136
  return lres;
}
137

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

143
  // Check if the command is not a python comment or empty.
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
144
  std::string::size_type iFirstNonWhite = command.find_first_not_of(" \t");
145
146
147
148
149
  // 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
150
151
  PyEval_RestoreThread(_pyState);

olivier stasse's avatar
olivier stasse committed
152
  std::cout << command.c_str() << std::endl;
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
153
  PyObject* result = PyRun_String(command.c_str(), Py_eval_input, globals_, globals_);
olivier stasse's avatar
olivier stasse committed
154
  // Check if the result is null.
Guilhem Saurel's avatar
Guilhem Saurel committed
155
  if (result == NULL) {
olivier stasse's avatar
olivier stasse committed
156
157
    // 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
158
    if (!HandleErr(err, globals_, Py_eval_input)) {
olivier stasse's avatar
olivier stasse committed
159
160
161
162
      // 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
163
164
      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
165
166
      else
        err = "";
Guilhem Saurel's avatar
Guilhem Saurel committed
167
    } else
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
168
      dgDEBUG(15) << "Do not try a second time." << std::endl;
olivier stasse's avatar
olivier stasse committed
169
  }
170

olivier stasse's avatar
olivier stasse committed
171
  PyObject* stdout_obj = 0;
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
172
  stdout_obj = PyRun_String("stdout_catcher.fetch()", Py_eval_input, globals_, globals_);
Guilhem Saurel's avatar
Guilhem Saurel committed
173
174
  out = obj_to_str(stdout_obj);
  Py_DECREF(stdout_obj);
olivier stasse's avatar
olivier stasse committed
175
  // Local display for the robot (in debug mode or for the logs)
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
176
177
  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
178
179
  // 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
180
181
182
  dgDEBUG(15) << "For command: " << command << std::endl;
  if (result != NULL) {
    res = obj_to_str(result);
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
183
    dgDEBUG(15) << "Result is: " << res << std::endl;
Guilhem Saurel's avatar
Guilhem Saurel committed
184
    Py_DECREF(result);
Guilhem Saurel's avatar
format    
Guilhem Saurel committed
185
  } else {
Guilhem Saurel's avatar
Guilhem Saurel committed
186
    dgDEBUG(15) << "Result is: empty" << std::endl;
olivier stasse's avatar
olivier stasse committed
187
  }
Guilhem Saurel's avatar
Guilhem Saurel committed
188
189
  dgDEBUG(15) << "Out is: " << out << std::endl;
  dgDEBUG(15) << "Err is :" << err << std::endl;
Joseph Mirabel's avatar
Joseph Mirabel committed
190
191
192

  _pyState = PyEval_SaveThread();

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

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

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

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

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

212
  err = "";
Guilhem Saurel's avatar
Guilhem Saurel committed
213
214
215
  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
216
    std::cerr << err << std::endl;
olivier stasse's avatar
olivier stasse committed
217
  }
Francois Keith's avatar
Francois Keith committed
218
  Py_DecRef(run);
Joseph Mirabel's avatar
Joseph Mirabel committed
219
220

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

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

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

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