interpreter.cc 7.97 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// -*- mode: c++ -*-
// Copyright 2011, Florent Lamiraux, CNRS.
//
// This file is part of dynamic-graph-python.
// dynamic-graph is free software: you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// dynamic-graph is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Lesser Public License for more details.  You should have
// received a copy of the GNU Lesser General Public License along with
// dynamic-graph. If not, see <http://www.gnu.org/licenses/>.

#include <iostream>
olivier stasse's avatar
olivier stasse committed
18
#include "dynamic-graph/debug.h"
19
#include "dynamic-graph/python/interpreter.hh"
20
#include "link-to-python.hh"
21

22
23
std::ofstream dg_debugfile( "/tmp/dynamic-graph-traces.txt", std::ios::trunc&std::ios::out );

24
25
// Python initialization commands
namespace dynamicgraph {
olivier stasse's avatar
olivier stasse committed
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
namespace python {
static const std::string pythonPrefix[5] = {
  "import traceback\n",
  "def display(s): return str(s) if not s is None else None",
  "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"
  "        return s\n"
  "stdout_catcher = StdoutCatcher()\n"
  "import sys\n"
  "sys.stdout = stdout_catcher"
};
}
44
45
}

46
namespace dynamicgraph {
olivier stasse's avatar
olivier stasse committed
47
namespace python {
48

49
bool HandleErr(std::string & err,
olivier stasse's avatar
olivier stasse committed
50
51
52
               PyObject * traceback_format_exception,
               PyObject * globals_,
               int PythonInputType)
53
{
olivier stasse's avatar
olivier stasse committed
54
  dgDEBUGIN(15);
55
56
57
58
  err="";
  bool lres=false;

  if (PyErr_Occurred()) {
olivier stasse's avatar
olivier stasse committed
59

60
61
62
63
    PyObject *ptype, *pvalue, *ptraceback, *pyerr;
    PyErr_Fetch(&ptype, &pvalue, &ptraceback);
    if (ptraceback == NULL) {
      ptraceback = Py_None;
64
65
      // increase the Py_None count, to avoid a crash at the tuple destruction
      Py_INCREF(ptraceback);
66
67
68
69
70
71
72
    }
    PyObject* args = PyTuple_New(3);
    PyTuple_SET_ITEM(args, 0, ptype);
    PyTuple_SET_ITEM(args, 1, pvalue);
    PyTuple_SET_ITEM(args, 2, ptraceback);
    pyerr = PyObject_CallObject(traceback_format_exception, args);
    assert(PyList_Check(pyerr));
73
    Py_ssize_t size = PyList_GET_SIZE(pyerr);
74
    std::string stringRes;
75
76
    for (Py_ssize_t i=0; i<size; ++i)
      stringRes += std::string
olivier stasse's avatar
olivier stasse committed
77
          (PyString_AsString(PyList_GET_ITEM(pyerr, i)));
78
79
    Py_DecRef(pyerr);

80

81
82
    pyerr  = PyString_FromString(stringRes.c_str());
    err = PyString_AsString(pyerr);
olivier stasse's avatar
olivier stasse committed
83
    dgDEBUG(15) << "err: " << err << std::endl;
84
    Py_DecRef(pyerr);
85

86
    // Here if there is a syntax error and
87
88
89
90
    // 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.
    if (PyErr_GivenExceptionMatches(ptype, PyExc_SyntaxError) &&
olivier stasse's avatar
olivier stasse committed
91
92
93
94
95
        (PythonInputType==Py_eval_input))
    {
      dgDEBUG(15) << "Detected a syntax error " << std::endl;
      lres=false;
    }
96
97
98
    else
      lres=true;

99
100
101
    Py_CLEAR(args);

    PyErr_Clear();
102
  } else {
olivier stasse's avatar
olivier stasse committed
103
    dgDEBUG(15) << "no object generated but no error occured." << std::endl;
104
105
106
107
108
109
110
111
112
  }
  PyObject* stdout_obj = PyRun_String("stdout_catcher.fetch()",
                                      Py_eval_input, globals_,
                                      globals_);
  std::string out("");

  out = PyString_AsString(stdout_obj);
  // Local display for the robot (in debug mode or for the logs)
  if (out.length()!=0)
olivier stasse's avatar
olivier stasse committed
113
  { dgDEBUG(15) << std::endl; }
olivier stasse's avatar
olivier stasse committed
114
115
  else { dgDEBUG(15) << "No exception." << std::endl; }
  dgDEBUGOUT(15);
116
  Py_DecRef(stdout_obj);
117
118
119
  return lres;
}

120

olivier stasse's avatar
olivier stasse committed
121
122
123
124
Interpreter::Interpreter()
{
  // load python dynamic library
  // this is silly, but required to be able to import dl module.
125
#ifndef WIN32
olivier stasse's avatar
olivier stasse committed
126
  dlopen(libpython.c_str(), RTLD_LAZY | RTLD_GLOBAL);
127
#endif
olivier stasse's avatar
olivier stasse committed
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
  Py_Initialize();
  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());
  traceback_format_exception_ = PyDict_GetItemString
      (PyModule_GetDict(PyImport_AddModule("traceback")), "format_exception");
  assert(PyCallable_Check(traceback_format_exception_));
  Py_INCREF(traceback_format_exception_);
}
144

olivier stasse's avatar
olivier stasse committed
145
146
147
148
149
150
151
Interpreter::~Interpreter()
{
  //Py_DECREF(mainmod_);
  //Py_DECREF(globals_);
  //Py_DECREF(traceback_format_exception_);
  Py_Finalize();
}
152

olivier stasse's avatar
olivier stasse committed
153
154
155
156
157
158
std::string Interpreter::python( const std::string& command )
{
  std::string lerr(""),lout(""),lres("");
  python(command,lres,lout,lerr);
  return lres;
}
159

160

olivier stasse's avatar
olivier stasse committed
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
void Interpreter::python( const std::string& command, std::string& res,
                          std::string& out, std::string& err)
{
  res = "";
  out = "";
  err = "";

  std::cout << command.c_str() << std::endl;
  PyObject* result = PyRun_String(command.c_str(), Py_eval_input, globals_,
                                  globals_);
  // Check if the result is null.
  if (!result) {

    // Test if this is a syntax error (due to the evaluation of an expression)
    // else just output the problem.
    if (!HandleErr(err,
                   traceback_format_exception_, globals_,
                   Py_eval_input))
179
    {
olivier stasse's avatar
olivier stasse committed
180
181
182
183
184
185
186
187
      // 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.
      if (result == NULL) 
        HandleErr(err,
                  traceback_format_exception_, globals_,
                  Py_single_input);
188
      else 
olivier stasse's avatar
olivier stasse committed
189
190
        // If there is no error, make sure that the previous error message is erased.
        err="";
191
    }
olivier stasse's avatar
olivier stasse committed
192
193
194
    else 
    { dgDEBUG(15) << "Do not try a second time." << std::endl; }
  }
195

olivier stasse's avatar
olivier stasse committed
196
197
198
199
200
201
202
203
204
205
  PyObject* stdout_obj = 0;
  stdout_obj = PyRun_String("stdout_catcher.fetch()",
                            Py_eval_input, globals_,
                            globals_);
  out = PyString_AsString(stdout_obj);
  // Local display for the robot (in debug mode or for the logs)
  if (out.size()!=0)
    std::cout << "Output:" << out << std::endl;
  if (err.size()!=0)
    std::cout << "Error:" << err << std::endl;
206
  PyObject* result2 = PyObject_Repr(result);
olivier stasse's avatar
olivier stasse committed
207
208
  // If python cannot build a string representation of result
  // then results is equal to NULL. This will trigger a SEGV
209
  if (result2!=NULL)
olivier stasse's avatar
olivier stasse committed
210
211
  {
    dgDEBUG(15) << "For command :" << command << std::endl;
212
    res = PyString_AsString(result2);
olivier stasse's avatar
olivier stasse committed
213
214
215
216
    dgDEBUG(15) << "Result is: " << res <<std::endl;
    dgDEBUG(15) << "Out is: " << out <<std::endl;
    dgDEBUG(15) << "Err is :" << err << std::endl;
  }
217
  else
olivier stasse's avatar
olivier stasse committed
218
  { dgDEBUG(15) << "Result is empty" << std::endl; }
219
220
221
  Py_DecRef(stdout_obj);
  Py_DecRef(result2);
  Py_DecRef(result);
olivier stasse's avatar
olivier stasse committed
222
223
  return;
}
224

olivier stasse's avatar
olivier stasse committed
225
226
227
228
PyObject* Interpreter::globals()
{
  return globals_;
}
229

olivier stasse's avatar
olivier stasse committed
230
231
232
233
234
235
236
237
238
239
240
void Interpreter::runPythonFile( std::string filename )
{
  PyObject* pymainContext = globals_;
  PyRun_File(fopen( filename.c_str(),"r" ), filename.c_str(),
             Py_file_input, pymainContext,pymainContext);
  if (PyErr_Occurred())
  {
    std::cout << "Error occures..." << std::endl;
    PyErr_Print();
  }
}
241

olivier stasse's avatar
olivier stasse committed
242
243
244
245
246
247
248
249
250
251
252
void Interpreter::runMain( void )
{
  const char * argv [] = { "dg-embedded-pysh" };
  Py_Main(1,const_cast<char**>(argv));
}

std::string Interpreter::processStream(std::istream& stream, std::ostream& os)
{
  char line[10000]; sprintf(line, "%s", "\n");
  std::string command;
  std::streamsize maxSize = 10000;
253
#if 0
olivier stasse's avatar
olivier stasse committed
254
255
256
257
  while (line != std::string("")) {
    stream.getline(line, maxSize, '\n');
    command += std::string(line) + std::string("\n");
  };
258
#else
olivier stasse's avatar
olivier stasse committed
259
260
261
  os << "dg> ";
  stream.getline(line, maxSize, ';');
  command += std::string(line);
262
#endif
olivier stasse's avatar
olivier stasse committed
263
264
265
  return command;
}
} //namespace python
266
} // namespace dynamicgraph