Commit 68e09d67 authored by olivier stasse's avatar olivier stasse
Browse files

Merge pull request #14 from francois-keith/master

Correct the run_python_file command + boost 1.50 compatible + win32 compatible
parents a0bfb6a7 da8d9931
Subproject commit c6f1a3d39037be5dfc98d0d1d67db7dfe0141863
Subproject commit 4e78d6f1ece19639c6ebdda18b2f081c784e3d04
......@@ -48,9 +48,6 @@ PKG_CONFIG_APPEND_LIBS("dynamic-graph-python")
SET(BOOST_COMPONENTS python filesystem system thread program_options unit_test_framework)
SEARCH_FOR_BOOST()
# Make sure Boost.Filesystem v2 is used.
ADD_DEFINITIONS(-DBOOST_FILESYSTEM_VERSION=2)
ADD_SUBDIRECTORY(src)
ADD_SUBDIRECTORY(include)
ADD_SUBDIRECTORY(doc)
......
......@@ -4,4 +4,118 @@ dynamic-graph-python
[![Build Status](https://travis-ci.org/stack-of-tasks/dynamic-graph-python.png?branch=master)](https://travis-ci.org/stack-of-tasks/dynamic-graph-python)
[![Coverage Status](https://coveralls.io/repos/stack-of-tasks/dynamic-graph-python/badge.png)](https://coveralls.io/r/stack-of-tasks/dynamic-graph-python)
Python bindings for dynamic-graph.
\ No newline at end of file
Python bindings for dynamic-graph.
**Warning:** this repository contains [Git
submodules][git-submodules]. Please clone this repository using the
`git clone --recursive` command. If you already have cloned the
repository, you can run `git submodule init && git submodule update`
to retrieve the submodules.
Documentation
-------------
To get started with this library, please read the [online Doxygen
documentation][doxygen-documentation].
It can also be generated locally by running the `make doc`
command. After the package is installed, the documentation will be
located in the `$prefix/share/doc/dynamic-graph` directoy where
`$prefix` is your installation prefix (`/usr/local` by default).
Getting Help
------------
Support is provided through:
* the HPP mailing-list: hpp@laas.fr
* the following HipChat room: http://www.hipchat.com/gh4wQrZeV
How can I install dynamic-graph?
--------------------------------
### Installing dependencies
The matrix abstract layer depends on several packages which
have to be available on your machine.
- Libraries:
- [Boost][] (>= 1.40)
Its detection is controled by the `BOOST_ROOT` variable, see next section
for more information.
- [Lapack][] library
Use the generic purpose `CMAKE_CXX_FLAGS` and `CMAKE_EXE_LINKER_FLAGS`
to insert the flags required for the compiler to find your Lapack library
if it is installed in a non-standard directory.
- [jrl-mal][] library
- [dynamic-graph][] library
- System tools:
- [CMake][] (>=2.6)
- [pkg-config][]
- usual compilation tools (GCC/G++, make, etc.)
If you are using Ubuntu, these tools are gathered in the `build-essential` package.
### Compiling and installing the package
The manual compilation requires two steps:
1. configuration of the build and generation of the build files
2. compilation of the sources and installation of the package
dynamic-graph uses [CMake][] to generate build files. It is
recommended to create a separate build directory:
```sh
mkdir _build # (1) Create a build directory
cd _build # (2) Go to the newly created build directory
cmake [options] .. # (3) Generate the build files
```
Options which can be passed to CMake are detailed in the next section.
```sh
make # (4) Compile the package
make test # (5) Execute the package tests
make install # (6) Install the package into the prefix (see step 3)
```
### Options
Additional options can be set on the command line through the
following command: `-D<option>=<value>`.
For instance: `cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo ..` will set
the `CMAKE_BUILD_TYPE` option to the value `RelWithDebInfo`.
Available options are:
- `CMAKE_BUILD_TYPE` set the build profile that should be used (debug,
release, etc.). We recommend `RelWithDebInfo` as it will provide
performances while keeping debugging symbols enabled.
- `CMAKE_INSTALL_PREFIX` set the installation prefix (the directory
where the software will be copied to after it has been compiled).
### Running the test suite
The test suite can be run from your build directory by running:
```sh
make test
```
Please open a ticket if some tests are failing on your computer, it
should not be the case.
Credits
-------
This package authors are credited in the [AUTHORS](AUTHORS) file.
......@@ -41,7 +41,7 @@ namespace dynamicgraph {
/// \brief Method to start python interperter.
/// \param command string to execute
/// Method deprecated, you *SHOULD* handle error messages.
std::string python( const std::string& command ) DYNAMIC_GRAPH_PYTHON_DEPRECATED;
DYNAMIC_GRAPH_PYTHON_DEPRECATED std::string python( const std::string& command );
/// \brief Method to start python interperter.
/// \param command string to execute, result, stdout, stderr strings
......
......@@ -37,7 +37,9 @@ ADD_LIBRARY(${LIBRARY_NAME}
TARGET_LINK_LIBRARIES(${LIBRARY_NAME}
${PYTHON_LIBRARY})
TARGET_LINK_LIBRARIES(${LIBRARY_NAME} ${Boost_LIBRARIES})
IF(UNIX)
TARGET_LINK_LIBRARIES(${LIBRARY_NAME} ${Boost_LIBRARIES})
ENDIF(UNIX)
SET_TARGET_PROPERTIES(${LIBRARY_NAME} PROPERTIES SOVERSION ${PROJECT_VERSION})
PKG_CONFIG_USE_DEPENDENCY(${LIBRARY_NAME} dynamic-graph)
......
......@@ -23,8 +23,11 @@
#include <boost/python/object.hpp>
#include <boost/python/handle.hpp>
#include <boost/python/extract.hpp>
#include <boost/python/str.hpp>
#include <boost/python/import.hpp>
using namespace boost::python;
namespace py=boost::python;
std::ofstream dg_debugfile( "/tmp/dynamic-graph-traces.txt", std::ios::trunc&std::ios::out );
......@@ -53,6 +56,10 @@ static const std::string pythonPrefix[5] = {
namespace dynamicgraph {
namespace python {
// Parse a python exception and return the corresponding error message
// http://thejosephturner.com/blog/post/embedding-python-in-c-applications-with-boostpython-part-2/
std::string parse_python_exception();
bool HandleErr(std::string & err,
PyObject * traceback_format_exception,
PyObject * globals_,
......@@ -63,7 +70,6 @@ bool HandleErr(std::string & err,
bool lres=false;
if (PyErr_Occurred()) {
PyObject *ptype, *pvalue, *ptraceback, *pyerr;
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
if (ptraceback == NULL) {
......@@ -242,60 +248,24 @@ void Interpreter::runPythonFile( std::string filename )
runPythonFile(filename, err);
}
void Interpreter::runPythonFile( std::string filename, std::string& err)
{
FILE* pFile = fopen( filename.c_str(),"r" );
if (pFile==0x0)
{
err = filename + " cannot be open";
return;
}
err = "";
PyObject* pymainContext = globals_;
PyObject* run = PyRun_FileExFlags(fopen( filename.c_str(),"r" ), filename.c_str(),
PyObject* run = PyRun_FileExFlags(pFile, filename.c_str(),
Py_file_input, pymainContext,pymainContext, true, NULL);
if (PyErr_Occurred())
{
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;
err = parse_python_exception();
std::cerr << err << std::endl;;
}
Py_DecRef(run);
}
......@@ -323,5 +293,49 @@ std::string Interpreter::processStream(std::istream& stream, std::ostream& os)
#endif
return command;
}
std::string parse_python_exception()
{
PyObject *type_ptr = NULL, *value_ptr = NULL, *traceback_ptr = NULL;
PyErr_Fetch(&type_ptr, &value_ptr, &traceback_ptr);
std::string ret("Unfetchable Python error");
if(type_ptr != NULL)
{
py::handle<> h_type(type_ptr);
py::str type_pstr(h_type);
py::extract<std::string> e_type_pstr(type_pstr);
if(e_type_pstr.check())
ret = e_type_pstr();
else
ret = "Unknown exception type";
}
if(value_ptr != NULL)
{
py::handle<> h_val(value_ptr);
py::str a(h_val);
py::extract<std::string> returned(a);
if(returned.check())
ret += ": " + returned();
else
ret += std::string(": Unparseable Python error: ");
}
if(traceback_ptr != NULL)
{
py::handle<> h_tb(traceback_ptr);
py::object tb(py::import("traceback"));
py::object fmt_tb(tb.attr("format_tb"));
py::object tb_list(fmt_tb(h_tb));
py::object tb_str(py::str("\n").join(tb_list));
py::extract<std::string> returned(tb_str);
if(returned.check())
ret += ": " + returned();
else
ret += std::string(": Unparseable Python traceback");
}
return ret;
}
} //namespace python
} // namespace dynamicgraph
......@@ -38,9 +38,11 @@ TARGET_LINK_LIBRARIES(${EXECUTABLE_NAME} dynamic-graph-python)
ADD_TEST(${EXECUTABLE_NAME} ${EXECUTABLE_NAME})
ADD_CUSTOM_COMMAND(TARGET interpreter-test-runfile POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/unitTesting/test_python_ok.py
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/unitTesting/test_python-ok.py
${CMAKE_BINARY_DIR}/unitTesting
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/unitTesting/test_python_error.py
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/unitTesting/test_python-name_error.py
${CMAKE_BINARY_DIR}/unitTesting
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/unitTesting/test_python-syntax_error.py
${CMAKE_BINARY_DIR}/unitTesting
)
......@@ -4,6 +4,25 @@
#include "dynamic-graph/python/interpreter.hh"
bool testFile(const std::string & filename,
const std::string & expectedOutput,
int numTest)
{
std::string err = "";
dynamicgraph::python::Interpreter interp;
for (int i=0; i<numTest; ++i)
{
interp.runPythonFile(filename, err);
if (err != expectedOutput)
{
std::cerr << "At iteration " << i << ", the output was not the one expected:" << std::endl;
std::cerr << " expected: " << expectedOutput << std::endl;
std::cerr << " err: " << err << std::endl;
return false;
}
}
return true;
}
int main(int argc, char ** argv)
{
......@@ -14,35 +33,18 @@ int main(int argc, char ** argv)
if (argc > 1)
numTest = atoi(argv[1]);
std::string empty_err = "";
dynamicgraph::python::Interpreter interp;
for (int i=0; i<numTest; ++i)
{
interp.runPythonFile("test_python_ok.py", empty_err);
if (empty_err != "")
{
std::cerr << "At iteration " << i << ", the error was not empty:" << std::endl;
std::cerr << " err " << empty_err << std::endl;
return -1;
}
}
// check that the error remains the same, despite of the number of executions
std::string old_err;
interp.runPythonFile("test_python_error.py", old_err);
std::string new_err = old_err;
for (int i=0; i<numTest; ++i)
{
interp.runPythonFile("test_python_error.py", new_err);
if (old_err != new_err)
{
std::cerr << "At iteration " << i << ", the error changed:" << std::endl;
std::cerr << " old " << old_err << std::endl;
std::cerr << " new " << new_err << std::endl;
return -1;
}
}
return 0;
bool res = true;
res = testFile("test_python-ok.py", "", numTest) && res;
res = testFile("unexistant_file.py",
"unexistant_file.py cannot be open",
numTest) && res;
res = testFile("test_python-name_error.py",
std::string("<type 'exceptions.NameError'>: name 're' is not defined:")+
" File \"test_python-name_error.py\", line 6, in <module>\n" +
" pathList = re.split(':', pkgConfigPath)\n", numTest) && res;
res = testFile("test_python-syntax_error.py",
std::string("<type 'exceptions.SyntaxError'>: ('invalid syntax', ")+
"('test_python-syntax_error.py', 1, 11, "+
"'hello world\\n'))", numTest) && res;
return (res?0:1);
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment