From 1d505bed565d2a878eea2c2d5c8e0b9aca22068f Mon Sep 17 00:00:00 2001
From: Francois Keith <keith@lirmm.fr>
Date: Tue, 18 Mar 2014 00:06:22 +0100
Subject: [PATCH] Add a method allowing to execute a file and retrived the
 error (if any) via a string.

---
 CMakeLists.txt                              |  1 +
 include/dynamic-graph/python/interpreter.hh |  1 +
 src/interpreter.cc                          | 64 ++++++++++++++++++++-
 3 files changed, 64 insertions(+), 2 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index a32ff0a..ae34028 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -45,6 +45,7 @@ ADD_REQUIRED_DEPENDENCY("dynamic-graph >= 2.5.5-6")
 PKG_CONFIG_APPEND_LIBS("dynamic-graph-python")
 
 # Search for Boost.
+SET(BOOST_COMPONENTS python filesystem system thread program_options unit_test_framework)
 SEARCH_FOR_BOOST()
 
 # Make sure Boost.Filesystem v2 is used.
diff --git a/include/dynamic-graph/python/interpreter.hh b/include/dynamic-graph/python/interpreter.hh
index 5addec4..5d2e4f3 100644
--- a/include/dynamic-graph/python/interpreter.hh
+++ b/include/dynamic-graph/python/interpreter.hh
@@ -51,6 +51,7 @@ namespace dynamicgraph {
       /// \brief Method to exectue a python script.
       /// \param filename the filename
       void runPythonFile( std::string filename );
+      void runPythonFile( std::string filename, std::string& err);
       void runMain( void );
 
       /// \brief Process input stream to send relevant blocks to python
diff --git a/src/interpreter.cc b/src/interpreter.cc
index 9f1e2e5..0b97823 100644
--- a/src/interpreter.cc
+++ b/src/interpreter.cc
@@ -19,6 +19,13 @@
 #include "dynamic-graph/python/interpreter.hh"
 #include "link-to-python.hh"
 
+#include <boost/python/errors.hpp>
+#include <boost/python/object.hpp>
+#include <boost/python/handle.hpp>
+#include <boost/python/extract.hpp>
+
+using namespace boost::python;
+
 std::ofstream dg_debugfile( "/tmp/dynamic-graph-traces.txt", std::ios::trunc&std::ios::out );
 
 // Python initialization commands
@@ -136,6 +143,8 @@ Interpreter::Interpreter()
   PyRun_SimpleString(pythonPrefix[2].c_str());
   PyRun_SimpleString(pythonPrefix[3].c_str());
   PyRun_SimpleString(pythonPrefix[4].c_str());
+  PyRun_SimpleString("import linecache");
+
   traceback_format_exception_ = PyDict_GetItemString
       (PyModule_GetDict(PyImport_AddModule("traceback")), "format_exception");
   assert(PyCallable_Check(traceback_format_exception_));
@@ -229,13 +238,64 @@ PyObject* Interpreter::globals()
 
 void Interpreter::runPythonFile( std::string filename )
 {
+  std::string err = "";
+  runPythonFile(filename, err);
+}
+
+
+void Interpreter::runPythonFile( std::string filename, std::string& err)
+{
+  err = "";
   PyObject* pymainContext = globals_;
   PyObject* run = PyRun_FileExFlags(fopen( filename.c_str(),"r" ), filename.c_str(),
              Py_file_input, pymainContext,pymainContext, true, NULL);
   if (PyErr_Occurred())
   {
-    std::cout << "Error occures..." << std::endl;
-    PyErr_Print();
+    PyObject *ptype, *pvalue, *ptraceback;
+    PyErr_Fetch(&ptype, &pvalue, &ptraceback);
+
+    handle<> hTraceback(ptraceback);
+    object traceback(hTraceback);
+
+    //Extract error message
+    std::string strErrorMessage = extract<std::string>(pvalue);
+    std::ostringstream errstream;
+
+    //TODO does not work for now for a single command.
+    do
+      {
+	//Extract line number (top entry of call stack)
+	// if you want to extract another levels of call stack
+	// also process traceback.attr("tb_next") recurently
+	long lineno = extract<long> (traceback.attr("tb_lineno"));
+	std::string filename = extract<std::string>
+	  (traceback.attr("tb_frame").attr("f_code").attr("co_filename"));
+	std::string funcname = extract<std::string>
+	  (traceback.attr("tb_frame").attr("f_code").attr("co_name"));
+	errstream << " File \"" << filename  <<"\", line "
+		  << lineno << ", in "<< funcname << std::endl;
+
+	// get the corresponding line.
+	std::ostringstream cmd;
+	cmd << "linecache.getline('"<<filename<<"', "<<lineno <<")";
+	PyObject* line_obj = PyRun_String(cmd.str().c_str(),
+					  Py_eval_input, globals_, globals_);
+	std::string line = PyString_AsString(line_obj);
+	Py_DecRef(line_obj);
+
+	// remove the spaces at the beginning of the line.
+	size_t index = line.find_first_not_of (" \t");
+	errstream << "  " << line.substr(index, line.size()-index);
+	
+	// go to the next line.
+	traceback = traceback.attr("tb_next");
+      }
+    while (traceback);
+
+    // recreate the error message
+    errstream << strErrorMessage << std::endl;
+    err =errstream.str();
+    std::cerr << err;
   }
   Py_DecRef(run);
 }
-- 
GitLab