diff --git a/CMakeLists.txt b/CMakeLists.txt index 8dbf1a490f8f16981f95a12a5b77e32c1c1b1344..7fee2ed596728369effb993f75db4f10620ab4a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,7 +24,7 @@ SET(PROJECT_NAME dynamic-graph-python) SET(PROJECT_DESCRIPTION "Dynamic graph library Python bindings") SET(PROJECT_URL "http://github.com/jrl-umi3218/dynamic-graph-python") -SET(CUSTOM_HEADER_DIR "${PROJECT_NAME}") +SET(CUSTOM_HEADER_DIR "dynamic-graph/python") # Defines paths. SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib) @@ -35,5 +35,10 @@ SETUP_PROJECT() ADD_REQUIRED_DEPENDENCY("dynamic-graph >= 1.0") ADD_SUBDIRECTORY(src) +ADD_SUBDIRECTORY(include) ADD_SUBDIRECTORY(doc) + +# Search for Boost. +SEARCH_FOR_BOOST() + SETUP_PROJECT_FINALIZE() diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..5e93dfe82328e35e01529076273ce895c776a060 --- /dev/null +++ b/include/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright 2010, 2011, Florent Lamiraux, CNRS +# +# This file is part of dynamic-graph-python. +# dynamic-graph-python 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-python 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-python. If not, see <http://www.gnu.org/licenses/>. + +include(../cmake/header.cmake) + +# Headers list. +SET(${PROJECT_NAME}_HEADERS + interpreter.hh +) diff --git a/include/dynamic-graph/python/api.hh b/include/dynamic-graph/python/api.hh new file mode 100644 index 0000000000000000000000000000000000000000..a28d7f39573ec39d4d2ba8d9e49d5ab969510137 --- /dev/null +++ b/include/dynamic-graph/python/api.hh @@ -0,0 +1,20 @@ +// -*- 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/>. + +#ifndef DYNAMIC_GRAPH_PYTHON_API_HH +# define DYNAMIC_GRAPH_PYTHON_API_HH +# include <dynamic-graph/python/config.hh> +#endif //DYNAMIC_GRAPH_PYTHON_API_HH diff --git a/include/dynamic-graph/python/interpreter.hh b/include/dynamic-graph/python/interpreter.hh new file mode 100644 index 0000000000000000000000000000000000000000..748d1077c30b3ca5683e94316c5142f393a9be74 --- /dev/null +++ b/include/dynamic-graph/python/interpreter.hh @@ -0,0 +1,60 @@ +// -*- 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/>. + +#undef _POSIX_C_SOURCE +#undef _XOPEN_SOURCE +#include <Python.h> +#include <string> +#include "dynamic-graph/python/api.hh" + +#ifndef DYNAMIC_GRAPH_PYTHON_INTERPRETER_H +# define DYNAMIC_GRAPH_PYTHON_INTERPRETER_H + +namespace dynamicgraph { + namespace python { + /// + /// This class implements a basis python interpreter. + /// + /// String sent to method python are interpreted by an onboard python + /// interpreter. + class DYNAMIC_GRAPH_PYTHON_DLLAPI Interpreter + { + public: + Interpreter(); + ~Interpreter(); + /// \brief Method to start python interperter. + /// \param command string to execute + std::string python( const std::string& command ); + + /// \brief Method to exectue a python script. + /// \param filename the filename + void runPythonFile( std::string filename ); + + /// \brief Process input stream to send relevant blocks to python + /// \param stream input stream + std::string processStream(std::istream& stream, std::ostream& os); + + private: + /// Pointer to the dictionary of global variables + PyObject* globals_; + /// Pointer to the dictionary of local variables + PyObject* locals_; + PyObject* mainmod_; + PyObject* traceback_format_exception_; + }; + } // namespace python +} // namespace dynamicgraph +#endif // DYNAMIC_GRAPH_PYTHON_INTERPRETER_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 514d193807b3b4b91cc10ba8a19fca230156d3a0..7b1328457f0d3e79fcf492d8ee6d6437712ccfe0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -26,10 +26,36 @@ IF (NOT ${PYTHONINTERP_FOUND} STREQUAL TRUE) MESSAGE(FATAL_ERROR "Python executable has not been found.") ENDIF (NOT ${PYTHONINTERP_FOUND} STREQUAL TRUE) +INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS}) + # provide path to library libdynamic-graph.so LINK_DIRECTORIES(${DYNAMIC_GRAPH_LIBRARY_DIRS}) ADD_DEFINITIONS(${DYNAMIC_GRAPH_CFLAGS}) +# +# +# Python interpreter +# +# +SET(LIBRARY_NAME ${PROJECT_NAME}) +ADD_LIBRARY(${LIBRARY_NAME} + SHARED + interpreter.cc) + +SET_TARGET_PROPERTIES(${LIBRARY_NAME} PROPERTIES SOVERSION ${PROJECT_VERSION}) +PKG_CONFIG_USE_DEPENDENCY(${LIBRARY_NAME} dynamic-graph) + +INSTALL(TARGETS ${LIBRARY_NAME} + DESTINATION lib) + +SET(EXECUTABLE_NAME dg-python) +ADD_EXECUTABLE(${EXECUTABLE_NAME} dg-python.cc) +TARGET_LINK_LIBRARIES(${EXECUTABLE_NAME} + ${LIBRARY_NAME} + ${Boost_LIBRARIES} + ${PYTHON_LIBRARY}) +INSTALL(TARGETS dg-python DESTINATION bin) + # # # Python bindings diff --git a/src/dg-python.cc b/src/dg-python.cc new file mode 100644 index 0000000000000000000000000000000000000000..dd41db984f473bb5aebdade75672f16c40080d51 --- /dev/null +++ b/src/dg-python.cc @@ -0,0 +1,134 @@ +// 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 <sstream> +#include <string> +#include <vector> +#include <list> + +#include <boost/filesystem.hpp> +#include <boost/foreach.hpp> +#include <boost/format.hpp> +#include <boost/program_options.hpp> + +#include <dynamic-graph/debug.h> + +#include "dynamic-graph/python/interpreter.hh" + +// 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). +}; + +int main (int argc, char** argv) +{ + dgDEBUGIN(15); + + dgDEBUG(5) << " Loading..." << std::endl; + + // Parse options. + std::vector<std::string> inputs; + namespace po = boost::program_options; + po::options_description desc ("Allowed options"); + desc.add_options () + ("input,i", + po::value<std::vector<std::string> >(&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> inputList (inputs.begin (), + inputs.end ()); + + // Load all input files. + dynamicgraph::python::Interpreter interpreter; + BOOST_FOREACH (const std::string& pathStr, inputList) + { + boost::filesystem::path path (pathStr); + + std::stringstream ss; + ss << "!! In file <" << path.file_string () << "> : "; + std::string errorPrefix = ss.str (); + + try + { + interpreter.runPythonFile (path.file_string ()); + } + CATCH_EXCEPTIONS (); + return 0; + } + + std::string errorPrefix = ""; + std::string command; + while(1) { + command = interpreter.processStream(std::cin, std::cout); + if (command != "\n") { + std::string result = interpreter.python(command); + if (result != "None") { + std::cout << result << std::endl; + } + } + if (std::cin.eof ()) + break; + } + std::cout << std::endl; + return 0; +} diff --git a/src/interpreter.cc b/src/interpreter.cc new file mode 100644 index 0000000000000000000000000000000000000000..98493825b636e44e6833c8208efc5ac83f0f5fc6 --- /dev/null +++ b/src/interpreter.cc @@ -0,0 +1,125 @@ +// -*- 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> +#include "dynamic-graph/python/interpreter.hh" + +// Python initialization commands +namespace dynamicgraph { + namespace python { + static const std::string pythonPrefix[4] = { + "import sys\n", + "import traceback\n", + "if '' not in sys.path: sys.path.append('')\n", + "sys.argv = ['']\n", + }; + } +} + +using dynamicgraph::python::Interpreter; + +Interpreter::Interpreter() +{ + 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()); + traceback_format_exception_ = PyDict_GetItemString + (PyModule_GetDict(PyImport_AddModule("traceback")), "format_exception"); + assert(PyCallable_Check(traceback_format_exception_)); + Py_INCREF(traceback_format_exception_); +} + +Interpreter::~Interpreter() +{ + Py_DECREF(mainmod_); + Py_DECREF(globals_); + Py_DECREF(traceback_format_exception_); + Py_Finalize(); +} + +std::string Interpreter::python( const std::string& command ) +{ + PyObject* result = PyRun_String(command.c_str(), Py_eval_input, globals_, + globals_); + if (!result) { + PyErr_Clear(); + result = PyRun_String(command.c_str(), Py_single_input, globals_, + globals_); + } + if (result == NULL) { + if (PyErr_Occurred()) { + PyObject *ptype, *pvalue, *ptraceback; + PyErr_Fetch(&ptype, &pvalue, &ptraceback); + if (ptraceback == NULL) { + ptraceback = Py_None; + } + PyObject* args = PyTuple_New(3); + PyTuple_SET_ITEM(args, 0, ptype); + PyTuple_SET_ITEM(args, 1, pvalue); + PyTuple_SET_ITEM(args, 2, ptraceback); + result = PyObject_CallObject(traceback_format_exception_, args); + assert(PyList_Check(result)); + unsigned int size = PyList_GET_SIZE(result); + std::string stringRes; + for (unsigned int i=0; i<size; i++) { + stringRes += std::string(PyString_AsString(PyList_GET_ITEM(result, i))); + } + result = PyString_FromString(stringRes.c_str()); + PyErr_Clear(); + } else { + std::cout << "Result is NULL but no error occurred." << std::endl; + } + } else { + result = PyObject_Str(result); + } + std::string value = PyString_AsString(result); + return value; +} + +void Interpreter::runPythonFile( std::string filename ) +{ + Py_Finalize(); + Py_Initialize(); + PyRun_SimpleString(pythonPrefix[0].c_str()); + PyRun_SimpleString(pythonPrefix[1].c_str()); + PyRun_SimpleString(pythonPrefix[2].c_str()); + PyRun_SimpleFile(NULL, filename.c_str()); +} + +std::string Interpreter::processStream(std::istream& stream, std::ostream& os) +{ + char line[10000]; sprintf(line, "%s", "\n"); + std::string command; + std::streamsize maxSize = 10000; +#if 0 + while (line != std::string("")) { + stream.getline(line, maxSize, '\n'); + command += std::string(line) + std::string("\n"); + }; +#else + os << "dg> "; + stream.getline(line, maxSize, ';'); + command += std::string(line); +#endif + return command; +}