From 8fca0b1a7053beeb48eac0287ae2d62f0261bc87 Mon Sep 17 00:00:00 2001
From: florent <florent@laas.fr>
Date: Mon, 10 Jan 2011 15:48:33 +0100
Subject: [PATCH] Add method to interprete a string as a python command in
 class Interpreter.

    * include/dynamic-graph/interpreter.h,
    * src/CMakeLists.txt,
    * src/dgraph/interpreter.cpp,
    * tests/CMakeLists.txt,
    * tools/dg-python.cpp: new,
    * tools/CMakeLists.txt: this adds a dependency to  python in dynamic-graph.
---
 include/dynamic-graph/interpreter.h |  17 +++-
 src/CMakeLists.txt                  |  19 ++++
 src/dgraph/interpreter.cpp          |  57 ++++++++++-
 tests/CMakeLists.txt                |  11 +++
 tools/CMakeLists.txt                |  14 ++-
 tools/dg-python.cpp                 | 140 ++++++++++++++++++++++++++++
 6 files changed, 255 insertions(+), 3 deletions(-)
 create mode 100644 tools/dg-python.cpp

diff --git a/include/dynamic-graph/interpreter.h b/include/dynamic-graph/interpreter.h
index 95866c0..7d07543 100644
--- a/include/dynamic-graph/interpreter.h
+++ b/include/dynamic-graph/interpreter.h
@@ -168,7 +168,22 @@ namespace dynamicgraph
       \par[in] prompt: The prompt to be displayed.
 
     */
-    void shell( std::istream& sin, std::ostream& sout, const std::string& prompt="" );
+    void shell( std::istream& sin, std::ostream& sout,
+		const std::string& prompt="");
+    /*! \brief Method to start python interperter.
+      \par[in] sin: The input stream from which the command will be extracted.
+      \par[out] sout: The output stream to which the result will be displayed.
+      \par[in] prompt: The prompt to be displayed.
+
+    */
+    void python( std::istream& sin, std::ostream& sout,
+		 const std::string& prompt="" );
+
+    /*! \brief Method to exectue a python script.
+      \par[in] sin: The filename
+    */
+    void runPythonFile( std::string filename );
+
     void writeCompletionList(std::ostream& os);
 
 
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 5554891..c211f47 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -93,3 +93,22 @@ FOREACH(plugin_file ${plugins_list})
 
   INSTALL(TARGETS ${plugin} DESTINATION ${PLUGINDIR})
 ENDFOREACH(plugin_file)
+
+#
+# Python
+#
+INCLUDE(FindPythonLibs)
+IF (NOT ${PYTHONLIBS_FOUND} STREQUAL TRUE)
+   MESSAGE(FATAL_ERROR "Python has not been found.")
+ENDIF (NOT ${PYTHONLIBS_FOUND} STREQUAL TRUE)
+
+INCLUDE(FindPythonInterp)
+IF (NOT ${PYTHONINTERP_FOUND} STREQUAL TRUE)
+   MESSAGE(FATAL_ERROR "Python executable has not been found.")
+ENDIF (NOT ${PYTHONINTERP_FOUND} STREQUAL TRUE)
+
+# provide path to library libdynamic-graph.so
+LINK_DIRECTORIES(${DYNAMIC_GRAPH_LIBRARY_DIRS})
+ADD_DEFINITIONS(${DYNAMIC_GRAPH_CFLAGS})
+
+INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_PATH})
diff --git a/src/dgraph/interpreter.cpp b/src/dgraph/interpreter.cpp
index 64a7ee0..1cf1a09 100644
--- a/src/dgraph/interpreter.cpp
+++ b/src/dgraph/interpreter.cpp
@@ -22,6 +22,9 @@
 /* --- INCLUDE --------------------------------------------------------- */
 /* --------------------------------------------------------------------- */
 
+/* PYTHON */
+#include <Python.h>
+
 #include <boost/format.hpp>
 
 /* DYNAMIC-GRAPH */
@@ -38,7 +41,13 @@ using namespace dynamicgraph;
 /* --- CLASS ----------------------------------------------------------- */
 /* --------------------------------------------------------------------- */
 
-const std::string Interpreter::PROMPT_DEFAULT = "> ";
+namespace dynamicgraph {
+  const std::string Interpreter::PROMPT_DEFAULT = "> ";
+  static const std::string pythonPrefix[2] = {
+    "import sys",
+    "if '' not in sys.path: sys.path.append('')"
+  };
+}
 
 Interpreter::
 Interpreter( PluginLoader* dl__ )
@@ -503,6 +512,52 @@ shell( std::istream& sin, std::ostream& sout, const std::string& promptUser )
     }
 }
 
+void Interpreter::
+python( std::istream& sin, std::ostream& sout, const std::string& promptUser )
+{
+  Py_Initialize();
+  PyRun_SimpleString(pythonPrefix[0].c_str());
+  PyRun_SimpleString(pythonPrefix[1].c_str());
+  while( 1 )
+    {
+      if( promptUser.length() ) sout << promptUser; else sout << prompt;
+      const int SIZE = 16384;
+      char pythonLine[SIZE];
+      if( sin.eof() ) break;
+
+      sin.getline( pythonLine,SIZE-1 );
+
+      if( sin.gcount () >= SIZE-2 )
+	{
+	  sout << "!! Line size exceeded" << endl;
+	  do{ 
+	    sin.getline( pythonLine,SIZE-1 );
+	  }
+	  while ( sin.gcount () >= SIZE-2 );
+	}
+      else
+	{
+	  try{ 
+	    PyRun_SimpleString(pythonLine);
+	  }
+	  catch( exception& e ) { dgDEBUG(1) << e.what(); throw; }
+	  catch(...) { dgDEBUG(1) << "!! unknow!." <<endl; throw; }
+	}
+    }
+  Py_Finalize();
+  sout << std::endl;
+}
+
+void Interpreter::
+runPythonFile( std::string filename )
+{
+  Py_Initialize();
+  PyRun_SimpleString(pythonPrefix[0].c_str());
+  PyRun_SimpleString(pythonPrefix[1].c_str());
+  PyRun_SimpleFile(NULL, filename.c_str());
+  Py_Finalize();
+}
+
 ShellFunctionRegisterer::
 ShellFunctionRegisterer( const std::string& funName,
 			    const Interpreter::ShellBasicFunction& f)
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index d6dc7a1..862998d 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -15,6 +15,16 @@
 
 ADD_DEFINITIONS(-DDEBUG=2)
 
+#
+# Python
+#
+INCLUDE(FindPythonLibs)
+IF (NOT ${PYTHONLIBS_FOUND} STREQUAL TRUE)
+   MESSAGE(FATAL_ERROR "Python has not been found.")
+ELSE (NOT ${PYTHONLIBS_FOUND} STREQUAL TRUE)
+  MESSAGE(STATUS "PYTHON_LIBRARY="${PYTHON_LIBRARY})
+ENDIF (NOT ${PYTHONLIBS_FOUND} STREQUAL TRUE)
+
 # Add Boost path to include directories.
 INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS})
 
@@ -37,6 +47,7 @@ MACRO(DYNAMIC_GRAPH_TEST NAME)
   ADD_TEST(${NAME} ${RUNTIME_OUTPUT_DIRECTORY}/${NAME})
 
   TARGET_LINK_LIBRARIES(${NAME} ${PROJECT_NAME})
+  TARGET_LINK_LIBRARIES(${NAME} ${PYTHON_LIBRARY})
   ADD_DEPENDENCIES(${NAME} ${PROJECT_NAME})
 
   # Link against Boost.
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index 5c7f989..f930695 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -13,7 +13,17 @@
 # received a copy of the GNU Lesser General Public License along with
 # dynamic-graph. If not, see <http://www.gnu.org/licenses/>.
 
-SET(tools dg-shell)
+SET(tools dg-shell dg-python)
+
+#
+# Python
+#
+INCLUDE(FindPythonLibs)
+IF (NOT ${PYTHONLIBS_FOUND} STREQUAL TRUE)
+   MESSAGE(FATAL_ERROR "Python has not been found.")
+ELSE (NOT ${PYTHONLIBS_FOUND} STREQUAL TRUE)
+  MESSAGE(STATUS "PYTHON_LIBRARY="${PYTHON_LIBRARY})
+ENDIF (NOT ${PYTHONLIBS_FOUND} STREQUAL TRUE)
 
 include_directories(${Boost_INCLUDE_DIRS})
 
@@ -38,12 +48,14 @@ FOREACH(tool_name ${tools})
 
 	INCLUDE_DIRECTORIES(
 		${CMAKE_CURRENT_SOURCE_DIR}/../include
+		${PYTHON_INCLUDE_PATH}
 	)
 
 	LINK_DIRECTORIES(${${PROJECT_NAME}_BINARY_DIR}/lib)
 
 	TARGET_LINK_LIBRARIES(${EXECUTABLE_NAME}
 	  ${PROJECT_NAME})
+	TARGET_LINK_LIBRARIES(${EXECUTABLE_NAME} ${PYTHON_LIBRARY})
 
 	IF(UNIX)
 		TARGET_LINK_LIBRARIES(${EXECUTABLE_NAME} dl)
diff --git a/tools/dg-python.cpp b/tools/dg-python.cpp
new file mode 100644
index 0000000..c6fccd9
--- /dev/null
+++ b/tools/dg-python.cpp
@@ -0,0 +1,140 @@
+// Copyright 2010, François Bleibel, Thomas Moulard, Olivier Stasse,
+// JRL, CNRS/AIST.
+//
+// This file is part of dynamic-graph.
+// 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 <sstream>
+#include <string>
+#include <vector>
+
+#include <boost/filesystem.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+#include <boost/program_options.hpp>
+
+#include <dynamic-graph/entity.h>
+#include <dynamic-graph/plugin-loader.h>
+#include <dynamic-graph/interpreter.h>
+#include <dynamic-graph/debug.h>
+
+extern std::ofstream debugfile;
+
+// Factorize exception catching code.
+#define CATCH_EXCEPTIONS()						\
+  catch (std::exception& e)						\
+    {									\
+      std::cout								\
+	<< errorPrefix							\
+	<< e.what () << std::endl;					\
+    }									\
+  catch (const char* str)						\
+    {									\
+      std::cout << errorPrefix						\
+		<< "Unknown exception " << str << std::endl;		\
+    }									\
+  catch (...)								\
+    {									\
+      dgDEBUG(5) << errorPrefix << " Unknown exception " << std::endl;	\
+    }									\
+  struct e_n_d__w_i_t_h__s_e_m_i_c_o_l_o_n
+
+struct Options
+{
+  /// \brief Prologue path (a prologue is a script implicitly
+  /// evaluated at start-up).
+  std::string prologue;
+  /// \brief Files to be evaluated at start-up (left to right order).
+  std::vector<std::string> inputs;
+};
+
+int main (int argc, char** argv)
+{
+  dgDEBUGIN(15);
+
+  dgDEBUG(5) << " Loading..." << std::endl;
+  Options options;
+
+  // Parse options.
+  namespace po = boost::program_options;
+  po::options_description desc ("Allowed options");
+  desc.add_options ()
+    ("input,i",
+     po::value<std::vector<std::string> >(&options.inputs),
+     "file(s) evaluated at start-up")
+    ("help,h", "produce help message")
+    ;
+
+  po::positional_options_description p;
+  p.add("input", -1);
+  
+  po::variables_map vm;
+
+  try
+    {
+      po::store(po::command_line_parser(argc, argv).
+		options(desc).positional(p).run(), vm);
+      po::notify(vm);
+
+      if (vm.count ("help"))
+	{
+	  std::cout << "Usage: " << argv[0] << " [options]" << std::endl
+		    << desc << std::endl
+		    << "Report bugs to <hpp@laas.fr>" << std::endl;
+	  return 1;
+	}
+    }
+  catch (po::error& error)
+    {
+      std::cerr << "Error while parsing argument: "
+		<< error.what () << std::endl;
+      return 1;
+    }
+
+  std::list<std::string> inputs (options.inputs.begin (),
+				 options.inputs.end ());
+
+  // Load all input files.
+  BOOST_FOREACH (const std::string& pathStr, inputs)
+    {
+      boost::filesystem::path path (pathStr);
+
+      std::stringstream ss;
+      ss << "!! In file <" << path.file_string () << "> : ";
+      std::string errorPrefix = ss.str ();
+
+      try
+	{
+	  dynamicgraph::g_shell.runPythonFile (path.file_string ());
+	}
+      CATCH_EXCEPTIONS ();
+      return 0;
+    }
+
+  std::string errorPrefix = "";
+  while (1)
+    {
+      try
+	{
+	  dgDEBUG(5) << "Run shell." << std::endl;
+	  dynamicgraph::g_shell.python (std::cin, std::cout, ">>> ");
+	  dgDEBUG(5) << "Shell over." << std::endl;
+	  if (std::cin.eof ())
+	    break;
+	}
+      CATCH_EXCEPTIONS ();
+    }
+
+  dgDEBUGOUT(15);
+  return 0;
+}
-- 
GitLab