Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • ostasse/dynamic-graph-python
  • gsaurel/dynamic-graph-python
  • stack-of-tasks/dynamic-graph-python
3 results
Show changes
Showing
with 386 additions and 706 deletions
// -*- mode: c++ -*-
// Copyright 2011, Florent Lamiraux, CNRS.
#ifdef WIN32
#include <Windows.h>
#else
#include <dlfcn.h>
#endif
#include <iostream>
#include "dynamic-graph/debug.h"
#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>
#include <boost/python/str.hpp>
#include <boost/python/import.hpp>
namespace py = boost::python;
std::ofstream dg_debugfile("/tmp/dynamic-graph-traces.txt", std::ios::trunc& std::ios::out);
std::ofstream dg_debugfile("/tmp/dynamic-graph-traces.txt",
std::ios::trunc& std::ios::out);
// Python initialization commands
namespace dynamicgraph {
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"};
}
} // namespace dynamicgraph
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_, int PythonInputType) {
static const std::string pythonPrefix[8] = {
"from __future__ import print_function\n",
"import traceback\n",
"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",
"stderr_catcher = StdoutCatcher()\n",
"import sys\n",
"sys.stdout = stdout_catcher",
"sys.stderr = stderr_catcher"};
bool HandleErr(std::string& err, PyObject* globals_, int PythonInputType) {
dgDEBUGIN(15);
err = "";
bool lres = false;
if (PyErr_Occurred()) {
PyObject *ptype, *pvalue, *ptraceback, *pyerr;
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
if (ptraceback == NULL) {
ptraceback = Py_None;
// increase the Py_None count, to avoid a crash at the tuple destruction
Py_INCREF(ptraceback);
}
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));
Py_ssize_t size = PyList_GET_SIZE(pyerr);
std::string stringRes;
for (Py_ssize_t i = 0; i < size; ++i) stringRes += std::string(PyString_AsString(PyList_GET_ITEM(pyerr, i)));
Py_DecRef(pyerr);
pyerr = PyString_FromString(stringRes.c_str());
err = PyString_AsString(pyerr);
dgDEBUG(15) << "err: " << err << std::endl;
Py_DecRef(pyerr);
if (PyErr_Occurred() != NULL) {
bool is_syntax_error = PyErr_ExceptionMatches(PyExc_SyntaxError);
PyErr_Print();
PyObject* stderr_obj = PyRun_String("stderr_catcher.fetch()", Py_eval_input,
globals_, globals_);
err = obj_to_str(stderr_obj);
Py_DECREF(stderr_obj);
// Here if there is a syntax error and
// 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) && (PythonInputType == Py_eval_input)) {
if (is_syntax_error && PythonInputType == Py_eval_input) {
dgDEBUG(15) << "Detected a syntax error " << std::endl;
lres = false;
} else
lres = true;
Py_CLEAR(args);
PyErr_Clear();
} else {
dgDEBUG(15) << "no object generated but no error occured." << std::endl;
}
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) {
dgDEBUG(15) << std::endl;
} else {
dgDEBUG(15) << "No exception." << std::endl;
}
dgDEBUGOUT(15);
Py_DecRef(stdout_obj);
return lres;
}
......@@ -108,10 +70,12 @@ Interpreter::Interpreter() {
// load python dynamic library
// this is silly, but required to be able to import dl module.
#ifndef WIN32
dlopen(libpython.c_str(), RTLD_LAZY | RTLD_GLOBAL);
dlopen(PYTHON_LIBRARY, RTLD_LAZY | RTLD_GLOBAL);
#endif
Py_Initialize();
#if PY_MAJOR_VERSION < 3 || PY_MINOR_VERSION < 7
PyEval_InitThreads();
#endif
mainmod_ = PyImport_AddModule("__main__");
Py_INCREF(mainmod_);
globals_ = PyModule_GetDict(mainmod_);
......@@ -122,13 +86,11 @@ Interpreter::Interpreter() {
PyRun_SimpleString(pythonPrefix[2].c_str());
PyRun_SimpleString(pythonPrefix[3].c_str());
PyRun_SimpleString(pythonPrefix[4].c_str());
PyRun_SimpleString(pythonPrefix[5].c_str());
PyRun_SimpleString(pythonPrefix[6].c_str());
PyRun_SimpleString(pythonPrefix[7].c_str());
PyRun_SimpleString("import linecache");
traceback_format_exception_ =
PyDict_GetItemString(PyModule_GetDict(PyImport_AddModule("traceback")), "format_exception");
assert(PyCallable_Check(traceback_format_exception_));
Py_INCREF(traceback_format_exception_);
// Allow threads
_pyState = PyEval_SaveThread();
}
......@@ -139,35 +101,37 @@ Interpreter::~Interpreter() {
// Ideally, we should call Py_Finalize but this is not really supported by
// Python.
// Instead, we merelly remove variables.
// Code was taken here: https://github.com/numpy/numpy/issues/8097#issuecomment-356683953
// Code was taken here:
// https://github.com/numpy/numpy/issues/8097#issuecomment-356683953
{
PyObject* poAttrList = PyObject_Dir(mainmod_);
PyObject* poAttrIter = PyObject_GetIter(poAttrList);
PyObject* poAttrName;
while ((poAttrName = PyIter_Next(poAttrIter)) != NULL) {
std::string oAttrName(PyString_AS_STRING(poAttrName));
std::string oAttrName(obj_to_str(poAttrName));
// Make sure we don't delete any private objects.
if (oAttrName.compare(0, 2, "__") != 0 || oAttrName.compare(oAttrName.size() - 2, 2, "__") != 0) {
if (oAttrName.compare(0, 2, "__") != 0 ||
oAttrName.compare(oAttrName.size() - 2, 2, "__") != 0) {
PyObject* poAttr = PyObject_GetAttr(mainmod_, poAttrName);
// Make sure we don't delete any module objects.
if (poAttr && poAttr->ob_type != mainmod_->ob_type) PyObject_SetAttr(mainmod_, poAttrName, NULL);
if (poAttr && poAttr->ob_type != mainmod_->ob_type)
PyObject_SetAttr(mainmod_, poAttrName, NULL);
Py_DecRef(poAttr);
Py_DECREF(poAttr);
}
Py_DecRef(poAttrName);
Py_DECREF(poAttrName);
}
Py_DecRef(poAttrIter);
Py_DecRef(poAttrList);
Py_DECREF(poAttrIter);
Py_DECREF(poAttrList);
}
Py_DECREF(mainmod_);
Py_DECREF(globals_);
Py_DECREF(traceback_format_exception_);
// Py_Finalize();
}
......@@ -177,7 +141,8 @@ std::string Interpreter::python(const std::string& command) {
return lres;
}
void Interpreter::python(const std::string& command, std::string& res, std::string& out, std::string& err) {
void Interpreter::python(const std::string& command, std::string& res,
std::string& out, std::string& err) {
res = "";
out = "";
err = "";
......@@ -192,47 +157,47 @@ void Interpreter::python(const std::string& command, std::string& res, std::stri
PyEval_RestoreThread(_pyState);
std::cout << command.c_str() << std::endl;
PyObject* result = PyRun_String(command.c_str(), Py_eval_input, globals_, globals_);
PyObject* result =
PyRun_String(command.c_str(), Py_eval_input, globals_, globals_);
// Check if the result is null.
if (!result) {
if (result == NULL) {
// 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)) {
if (!HandleErr(err, globals_, Py_eval_input)) {
// If this is a statement, re-parse the command.
result = PyRun_String(command.c_str(), Py_single_input, globals_, globals_);
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);
if (result == NULL) HandleErr(err, globals_, Py_single_input);
// If there is no error, make sure that the previous error message is
// erased.
else
// If there is no error, make sure that the previous error message is erased.
err = "";
} else {
} else
dgDEBUG(15) << "Do not try a second time." << std::endl;
}
}
PyObject* stdout_obj = 0;
stdout_obj = PyRun_String("stdout_catcher.fetch()", Py_eval_input, globals_, globals_);
out = PyString_AsString(stdout_obj);
stdout_obj =
PyRun_String("stdout_catcher.fetch()", Py_eval_input, globals_, globals_);
out = obj_to_str(stdout_obj);
Py_DECREF(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;
PyObject* result2 = PyObject_Repr(result);
// If python cannot build a string representation of result
// then results is equal to NULL. This will trigger a SEGV
if (result2 != NULL) {
dgDEBUG(15) << "For command :" << command << std::endl;
res = PyString_AsString(result2);
dgDEBUG(15) << "For command: " << command << std::endl;
if (result != NULL) {
res = obj_to_str(result);
dgDEBUG(15) << "Result is: " << res << std::endl;
dgDEBUG(15) << "Out is: " << out << std::endl;
dgDEBUG(15) << "Err is :" << err << std::endl;
Py_DECREF(result);
} else {
dgDEBUG(15) << "Result is empty" << std::endl;
dgDEBUG(15) << "Result is: empty" << std::endl;
}
Py_DecRef(stdout_obj);
Py_DecRef(result2);
Py_DecRef(result);
dgDEBUG(15) << "Out is: " << out << std::endl;
dgDEBUG(15) << "Err is :" << err << std::endl;
_pyState = PyEval_SaveThread();
......@@ -256,23 +221,27 @@ void Interpreter::runPythonFile(std::string filename, std::string& err) {
PyEval_RestoreThread(_pyState);
err = "";
PyObject* pymainContext = globals_;
PyObject* run = PyRun_FileExFlags(pFile, filename.c_str(), Py_file_input, pymainContext, pymainContext, true, NULL);
if (PyErr_Occurred()) {
err = parse_python_exception();
PyObject* run =
PyRun_File(pFile, filename.c_str(), Py_file_input, globals_, globals_);
if (run == NULL) {
HandleErr(err, globals_, Py_file_input);
std::cerr << err << std::endl;
;
}
Py_DecRef(run);
_pyState = PyEval_SaveThread();
fclose(pFile);
}
void Interpreter::runMain(void) {
const char* argv[] = {"dg-embedded-pysh"};
PyEval_RestoreThread(_pyState);
#if PY_MAJOR_VERSION >= 3
const Py_UNICODE* argv[] = {L"dg-embedded-pysh"};
Py_Main(1, const_cast<Py_UNICODE**>(argv));
#else
const char* argv[] = {"dg-embedded-pysh"};
Py_Main(1, const_cast<char**>(argv));
#endif
_pyState = PyEval_SaveThread();
}
......@@ -294,44 +263,5 @@ std::string Interpreter::processStream(std::istream& stream, std::ostream& os) {
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
// -*- mode: c++ -*-
// Copyright 2011, Florent Lamiraux, CNRS.
#ifdef WIN32
#include <Windows.h>
#else
#include <dlfcn.h>
#endif
namespace dynamicgraph {
namespace python {
std::string libpython("@PYTHON_LIBRARY@");
} // namespace python
} // namespace dynamicgraph
// Copyright 2011, 2012, Florent Lamiraux, LAAS-CNRS.
#include <Python.h>
#include <dynamic-graph/pool.h>
#include <dynamic-graph/entity.h>
#include <vector>
#include "exception.hh"
namespace dynamicgraph {
namespace python {
extern PyObject* dgpyError;
namespace pool {
PyObject* writeGraph(PyObject* /*self*/, PyObject* args) {
char* filename;
if (!PyArg_ParseTuple(args, "s", &filename)) return NULL;
try {
PoolStorage::getInstance()->writeGraph(filename);
}
CATCH_ALL_EXCEPTIONS();
return Py_BuildValue("");
}
/**
\brief Get list of entities
*/
PyObject* getEntityList(PyObject* /*self*/, PyObject* args) {
if (!PyArg_ParseTuple(args, "")) return NULL;
std::vector<std::string> entityNames;
try {
const PoolStorage::Entities& listOfEntities = dynamicgraph::PoolStorage::getInstance()->getEntityMap();
Py_ssize_t classNumber = listOfEntities.size();
// Build a tuple object
PyObject* classTuple = PyTuple_New(classNumber);
Py_ssize_t iEntity = 0;
for (PoolStorage::Entities::const_iterator entity_it = listOfEntities.begin(); entity_it != listOfEntities.end();
++entity_it) {
const std::string& aname = entity_it->second->getName();
PyObject* className = Py_BuildValue("s", aname.c_str());
PyTuple_SetItem(classTuple, iEntity, className);
iEntity++;
}
return Py_BuildValue("O", classTuple);
}
CATCH_ALL_EXCEPTIONS();
return NULL;
}
} // namespace pool
} // namespace python
} // namespace dynamicgraph
// Copyright 2010, Florent Lamiraux, Thomas Moulard, LAAS-CNRS.
#include <Python.h>
#include <iostream>
#include <sstream>
#include <dynamic-graph/signal-base.h>
#include <dynamic-graph/signal.h>
#include <dynamic-graph/signal-ptr.h>
#include <dynamic-graph/signal-caster.h>
#include <dynamic-graph/linear-algebra.h>
#include <dynamic-graph/pool.h>
#include <dynamic-graph/factory.h>
#include "convert-dg-to-py.hh"
#include "exception.hh"
#include "signal-wrapper.hh"
using dynamicgraph::SignalBase;
namespace dynamicgraph {
namespace python {
extern PyObject* dgpyError;
using namespace convert;
namespace signalBase {
static void destroy(void* self);
/**
\brief Create an instance of SignalBase
*/
PyObject* create(PyObject* /*self*/, PyObject* args) {
char* name = NULL;
if (!PyArg_ParseTuple(args, "s", &name)) return NULL;
SignalBase<int>* obj = NULL;
obj = new SignalBase<int>(std::string(name));
// Return the pointer
return PyCObject_FromVoidPtr((void*)obj, destroy);
}
template <class T>
SignalWrapper<T, int>* createSignalWrapperTpl(const char* name, PyObject* o, std::string& error) {
typedef SignalWrapper<T, int> SignalWrapper_t;
if (!SignalWrapper_t::checkCallable(o, error)) {
return NULL;
}
SignalWrapper_t* obj = new SignalWrapper_t(name, o);
return obj;
}
PythonSignalContainer* getPythonSignalContainer() {
const std::string instanceName = "python_signals";
const std::string className = "PythonSignalContainer";
Entity* obj;
if (PoolStorage::getInstance()->existEntity(instanceName, obj)) {
if (obj->getClassName() != className) {
std::string msg("Found an object named " + std::string(instanceName) +
",\n"
"but this object is of type " +
std::string(obj->getClassName()) + " and not " + std::string(className));
PyErr_SetString(dgpyError, msg.c_str());
return NULL;
}
} else {
try {
obj = FactoryStorage::getInstance()->newEntity(std::string(className), std::string(instanceName));
}
CATCH_ALL_EXCEPTIONS();
}
return dynamic_cast<PythonSignalContainer*>(obj);
}
#define SIGNAL_WRAPPER_TYPE(IF, Enum, Type) \
IF(command::Value::typeName(command::Value::Enum).compare(type) == 0) { \
obj = createSignalWrapperTpl<Type>(name, object, error); \
}
/**
\brief Create an instance of SignalWrapper
*/
PyObject* createSignalWrapper(PyObject* /*self*/, PyObject* args) {
PythonSignalContainer* psc = getPythonSignalContainer();
if (psc == NULL) return NULL;
char* name = NULL;
char* type = NULL;
PyObject* object = NULL;
if (!PyArg_ParseTuple(args, "ssO", &name, &type, &object)) return NULL;
SignalBase<int>* obj = NULL;
std::string error;
SIGNAL_WRAPPER_TYPE(if, BOOL, bool)
// SIGNAL_WRAPPER_TYPE(else if, UNSIGNED ,bool)
SIGNAL_WRAPPER_TYPE(else if, INT, int)
SIGNAL_WRAPPER_TYPE(else if, FLOAT, float)
SIGNAL_WRAPPER_TYPE(else if, DOUBLE, double)
// SIGNAL_WRAPPER_TYPE(else if, STRING ,bool)
SIGNAL_WRAPPER_TYPE(else if, VECTOR, Vector)
// SIGNAL_WRAPPER_TYPE(else if, MATRIX ,bool)
// SIGNAL_WRAPPER_TYPE(else if, MATRIX4D ,bool)
else {
error = "Type not understood";
}
if (obj == NULL) {
PyErr_SetString(dgpyError, error.c_str());
return NULL;
}
// Register signal into the python signal container
psc->signalRegistration(*obj);
// Return the pointer
return PyCObject_FromVoidPtr((void*)obj, destroy);
}
/**
\brief Destroy an instance of InvertedPendulum
*/
static void destroy(void* self) {
SignalBase<int>* obj = (SignalBase<int>*)self;
delete obj;
}
PyObject* getTime(PyObject* /*self*/, PyObject* args) {
void* pointer = NULL;
PyObject* object = NULL;
if (!PyArg_ParseTuple(args, "O", &object)) return NULL;
if (!PyCObject_Check(object)) return NULL;
pointer = PyCObject_AsVoidPtr(object);
SignalBase<int>* obj = (SignalBase<int>*)pointer;
int time = obj->getTime();
return Py_BuildValue("i", time);
}
PyObject* setTime(PyObject* /*self*/, PyObject* args) {
void* pointer = NULL;
PyObject* object = NULL;
int time;
if (!PyArg_ParseTuple(args, "Oi", &object, &time)) return NULL;
if (!PyCObject_Check(object)) {
PyErr_SetString(dgpyError, "object should be a C object");
return NULL;
}
pointer = PyCObject_AsVoidPtr(object);
SignalBase<int>* obj = (SignalBase<int>*)pointer;
obj->setTime(time);
return Py_BuildValue("");
}
PyObject* display(PyObject* /*self*/, PyObject* args) {
void* pointer = NULL;
PyObject* object = NULL;
if (!PyArg_ParseTuple(args, "O", &object)) return NULL;
if (!PyCObject_Check(object)) return NULL;
pointer = PyCObject_AsVoidPtr(object);
SignalBase<int>* obj = (SignalBase<int>*)pointer;
std::ostringstream oss;
try {
obj->display(oss);
}
CATCH_ALL_EXCEPTIONS();
return Py_BuildValue("s", oss.str().c_str());
}
PyObject* displayDependencies(PyObject* /*self*/, PyObject* args) {
void* pointer = NULL;
PyObject* object = NULL;
int time;
if (!PyArg_ParseTuple(args, "OI", &object, &time)) return NULL;
if (!PyCObject_Check(object)) return NULL;
pointer = PyCObject_AsVoidPtr(object);
SignalBase<int>* obj = (SignalBase<int>*)pointer;
std::ostringstream oss;
try {
obj->displayDependencies(oss, time);
}
CATCH_ALL_EXCEPTIONS();
return Py_BuildValue("s", oss.str().c_str());
}
PyObject* getValue(PyObject* /*self*/, PyObject* args) {
void* pointer = NULL;
PyObject* object = NULL;
if (!PyArg_ParseTuple(args, "O", &object)) return NULL;
if (!PyCObject_Check(object)) return NULL;
pointer = PyCObject_AsVoidPtr(object);
SignalBase<int>* signal = (SignalBase<int>*)pointer;
try {
{ // --- VECTOR SIGNALS -----------------
// Two cases: the signal embeds directly a vector, or embeds
// an object deriving from vector.In the first case,
// the signal is directly cast into sig<vector>.
// In the second case, the derived object can be access as a vector
// using the signal-ptr<vector> type.
Signal<dynamicgraph::Vector, int>* sigvec = dynamic_cast<Signal<dynamicgraph::Vector, int>*>(signal);
if (NULL != sigvec) {
return vectorToPython(sigvec->accessCopy());
}
// Extraction of object derinving from vector: plug signal into
// a vector signal and get the value from the signal-ptr instead
// of the original vector.
SignalPtr<dynamicgraph::Vector, int> sigptr(NULL, "vector-caster");
try {
sigptr.plug(signal);
return vectorToPython(sigptr.accessCopy());
} catch (dynamicgraph::ExceptionSignal& ex) {
if (ex.getCode() != dynamicgraph::ExceptionSignal::PLUG_IMPOSSIBLE) throw;
}
}
{ // --- MATRIX SIGNALS --------------------
// Two cases: the signal embeds directly a matrix, or embeds
// an object deriving from matrix.In the first case,
// the signal is directly cast into sig<matrix>.
// In the second case, the derived object can be access as a matrix
// using the signal-ptr<matrix> type.
Signal<dynamicgraph::Matrix, int>* sigmat = dynamic_cast<Signal<dynamicgraph::Matrix, int>*>(signal);
if (NULL != sigmat) {
return matrixToPython(sigmat->accessCopy());
}
SignalPtr<dynamicgraph::Matrix, int> sigptr(NULL, "matrix-caster");
try {
sigptr.plug(signal);
return matrixToPython(sigptr.accessCopy());
} catch (dynamicgraph::ExceptionSignal& ex) {
if (ex.getCode() != dynamicgraph::ExceptionSignal::PLUG_IMPOSSIBLE) throw;
}
}
{ // --- HOMOGENEOUS MATRIX SIGNALS --------------------
// Two cases: the signal embeds directly a matrix, or embeds
// an object deriving from matrix.In the first case,
// the signal is directly cast into sig<matrix>.
// In the second case, the derived object can be access as a matrix
// using the signal-ptr<matrix> type.
// TODO: See if matrix homogeneous can be properly put in linear-algebra.h
typedef Eigen::Transform<double, 3, Eigen::Affine> MatrixHomogeneous;
Signal<MatrixHomogeneous, int>* sigmat = dynamic_cast<Signal<MatrixHomogeneous, int>*>(signal);
if (NULL != sigmat) {
return matrixToPython(sigmat->accessCopy().matrix());
}
SignalPtr<Eigen::Transform<double, 3, Eigen::Affine>, int> sigptr(NULL, "matrix-caster");
try {
sigptr.plug(signal);
return matrixToPython(sigptr.accessCopy().matrix());
} catch (dynamicgraph::ExceptionSignal& ex) {
if (ex.getCode() != dynamicgraph::ExceptionSignal::PLUG_IMPOSSIBLE) throw;
}
}
Signal<double, int>* sigdouble = dynamic_cast<Signal<double, int>*>(signal);
if (NULL != sigdouble) {
return Py_BuildValue("d", sigdouble->accessCopy());
}
}
CATCH_ALL_EXCEPTIONS();
/* Non specific signal: use a generic way. */
std::ostringstream value;
try {
signal->get(value);
}
CATCH_ALL_EXCEPTIONS();
std::string valueString = value.str();
return Py_BuildValue("s", valueString.c_str());
}
PyObject* getName(PyObject* /*self*/, PyObject* args) {
void* pointer = NULL;
PyObject* object = NULL;
if (!PyArg_ParseTuple(args, "O", &object)) return NULL;
if (!PyCObject_Check(object)) return NULL;
pointer = PyCObject_AsVoidPtr(object);
SignalBase<int>* signal = (SignalBase<int>*)pointer;
std::string name;
try {
name = signal->getName();
}
CATCH_ALL_EXCEPTIONS();
return Py_BuildValue("s", name.c_str());
}
PyObject* getClassName(PyObject* /*self*/, PyObject* args) {
void* pointer = NULL;
PyObject* object = NULL;
if (!PyArg_ParseTuple(args, "O", &object)) return NULL;
if (!PyCObject_Check(object)) return NULL;
pointer = PyCObject_AsVoidPtr(object);
SignalBase<int>* signal = (SignalBase<int>*)pointer;
std::string name;
try {
signal->getClassName(name);
}
CATCH_ALL_EXCEPTIONS();
return Py_BuildValue("s", name.c_str());
}
PyObject* setValue(PyObject* /*self*/, PyObject* args) {
void* pointer = NULL;
PyObject* object = NULL;
char* valueString = NULL;
if (!PyArg_ParseTuple(args, "Os", &object, &valueString)) return NULL;
if (!PyCObject_Check(object)) return NULL;
pointer = PyCObject_AsVoidPtr(object);
SignalBase<int>* signal = (SignalBase<int>*)pointer;
std::ostringstream os;
os << valueString;
std::istringstream value(os.str());
try {
signal->set(value);
}
CATCH_ALL_EXCEPTIONS();
return Py_BuildValue("");
}
PyObject* recompute(PyObject* /*self*/, PyObject* args) {
void* pointer = NULL;
PyObject* object = NULL;
unsigned int time;
if (!PyArg_ParseTuple(args, "OI", &object, &time)) return NULL;
if (!PyCObject_Check(object)) return NULL;
pointer = PyCObject_AsVoidPtr(object);
SignalBase<int>* signal = (SignalBase<int>*)pointer;
try {
signal->recompute(time);
}
CATCH_ALL_EXCEPTIONS();
return Py_BuildValue("");
}
PyObject* unplug(PyObject* /*self*/, PyObject* args) {
void* pointer = NULL;
PyObject* object = NULL;
if (!PyArg_ParseTuple(args, "O", &object)) return NULL;
if (!PyCObject_Check(object)) return NULL;
pointer = PyCObject_AsVoidPtr(object);
SignalBase<int>* signal = (SignalBase<int>*)pointer;
try {
signal->unplug();
}
CATCH_ALL_EXCEPTIONS();
return Py_BuildValue("");
}
PyObject* isPlugged(PyObject*, PyObject* args) {
void* pointer = NULL;
PyObject* object = NULL;
if (!PyArg_ParseTuple(args, "O", &object)) return NULL;
if (!PyCObject_Check(object)) return NULL;
pointer = PyCObject_AsVoidPtr(object);
SignalBase<int>* signal = (SignalBase<int>*)pointer;
bool plugged = false;
try {
plugged = signal->isPlugged();
}
CATCH_ALL_EXCEPTIONS();
if (plugged)
return PyBool_FromLong(1);
else
return PyBool_FromLong(0);
}
PyObject* getPlugged(PyObject*, PyObject* args) {
void* pointer = NULL;
PyObject* object = NULL;
if (!PyArg_ParseTuple(args, "O", &object)) return NULL;
if (!PyCObject_Check(object)) return NULL;
pointer = PyCObject_AsVoidPtr(object);
SignalBase<int>* signal = (SignalBase<int>*)pointer;
SignalBase<int>* otherSignal = 0;
try {
bool plugged = signal->isPlugged();
otherSignal = signal->getPluged();
if (!plugged || otherSignal == 0) {
std::string msg = std::string("Signal ") + signal->getName() + std::string(" is not plugged.");
throw std::runtime_error(msg);
}
}
CATCH_ALL_EXCEPTIONS();
// Return the pointer to the signal without destructor since the signal
// is not owned by the calling object.
return PyCObject_FromVoidPtr((void*)otherSignal, NULL);
}
} // namespace signalBase
} // namespace python
} // namespace dynamicgraph
// Copyright 2010, Florent Lamiraux, Thomas Moulard, LAAS-CNRS.
#include <Python.h>
#include <iostream>
#include <sstream>
#include <dynamic-graph/signal-caster.h>
namespace dynamicgraph {
namespace python {
namespace signalCaster {
PyObject* getSignalTypeList(PyObject* /*self*/, PyObject* args) {
if (!PyArg_ParseTuple(args, "")) return NULL;
std::vector<std::string> typeList = dynamicgraph::SignalCaster::getInstance()->listTypenames();
Py_ssize_t typeNumber = typeList.size();
// Build a tuple object
PyObject* typeTuple = PyTuple_New(typeNumber);
for (Py_ssize_t iType = 0; iType < typeNumber; ++iType) {
PyObject* className = Py_BuildValue("s", typeList[iType].c_str());
PyTuple_SetItem(typeTuple, iType, className);
}
return Py_BuildValue("O", typeTuple);
}
} // namespace signalCaster
} // namespace python
} // namespace dynamicgraph
# Copyright 2010-2020, Florent Lamiraux, Thomas Moulard, Olivier Stasse, Guilhem
# Saurel, JRL, CNRS/AIST, LAAS-CNRS
# Test the interpreter
add_unit_test(interpreter-test interpreter-test.cc)
target_link_libraries(interpreter-test PRIVATE ${PROJECT_NAME})
# Test runfile
add_unit_test(interpreter-test-runfile interpreter-test-runfile.cc)
target_link_libraries(interpreter-test-runfile PRIVATE ${PROJECT_NAME})
target_include_directories(interpreter-test-runfile
PRIVATE Boost::unit_test_framework)
target_compile_definitions(interpreter-test-runfile
PRIVATE PATH="${CMAKE_CURRENT_LIST_DIR}/")
# Test the module generation Create an entity
set(LIBRARY_NAME "custom_entity")
add_library(${LIBRARY_NAME} SHARED "${LIBRARY_NAME}.cpp")
if(SUFFIX_SO_VERSION)
set_target_properties(${LIBRARY_NAME} PROPERTIES SOVERSION ${PROJECT_VERSION})
endif(SUFFIX_SO_VERSION)
target_link_libraries(${LIBRARY_NAME} PRIVATE dynamic-graph::dynamic-graph)
# Create its bindings This mimics DYNAMIC_GRAPH_PYTHON_MODULE(${LIBRARY_NAME}
# ${LIBRARY_NAME} "${LIBRARY_NAME}-wrap")
configure_file(
${PROJECT_SOURCE_DIR}/cmake/dynamic_graph/submodule/__init__.py.cmake
${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}/__init__.py)
set(PYTHON_MODULE "${LIBRARY_NAME}-wrap")
set(DYNAMICGRAPH_MODULE_HEADER
"${CMAKE_SOURCE_DIR}/tests/custom_entity_module.h")
configure_file(${PROJECT_SOURCE_DIR}/cmake/dynamic_graph/python-module-py.cc.in
${CMAKE_CURRENT_BINARY_DIR}/python-module-py.cc @ONLY)
add_library(${PYTHON_MODULE} MODULE
${CMAKE_CURRENT_BINARY_DIR}/python-module-py.cc)
set_target_properties(${PYTHON_MODULE}
PROPERTIES PREFIX "" OUTPUT_NAME ${LIBRARY_NAME}/wrap)
if(UNIX AND NOT APPLE)
target_link_libraries(${PYTHON_MODULE} PRIVATE "-Wl,--no-as-needed")
endif(UNIX AND NOT APPLE)
target_link_libraries(${PYTHON_MODULE} PRIVATE ${LIBRARY_NAME} ${PROJECT_NAME})
# Test it
add_python_unit_test("test-custom-entity" "tests/test_custom_entity.py" src
tests)
# also test other bindings, using this custom entity
add_python_unit_test("test-bindings" "tests/test_bindings.py" src tests)
/* Copyright 2010-2019 LAAS, CNRS
* Thomas Moulard.
*
*/
#define ENABLE_RT_LOG
#include "custom_entity.h"
#include <dynamic-graph/command-bind.h>
#include <dynamic-graph/entity.h>
#include <dynamic-graph/exception-factory.h>
#include <dynamic-graph/factory.h>
#include <dynamic-graph/pool.h>
#include <dynamic-graph/real-time-logger.h>
#include <dynamic-graph/signal-ptr.h>
#include <dynamic-graph/signal-time-dependent.h>
#include <boost/bind.hpp>
namespace dynamicgraph {
CustomEntity::CustomEntity(const std::string n)
: Entity(n),
m_sigdSIN(NULL, "CustomEntity(" + name + ")::input(double)::in_double"),
m_sigdTimeDepSOUT(boost::bind(&CustomEntity::update, this, _1, _2),
m_sigdSIN,
"CustomEntity(" + name + ")::input(double)::out_double")
{
addSignal();
using namespace dynamicgraph::command;
this->addCommand("act",
makeCommandVoid0(*this, &CustomEntity::act,
docCommandVoid0("act on input signal")));
}
void CustomEntity::addSignal() {
signalRegistration(m_sigdSIN << m_sigdTimeDepSOUT);
}
void CustomEntity::rmValidSignal() {
signalDeregistration("in_double");
signalDeregistration("out_double");
}
double &CustomEntity::update(double &res, const int &inTime) {
const double &aDouble = m_sigdSIN(inTime);
res = aDouble;
logger().stream(MSG_TYPE_ERROR) << "start update " << res << '\n';
DYNAMIC_GRAPH_ENTITY_DEBUG(*this)
<< "This is a message of level MSG_TYPE_DEBUG\n";
DYNAMIC_GRAPH_ENTITY_INFO(*this)
<< "This is a message of level MSG_TYPE_INFO\n";
DYNAMIC_GRAPH_ENTITY_WARNING(*this)
<< "This is a message of level MSG_TYPE_WARNING\n";
DYNAMIC_GRAPH_ENTITY_ERROR(*this)
<< "This is a message of level MSG_TYPE_ERROR\n";
DYNAMIC_GRAPH_ENTITY_DEBUG_STREAM(*this)
<< "This is a message of level MSG_TYPE_DEBUG_STREAM\n";
DYNAMIC_GRAPH_ENTITY_INFO_STREAM(*this)
<< "This is a message of level MSG_TYPE_INFO_STREAM\n";
DYNAMIC_GRAPH_ENTITY_WARNING_STREAM(*this)
<< "This is a message of level MSG_TYPE_WARNING_STREAM\n";
DYNAMIC_GRAPH_ENTITY_ERROR_STREAM(*this)
<< "This is a message of level MSG_TYPE_ERROR_STREAM\n";
logger().stream(MSG_TYPE_ERROR) << "end update\n";
return res;
}
void CustomEntity::act() { m_sigdSIN.accessCopy(); }
DYNAMICGRAPH_FACTORY_ENTITY_PLUGIN(CustomEntity, "CustomEntity");
} // namespace dynamicgraph
/* Copyright 2020 LAAS, CNRS
* Joseph Mirabel
*
*/
#define ENABLE_RT_LOG
#include <dynamic-graph/entity.h>
#include <dynamic-graph/signal-ptr.h>
#include <dynamic-graph/signal-time-dependent.h>
#include <sstream>
namespace dynamicgraph {
class CustomEntity : public Entity {
public:
dynamicgraph::SignalPtr<double, int> m_sigdSIN;
dynamicgraph::SignalTimeDependent<double, int> m_sigdTimeDepSOUT;
DYNAMIC_GRAPH_ENTITY_DECL();
CustomEntity(const std::string n);
void addSignal();
void rmValidSignal();
double &update(double &res, const int &inTime);
void act();
};
} // namespace dynamicgraph
#include "custom_entity.h"
typedef boost::mpl::vector<dynamicgraph::CustomEntity> entities_t;
......@@ -2,7 +2,10 @@
* Copyright
*/
#include <iostream>
#include "dynamic-graph-python-test.hh"
GraphTest::GraphTest() { std::cout << "Constructor of unitTesting object of class Graph." << std::endl; }
#include <iostream>
GraphTest::GraphTest() {
std::cout << "Constructor of unitTesting object of class Graph." << std::endl;
}
/*
* Copyright
*/
#include "dynamic-graph-python-test.hh"
#include <iostream>
GraphTest::GraphTest() {
std::cout << "Constructor of unitTesting object of class Graph." << std::endl;
}
// The purpose of this unit test is to check the interpreter::runPythonFile method
// The purpose of this unit test is to check the interpreter::runPythonFile
// method
#include <cstring>
#include <iostream>
#include "dynamic-graph/python/interpreter.hh"
bool testFile(const std::string& filename, const std::string& expectedOutput, int numTest) {
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 << "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;
......@@ -19,7 +22,8 @@ bool testFile(const std::string& filename, const std::string& expectedOutput, in
return true;
}
bool testInterpreterDestructor(const std::string& filename, const std::string& expectedOutput) {
bool testInterpreterDestructor(const std::string& filename,
const std::string& expectedOutput) {
std::string err = "";
{
dynamicgraph::python::Interpreter interp;
......@@ -49,20 +53,36 @@ int main(int argc, char** argv) {
// This test succeeds only because it is launched before "test_python-ok.py"
// because re as been imported in a previous test and it is not
// safe to delete imported module...
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",
res = testFile(PATH "test_python-name_error.py",
std::string(
"Traceback (most recent call last):\n"
" File \"" PATH
"test_python-name_error.py\", line 7, in <module>\n"
" pathList = re.split(\":\", pkgConfigPath) # noqa\n"
"NameError: name 're' is not defined\n"),
numTest) &&
res;
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-syntax_error.py",
std::string("<type 'exceptions.SyntaxError'>: ('invalid syntax', ") +
"('test_python-syntax_error.py', 1, 11, " + "'hello world\\n'))",
res = testFile(PATH "test_python-ok.py", "", numTest) && res;
res = testFile(PATH "unexistant_file.py",
PATH "unexistant_file.py cannot be open", numTest) &&
res;
res = testFile(PATH "test_python-syntax_error.py",
std::string(" File \"" PATH
"test_python-syntax_error.py\", line 2\n"
" hello world\n"
#if PY_MINOR_VERSION >= 10
" ^^^^^\n"
#elif PY_MINOR_VERSION >= 8
" ^\n"
#else
" ^\n"
#endif
"SyntaxError: invalid syntax\n"),
numTest) &&
res;
res = testInterpreterDestructor("test_python-restart_interpreter.py", "") && res;
res = testInterpreterDestructor(PATH "test_python-restart_interpreter.py",
"") &&
res;
return (res ? 0 : 1);
}
......@@ -14,9 +14,14 @@ int main(int argc, char** argv) {
for (int i = 0; i < numTest; ++i) {
// correct input
interp.python("print \"I am the interpreter\"", result, out, err);
interp.python("print('I am the interpreter')", result, out, err);
assert(out.compare("I am the interpreter"));
assert(err.length() == 0);
// incorrect input
interp.python("print I am the interpreter", result, out, err);
assert(result.length() == 0);
assert(out.length() == 0);
assert(err.length() > 50);
}
return 0;
}
File moved
import unittest
import dynamic_graph as dg
from custom_entity import CustomEntity
ERR = (
"""Python argument types in
dynamic_graph.wrap.plug(%s, %s)
did not match C++ signature:
plug("""
"dynamicgraph::SignalBase<int>* signalOut, "
"dynamicgraph::SignalBase<int>* signalIn)"
)
class BindingsTests(unittest.TestCase):
def test_type_check(self):
"""
test the type checking in signal plugs
"""
first = CustomEntity("first_entity")
second = CustomEntity("second_entity")
# Check that we can connect first.out to second.in
dg.plug(first.signal("out_double"), second.signal("in_double"))
# Check that we can't connect first.out to second
with self.assertRaises(TypeError) as cm_in:
dg.plug(first.signal("out_double"), second)
self.assertEqual(
str(cm_in.exception), ERR % ("SignalTimeDependentDouble", "CustomEntity")
)
# Check that we can't connect first to second.in
with self.assertRaises(TypeError) as cm_out:
dg.plug(first, second.signal("in_double"))
self.assertEqual(
str(cm_out.exception), ERR % ("CustomEntity", "SignalPtrDouble")
)
def test_dg_exc(self):
"""
test that exceptions from dynamic graph are correctly raised
"""
ent = CustomEntity("test_dg_exc")
# check that accessing a non initialized signal raises
with self.assertRaises(RuntimeError) as cm:
ent.act()
self.assertEqual(
str(cm.exception),
"In SignalPtr: SIN ptr not set. "
"(in signal <CustomEntity(test_dg_exc)::input(double)::in_double>)",
)
# check that accessing an initialized signal doesn't raise
ent_2 = CustomEntity("another_entity")
dg.plug(ent_2.signal("out_double"), ent.signal("in_double"))
ent.act()
if __name__ == "__main__":
unittest.main()
......@@ -2,17 +2,17 @@
# 2019 CNRS
#
import os
import sys
import time
from dynamic_graph import (addLoggerCoutOutputStream, addLoggerFileOutputStream, closeLoggerFileOutputStream,
real_time_logger_destroy, real_time_logger_spin_once)
from custom_entity import CustomEntity
from dynamic_graph import (
addLoggerCoutOutputStream,
addLoggerFileOutputStream,
closeLoggerFileOutputStream,
real_time_logger_destroy,
real_time_logger_spin_once,
)
from dynamic_graph.entity import VerbosityLevel
from dynamic_graph_tests.custom_entity import CustomEntity
# Put local python module at first priority
sys.path.insert(0, os.getcwd() + '/../src')
sys.path.insert(0, os.getcwd())
print(os.getcwd())
......
......@@ -3,7 +3,7 @@ import os
pkgConfigPath = os.environ.get("PKG_CONFIG_PATH")
if pkgConfigPath is None:
pkgConfigPath = ''
pathList = re.split(':', pkgConfigPath) # noqa
pkgConfigPath = ""
pathList = re.split(":", pkgConfigPath) # noqa
print(pathList)
......@@ -3,7 +3,7 @@ import re
pkgConfigPath = os.environ.get("PKG_CONFIG_PATH")
if pkgConfigPath is None:
pkgConfigPath = ''
pathList = re.split(':', pkgConfigPath)
pkgConfigPath = ""
pathList = re.split(":", pkgConfigPath)
print(pathList)
......@@ -3,6 +3,6 @@ import numpy # noqa
# Make sure the variable is deleted.
if "var" in locals() or "var" in globals():
raise ValueError('Not cleaned')
raise ValueError("Not cleaned")
var = "This string should have been deleted."