diff --git a/CMakeLists.txt b/CMakeLists.txt
index b22d65c554255e409d691018c72bf7acc633f3d9..307d5bebae0e17cfd6ddbdf1f48c037ac7b177e9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -30,33 +30,38 @@ PROJECT(${PROJECT_NAME} ${PROJECT_ARGS})
 FINDPYTHON()
 
 ADD_PROJECT_DEPENDENCY(dynamic-graph REQUIRED PKG_CONFIG_REQUIRES dynamic-graph)
-SET(BOOST_COMPONENTS filesystem system thread program_options unit_test_framework python)
-SEARCH_FOR_BOOST()
-
+ADD_PROJECT_DEPENDENCY(eigenpy REQUIRED PKG_CONFIG_REQUIRES eigenpy)
+SEARCH_FOR_BOOST_PYTHON(REQUIRED)
+IF(BUILD_TESTING)
+  FIND_PACKAGE(Boost REQUIRED COMPONENTS unit_test_framework)
+ENDIF(BUILD_TESTING)
 
 # Main Library
 SET(${PROJECT_NAME}_HEADERS
   include/${CUSTOM_HEADER_DIR}/api.hh
   include/${CUSTOM_HEADER_DIR}/convert-dg-to-py.hh
   include/${CUSTOM_HEADER_DIR}/dynamic-graph-py.hh
-  include/${CUSTOM_HEADER_DIR}/exception.hh
-  include/${CUSTOM_HEADER_DIR}/exception-python.hh
   include/${CUSTOM_HEADER_DIR}/interpreter.hh
+  include/${CUSTOM_HEADER_DIR}/module.hh
   include/${CUSTOM_HEADER_DIR}/python-compat.hh
+  include/${CUSTOM_HEADER_DIR}/signal.hh
   include/${CUSTOM_HEADER_DIR}/signal-wrapper.hh
   )
 
 SET(${PROJECT_NAME}_SOURCES
   src/interpreter.cc
   src/dynamic_graph/python-compat.cc
+  src/dynamic_graph/entity-py.cc
+  src/dynamic_graph/convert-dg-to-py.cc
   )
 
 ADD_LIBRARY(${PROJECT_NAME} SHARED
   ${${PROJECT_NAME}_SOURCES} ${${PROJECT_NAME}_HEADERS})
-TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME} SYSTEM PUBLIC ${PYTHON_INCLUDE_DIRS})
+TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME} SYSTEM PUBLIC ${PYTHON_INCLUDE_DIR})
 TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME} PUBLIC $<INSTALL_INTERFACE:include>)
-TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${Boost_LIBRARIES}
-  ${PYTHON_LIBRARY} ${Boost_PYTHON_LIBRARIES} dynamic-graph::dynamic-graph)
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} PUBLIC ${PYTHON_LIBRARY}
+  dynamic-graph::dynamic-graph)
+TARGET_LINK_BOOST_PYTHON(${PROJECT_NAME} PRIVATE)
 
 IF(SUFFIX_SO_VERSION)
   SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES SOVERSION ${PROJECT_VERSION})
@@ -67,7 +72,9 @@ TARGET_COMPILE_DEFINITIONS(${PROJECT_NAME} PRIVATE PYTHON_LIBRARY="${PYTHON_LIBR
 INSTALL(TARGETS ${PROJECT_NAME} EXPORT ${TARGETS_EXPORT_NAME} DESTINATION lib)
 
 ADD_SUBDIRECTORY(src)
-ADD_SUBDIRECTORY(tests)
+IF(BUILD_TESTING)
+  ADD_SUBDIRECTORY(tests)
+ENDIF(BUILD_TESTING)
 
 PKG_CONFIG_APPEND_LIBS(${PROJECT_NAME})
 INSTALL(FILES package.xml DESTINATION share/${PROJECT_NAME})
diff --git a/cmake b/cmake
index fb4c22c319ec5320f9a85527eb1a4130954846f5..91f97c1c31608f48d697a6b11037f13e878b9837 160000
--- a/cmake
+++ b/cmake
@@ -1 +1 @@
-Subproject commit fb4c22c319ec5320f9a85527eb1a4130954846f5
+Subproject commit 91f97c1c31608f48d697a6b11037f13e878b9837
diff --git a/include/dynamic-graph/python/convert-dg-to-py.hh b/include/dynamic-graph/python/convert-dg-to-py.hh
index fe8d53c44d38eba863e655bd8dec5e1cfe69d445..d849fdc15be0256d624204c909948ffcee09aef7 100644
--- a/include/dynamic-graph/python/convert-dg-to-py.hh
+++ b/include/dynamic-graph/python/convert-dg-to-py.hh
@@ -1,18 +1,16 @@
 // Copyright 2010, Florent Lamiraux, Thomas Moulard, LAAS-CNRS.
 
+#include <boost/python.hpp>
+
 #include <dynamic-graph/linear-algebra.h>
 #include <dynamic-graph/value.h>
-#include <dynamic-graph/python/exception-python.hh>
 
 namespace dynamicgraph {
 namespace python {
 namespace convert {
 
-command::Value pythonToValue(PyObject* pyObject, const command::Value::Type& valueType);
-PyObject* vectorToPython(const Vector& vector);
-PyObject* matrixToPython(const ::dynamicgraph::Matrix& matrix);
-PyObject* matrix4dToPython(const Eigen::Matrix4d& matrix);
-PyObject* valueToPython(const ::dynamicgraph::command::Value& value);
+command::Value toValue(boost::python::object o, const command::Value::Type& type);
+boost::python::object fromValue(const command::Value& value);
 
 }  // namespace convert
 }  // namespace python
diff --git a/include/dynamic-graph/python/dynamic-graph-py.hh b/include/dynamic-graph/python/dynamic-graph-py.hh
index e218f256f3f7f5695575801d4317ef761128fb65..df7eb669ad72b8008f6c76f02a63c03363e32740 100644
--- a/include/dynamic-graph/python/dynamic-graph-py.hh
+++ b/include/dynamic-graph/python/dynamic-graph-py.hh
@@ -4,189 +4,70 @@
 #include <iostream>
 #include <sstream>
 
+#include <boost/python.hpp>
+
 #include <dynamic-graph/debug.h>
 #include <dynamic-graph/exception-factory.h>
 #include <dynamic-graph/signal-base.h>
 
 #include "dynamic-graph/python/signal-wrapper.hh"
 
+namespace bp = boost::python;
+
 namespace dynamicgraph {
 namespace python {
 
+template <typename Iterator>
+inline bp::list to_py_list(Iterator begin, Iterator end) {
+  typedef typename Iterator::value_type T;
+  bp::list lst;
+  std::for_each(begin, end, [&](const T& t) { lst.append(t); });
+  return lst;
+}
+
+template <typename Iterator>
+inline bp::tuple to_py_tuple(Iterator begin, Iterator end) {
+  return bp::tuple(to_py_list(begin, end));
+}
+
+template <typename T>
+inline std::vector<T> to_std_vector(const bp::object& iterable) {
+  return std::vector<T>(bp::stl_input_iterator<T>(iterable), bp::stl_input_iterator<T>());
+}
+
+void exposeSignals();
+
 // Declare functions defined in other source files
 namespace signalBase {
-PyObject* create(PyObject* self, PyObject* args);
-PyObject* createSignalWrapper(PyObject* self, PyObject* args);
-PyObject* getTime(PyObject* self, PyObject* args);
-PyObject* setTime(PyObject* self, PyObject* args);
-PyObject* getName(PyObject* self, PyObject* args);
-PyObject* getClassName(PyObject* self, PyObject* args);
-PyObject* display(PyObject* self, PyObject* args);
-PyObject* displayDependencies(PyObject* self, PyObject* args);
-PyObject* getValue(PyObject* self, PyObject* args);
-PyObject* setValue(PyObject* self, PyObject* args);
-PyObject* recompute(PyObject* self, PyObject* args);
-PyObject* unplug(PyObject* self, PyObject* args);
-PyObject* isPlugged(PyObject* self, PyObject* args);
-PyObject* getPlugged(PyObject* self, PyObject* args);
+SignalBase<int>* createSignalWrapper(const char* name, const char* type, bp::object object);
 }  // namespace signalBase
 namespace entity {
-PyObject* create(PyObject* self, PyObject* args);
-PyObject* display(PyObject* self, PyObject* args);
-PyObject* display(PyObject* self, PyObject* args);
-PyObject* getName(PyObject* self, PyObject* args);
-PyObject* getClassName(PyObject* self, PyObject* args);
-PyObject* hasSignal(PyObject* self, PyObject* args);
-PyObject* getSignal(PyObject* self, PyObject* args);
-PyObject* listSignals(PyObject* self, PyObject* args);
-PyObject* executeCommand(PyObject* self, PyObject* args);
-PyObject* listCommands(PyObject* self, PyObject* args);
-PyObject* getCommandDocstring(PyObject* self, PyObject* args);
-PyObject* getDocString(PyObject* self, PyObject* args);
-PyObject* setLoggerVerbosityLevel(PyObject* self, PyObject* args);
-PyObject* getLoggerVerbosityLevel(PyObject* self, PyObject* args);
-PyObject* setTimeSample(PyObject* self, PyObject* args);
-PyObject* getTimeSample(PyObject* self, PyObject* args);
-PyObject* setStreamPrintPeriod(PyObject* self, PyObject* args);
-PyObject* getStreamPrintPeriod(PyObject* self, PyObject* args);
+
+/// \param obj an Entity object
+void addCommands(boost::python::object obj);
+void addSignals(boost::python::object obj);
+
+Entity* create(const char* type, const char* name);
+bp::object executeCmd(bp::tuple args, bp::dict);
 }  // namespace entity
 
 namespace factory {
-PyObject* getEntityClassList(PyObject* self, PyObject* args);
-}
-namespace signalCaster {
-PyObject* getSignalTypeList(PyObject* self, PyObject* args);
+bp::tuple getEntityClassList();
 }
 namespace pool {
-PyObject* writeGraph(PyObject* self, PyObject* args);
-PyObject* getEntityList(PyObject* self, PyObject* args);
+void writeGraph(const char* filename);
+bp::list getEntityList();
+const std::map<std::string, Entity*>* getEntityMap();
 }  // namespace pool
 namespace debug {
-PyObject* addLoggerFileOutputStream(PyObject* self, PyObject* args);
-PyObject* addLoggerCoutOutputStream(PyObject* self, PyObject* args);
-PyObject* closeLoggerFileOutputStream(PyObject* self, PyObject* args);
-PyObject* realTimeLoggerSpinOnce(PyObject* self, PyObject* args);
-PyObject* realTimeLoggerDestroy(PyObject* self, PyObject* args);
-PyObject* realTimeLoggerInstance(PyObject* self, PyObject* args);
+void addLoggerFileOutputStream(const char* filename);
+void addLoggerCoutOutputStream();
+void closeLoggerFileOutputStream();
+void realTimeLoggerSpinOnce();
+void realTimeLoggerDestroy();
+void realTimeLoggerInstance();
 }  // namespace debug
 
-struct module_state {
-  PyObject* dgpyError;
-};
-
-PyObject* plug(PyObject* /*self*/, PyObject* args);
-
-PyObject* enableTrace(PyObject* /*self*/, PyObject* args);
-
-PyObject* error_out(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject*
-#else
-    PyObject*, PyObject*
-#endif
-);
-
-/**
-   \brief List of python functions
-*/
-__attribute__((unused)) static PyMethodDef dynamicGraphMethods[] = {
-    {"w_plug", dynamicgraph::python::plug, METH_VARARGS, "plug an output signal into an input signal"},
-    {"enableTrace", dynamicgraph::python::enableTrace, METH_VARARGS, "Enable or disable tracing debug info in a file"},
-    // Signals
-    {"create_signal_base", dynamicgraph::python::signalBase::create, METH_VARARGS, "create a SignalBase C++ object"},
-    {"create_signal_wrapper", dynamicgraph::python::signalBase::createSignalWrapper, METH_VARARGS,
-     "create a SignalWrapper C++ object"},
-    {"signal_base_get_time", dynamicgraph::python::signalBase::getTime, METH_VARARGS, "Get time of  a SignalBase"},
-    {"signal_base_set_time", dynamicgraph::python::signalBase::setTime, METH_VARARGS, "Set time of  a SignalBase"},
-    {"signal_base_get_name", dynamicgraph::python::signalBase::getName, METH_VARARGS, "Get the name of a signal"},
-    {"signal_base_get_class_name", dynamicgraph::python::signalBase::getClassName, METH_VARARGS,
-     "Get the class name of a signal"},
-    {"signal_base_display", dynamicgraph::python::signalBase::display, METH_VARARGS, "Print the signal in a string"},
-    {"signal_base_display_dependencies", dynamicgraph::python::signalBase::displayDependencies, METH_VARARGS,
-     "Print the signal dependencies in a string"},
-    {"signal_base_get_value", dynamicgraph::python::signalBase::getValue, METH_VARARGS, "Read the value of a signal"},
-    {"signal_base_set_value", dynamicgraph::python::signalBase::setValue, METH_VARARGS, "Set the value of a signal"},
-    {"signal_base_recompute", dynamicgraph::python::signalBase::recompute, METH_VARARGS,
-     "Recompute the signal at given time"},
-    {"signal_base_unplug", dynamicgraph::python::signalBase::unplug, METH_VARARGS, "Unplug the signal"},
-    {"signal_base_isPlugged", dynamicgraph::python::signalBase::isPlugged, METH_VARARGS,
-     "Whether the signal is plugged"},
-    {"signal_base_getPlugged", dynamicgraph::python::signalBase::getPlugged, METH_VARARGS,
-     "To which signal the signal is plugged"},
-    // Entity
-    {"create_entity", dynamicgraph::python::entity::create, METH_VARARGS, "create an Entity C++ object"},
-    {"display_entity", dynamicgraph::python::entity::display, METH_VARARGS, "print an Entity C++ object"},
-    {"entity_get_name", dynamicgraph::python::entity::getName, METH_VARARGS, "get the name of an Entity"},
-    {"entity_get_class_name", dynamicgraph::python::entity::getClassName, METH_VARARGS,
-     "get the class name of an Entity"},
-    {"entity_has_signal", dynamicgraph::python::entity::hasSignal, METH_VARARGS,
-     "return True if the entity has a signal with the given name"},
-    {"entity_get_signal", dynamicgraph::python::entity::getSignal, METH_VARARGS, "get signal by name from an Entity"},
-    {"entity_list_signals", dynamicgraph::python::entity::listSignals, METH_VARARGS,
-     "Return the list of signals of an entity."},
-    {"entity_execute_command", dynamicgraph::python::entity::executeCommand, METH_VARARGS, "execute a command"},
-    {"entity_list_commands", dynamicgraph::python::entity::listCommands, METH_VARARGS,
-     "list the commands of an entity"},
-    {"entity_get_command_docstring", dynamicgraph::python::entity::getCommandDocstring, METH_VARARGS,
-     "get the docstring of an entity command"},
-    {"entity_get_docstring", dynamicgraph::python::entity::getDocString, METH_VARARGS,
-     "get the doc string of an entity type"},
-    {"factory_get_entity_class_list", dynamicgraph::python::factory::getEntityClassList, METH_VARARGS,
-     "return the list of entity classes"},
-    {"signal_caster_get_type_list", dynamicgraph::python::signalCaster::getSignalTypeList, METH_VARARGS,
-     "return the list of signal type names"},
-    {"writeGraph", dynamicgraph::python::pool::writeGraph, METH_VARARGS, "Write the graph of entities in a filename."},
-    {"get_entity_list", dynamicgraph::python::pool::getEntityList, METH_VARARGS,
-     "return the list of instanciated entities"},
-    {"entity_set_logger_verbosity", dynamicgraph::python::entity::setLoggerVerbosityLevel, METH_VARARGS,
-     "set the verbosity level of the entity"},
-    {"entity_get_logger_verbosity", dynamicgraph::python::entity::getLoggerVerbosityLevel, METH_VARARGS,
-     "get the verbosity level of the entity"},
-    {"addLoggerFileOutputStream", dynamicgraph::python::debug::addLoggerFileOutputStream, METH_VARARGS,
-     "add a output file stream to the logger by filename"},
-    {"addLoggerCoutOutputStream", dynamicgraph::python::debug::addLoggerCoutOutputStream, METH_VARARGS,
-     "add std::cout as output stream to the logger"},
-    {"closeLoggerFileOutputStream", dynamicgraph::python::debug::closeLoggerFileOutputStream, METH_VARARGS,
-     "close all the loggers file output streams."},
-    {"entity_set_time_sample", dynamicgraph::python::entity::setTimeSample, METH_VARARGS,
-     "set the time sample for printing debugging information"},
-    {"entity_get_time_sample", dynamicgraph::python::entity::getTimeSample, METH_VARARGS,
-     "get the time sample for printing debugging information"},
-    {"entity_set_stream_print_period", dynamicgraph::python::entity::setStreamPrintPeriod, METH_VARARGS,
-     "set the period at which debugging information are printed"},
-    {"entity_get_stream_print_period", dynamicgraph::python::entity::getStreamPrintPeriod, METH_VARARGS,
-     "get the period at which debugging information are printed"},
-    {"real_time_logger_destroy", dynamicgraph::python::debug::realTimeLoggerDestroy, METH_VARARGS,
-     "Destroy the real time logger."},
-    {"real_time_logger_spin_once", dynamicgraph::python::debug::realTimeLoggerSpinOnce, METH_VARARGS,
-     "Destroy the real time logger."},
-    {"real_time_logger_instance", dynamicgraph::python::debug::realTimeLoggerInstance, METH_VARARGS,
-     "Starts the real time logger."},
-    {"error_out", (PyCFunction)dynamicgraph::python::error_out, METH_NOARGS, NULL},
-    {NULL, NULL, 0, NULL} /* Sentinel */
-};
-
-#if PY_MAJOR_VERSION >= 3
-__attribute__((unused)) static struct PyModuleDef dynamicGraphModuleDef = {
-    PyModuleDef_HEAD_INIT,
-    "wrap",
-    NULL,
-    sizeof(struct dynamicgraph::python::module_state),
-    dynamicGraphMethods,
-    NULL,
-    NULL,
-    NULL,
-    NULL};
-#define GETSTATE(m) ((struct dynamicgraph::python::module_state*)PyModule_GetState(m))
-#define DGPYERROR(m) GETSTATE(m)->dgpyError
-#define INITERROR return NULL
-#else
-__attribute__((unused)) static struct module_state _state;
-#define GETSTATE(m) (&dynamicgraph::python::_state)
-#define DGPYERROR(m) dynamicgraph::python::dgpyError
-#define INITERROR return
-#endif
-
 }  // namespace python
 }  // namespace dynamicgraph
 
diff --git a/include/dynamic-graph/python/exception-python.hh b/include/dynamic-graph/python/exception-python.hh
deleted file mode 100644
index 9dd7d2fc6f2298363b123fc90732172d7048c49d..0000000000000000000000000000000000000000
--- a/include/dynamic-graph/python/exception-python.hh
+++ /dev/null
@@ -1,47 +0,0 @@
-// -*- mode: c++ -*-
-// Copyright 2010, François Bleibel, Thomas Moulard, Olivier Stasse,
-// JRL, CNRS/AIST.
-
-#ifndef DYNAMIC_GRAPH_PYTHON_EXCEPTION_PYTHON_H
-#define DYNAMIC_GRAPH_PYTHON_EXCEPTION_PYTHON_H
-
-#include <dynamic-graph/fwd.hh>
-#include <dynamic-graph/exception-abstract.h>
-#include "dynamic-graph/python/python-compat.hh"
-
-// Depending on whether one is building or using the
-// library define DLLAPI to import or export.
-#if defined(WIN32)
-#if defined(wrap_EXPORTS)
-#define WRAP_DLLAPI __declspec(dllexport)
-#else
-#define WRAP_DLLAPI __declspec(dllimport)
-#endif
-#else
-#define WRAP_DLLAPI
-#endif
-
-namespace dynamicgraph {
-namespace python {
-
-/// \ingroup error
-///
-/// \brief Generic error class.
-class WRAP_DLLAPI ExceptionPython : public ExceptionAbstract {
- public:
-  enum ErrorCodeEnum { GENERIC, VALUE_PARSING, VECTOR_PARSING, MATRIX_PARSING, CLASS_INCONSISTENT };
-
-  static const std::string EXCEPTION_NAME;
-
-  explicit ExceptionPython(const ExceptionPython::ErrorCodeEnum& errcode, const std::string& msg = "");
-
-  ExceptionPython(const ExceptionPython::ErrorCodeEnum& errcode, const std::string& msg, const char* format, ...);
-
-  virtual ~ExceptionPython() throw() {}
-
-  virtual const std::string& getExceptionName() const { return ExceptionPython::EXCEPTION_NAME; }
-};
-}  // end of namespace python
-}  // end of namespace dynamicgraph
-
-#endif  //! DYNAMIC_GRAPH_PYTHON_EXCEPTION_PYTHON_H
diff --git a/include/dynamic-graph/python/exception.hh b/include/dynamic-graph/python/exception.hh
deleted file mode 100644
index 522c45c4e2efd06012829d397ae2f35a9009dbfa..0000000000000000000000000000000000000000
--- a/include/dynamic-graph/python/exception.hh
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2010, Florent Lamiraux, Thomas Moulard, LAAS-CNRS.
-
-#ifndef DYNAMIC_GRAPH_PYTHON_EXCEPTION
-#define DYNAMIC_GRAPH_PYTHON_EXCEPTION
-
-#include "dynamic-graph/python/dynamic-graph-py.hh"
-
-/// \brief Catch all exceptions which may be sent when C++ code is
-/// called.
-#define CATCH_ALL_EXCEPTIONS(m)                         \
-  catch (const std::exception& exc) {                   \
-    PyErr_SetString(DGPYERROR(m), exc.what());          \
-    return NULL;                                        \
-  }                                                     \
-  catch (const char* s) {                               \
-    PyErr_SetString(DGPYERROR(m), s);                   \
-    return NULL;                                        \
-  }                                                     \
-  catch (...) {                                         \
-    PyErr_SetString(DGPYERROR(m), "Unknown exception"); \
-    return NULL;                                        \
-  }                                                     \
-  struct e_n_d__w_i_t_h__s_e_m_i_c_o_l_o_n
-
-#endif  //! DYNAMIC_GRAPH_PYTHON_EXCEPTION
diff --git a/include/dynamic-graph/python/module.hh b/include/dynamic-graph/python/module.hh
new file mode 100644
index 0000000000000000000000000000000000000000..b93787b8c3937056474ddeae6b0534b4f4d12274
--- /dev/null
+++ b/include/dynamic-graph/python/module.hh
@@ -0,0 +1,81 @@
+#ifndef DYNAMIC_GRAPH_PYTHON_MODULE_HH
+#define DYNAMIC_GRAPH_PYTHON_MODULE_HH
+
+#ifdef PINOCCHIO_WITH_URDFDOM
+// If pinocchio is used, we have to include pinocchio header before boost mpl
+#include <pinocchio/fwd.hpp>
+#endif
+
+#include <boost/python.hpp>
+#include <boost/mpl/for_each.hpp>
+
+#include <dynamic-graph/entity.h>
+#include <dynamic-graph/python/dynamic-graph-py.hh>
+
+namespace dynamicgraph {
+namespace python {
+
+constexpr int AddSignals = 1;
+constexpr int AddCommands = 2;
+
+namespace internal {
+
+template <typename T, int Options = AddCommands | AddSignals>
+bp::object makeEntity1(const char* name) {
+  Entity* ent = entity::create(T::CLASS_NAME.c_str(), name);
+  assert(dynamic_cast<T*>(ent) != NULL);
+  bp::object obj(bp::ptr(static_cast<T*>(ent)));
+  if (Options & AddCommands) entity::addCommands(obj);
+  if (Options & AddSignals) entity::addSignals(obj);
+  return obj;
+}
+template <typename T, int Options = AddCommands | AddSignals>
+bp::object makeEntity2() {
+  return makeEntity1<T, Options>("");
+}
+
+}  // namespace internal
+
+/// \tparam Options by default, all the signals and commands are added as
+///         attribute to the Python object. This behaviour works fine for
+///         entities that have static commands and signals.
+///         If some commands or signals are added or removed dynamiccally, then
+///         it is better to disable the default behaviour and handle it
+///         specifically.
+template <typename T, typename bases = boost::python::bases<dynamicgraph::Entity>,
+          int Options = AddCommands | AddSignals>
+inline auto exposeEntity() {
+  // std::string hiddenClassName ("_" + T::CLASS_NAME);
+  std::string hiddenClassName(T::CLASS_NAME);
+  namespace bp = boost::python;
+  bp::class_<T, bases, boost::noncopyable> obj(hiddenClassName.c_str(), bp::init<std::string>());
+  /* TODO at the moment, I couldn't easily find a way to define a Python constructor
+   * that would create the entity via the factory and then populate the
+   * python object with its commands.
+   * This is achieved with a factory function of the same name.
+  obj.def ("__init__", bp::raw_function(+[](bp::object args, bp::dict) {
+          if (bp::len(args) != 2)
+            throw std::length_error("Expected 2 arguments.");
+          bp::object self = args[0];
+          self.attr("__init__")(bp::extract<std::string>(args[1]));
+          Entity* ent = entity::create(T::CLASS_NAME.c_str(), name);
+          if (dynamic_cast<T*>(ent) == NULL)
+            std::cout << "foo" << std::endl;
+          assert(dynamic_cast<T*>(ent) != NULL);
+          self = bp::object(bp::ptr(static_cast<T*>(ent)));
+          //dynamicgraph::Entity& unused = bp::extract<dynamicgraph::Entity&>(self);
+          //entity::addCommands(self);
+        })
+  ;
+  */
+  bp::def(T::CLASS_NAME.c_str(), &internal::makeEntity1<T, Options>);
+  bp::def(T::CLASS_NAME.c_str(), &internal::makeEntity2<T, Options>);
+  if (!(Options & AddCommands)) obj.def("add_commands", &entity::addCommands);
+  if (!(Options & AddSignals)) obj.def("add_signals", &entity::addSignals);
+  return obj;
+}
+
+}  // namespace python
+}  // namespace dynamicgraph
+
+#endif  // DYNAMIC_GRAPH_PYTHON_MODULE_HH
diff --git a/include/dynamic-graph/python/signal-wrapper.hh b/include/dynamic-graph/python/signal-wrapper.hh
index 3fd4cb3eeeb8c642be139536d60483981ca0ee71..941ca970d3a517aec0448922d35af16918813388 100644
--- a/include/dynamic-graph/python/signal-wrapper.hh
+++ b/include/dynamic-graph/python/signal-wrapper.hh
@@ -4,6 +4,8 @@
 #ifndef DGPY_SIGNAL_WRAPPER
 #define DGPY_SIGNAL_WRAPPER
 
+#include <boost/python.hpp>
+
 #include <dynamic-graph/linear-algebra.h>
 #include <dynamic-graph/signal.h>
 #include <dynamic-graph/entity.h>
@@ -11,22 +13,12 @@
 
 namespace dynamicgraph {
 namespace python {
-namespace signalWrapper {
-void convert(PyObject* o, int& v);
-void convert(PyObject* o, bool& v);
-void convert(PyObject* o, float& v);
-void convert(PyObject* o, double& v);
-// void convert (PyObject* o, std::string& v);
-void convert(PyObject* o, Vector& v);
-// void convert (PyObject* o, Eigen::MatrixXd& v);
-// void convert (PyObject* o, Eigen::Matrix4d& v);
-}  // namespace signalWrapper
 
 class PythonSignalContainer : public Entity {
   DYNAMIC_GRAPH_ENTITY_DECL();
 
  public:
-  PythonSignalContainer(const std::string& name);
+  using Entity::Entity;
 
   void signalRegistration(const SignalArray<int>& signals);
 
@@ -37,17 +29,17 @@ template <class T, class Time>
 class SignalWrapper : public Signal<T, Time> {
  public:
   typedef Signal<T, Time> parent_t;
+  typedef boost::python::object pyobject;
 
-  static bool checkCallable(PyObject* c, std::string& error);
+  static bool checkCallable(pyobject c, std::string& error);
 
-  SignalWrapper(std::string name, PyObject* _callable) : parent_t(name), callable(_callable) {
+  SignalWrapper(std::string name, pyobject callable) : parent_t(name), callable(callable) {
     typedef boost::function2<T&, T&, Time> function_t;
-    Py_INCREF(callable);
     function_t f = boost::bind(&SignalWrapper::call, this, _1, _2);
     this->setFunction(f);
   }
 
-  virtual ~SignalWrapper() { Py_DECREF(callable); };
+  virtual ~SignalWrapper(){};
 
  private:
   T& call(T& value, Time t) {
@@ -56,18 +48,12 @@ class SignalWrapper : public Signal<T, Time> {
     if (PyGILState_GetThisThreadState() == NULL) {
       dgDEBUG(10) << "python thread not initialized" << std::endl;
     }
-    char format[] = "i";
-    PyObject* obj = PyObject_CallFunction(callable, format, t);
-    if (obj == NULL) {
-      dgERROR << "Could not call callable" << std::endl;
-    } else {
-      signalWrapper::convert(obj, value);
-      Py_DECREF(obj);
-    }
+    pyobject obj = callable(t);
+    value = boost::python::extract<T>(obj);
     PyGILState_Release(gstate);
     return value;
   }
-  PyObject* callable;
+  pyobject callable;
 };
 
 }  // namespace python
diff --git a/include/dynamic-graph/python/signal.hh b/include/dynamic-graph/python/signal.hh
new file mode 100644
index 0000000000000000000000000000000000000000..ea115e655df193dbee1f9c8c57cf8230ea005685
--- /dev/null
+++ b/include/dynamic-graph/python/signal.hh
@@ -0,0 +1,73 @@
+// Copyright 2020, Joseph Mirabel, LAAS-CNRS.
+
+#include <sstream>
+
+#include <boost/python.hpp>
+
+#include <dynamic-graph/signal-base.h>
+#include <dynamic-graph/signal-ptr.h>
+#include <dynamic-graph/signal-time-dependent.h>
+#include <dynamic-graph/signal.h>
+
+#include "dynamic-graph/python/signal-wrapper.hh"
+
+namespace dynamicgraph {
+namespace python {
+
+template <typename T, typename Time>
+auto exposeSignal(const std::string& name) {
+  namespace bp = boost::python;
+
+  typedef Signal<T, Time> S_t;
+  bp::class_<S_t, bp::bases<SignalBase<Time> >, boost::noncopyable> obj(
+      name.c_str(), bp::init<std::string>());
+  obj.add_property(
+      "value",
+      bp::make_function(&S_t::accessCopy,
+                        bp::return_value_policy<bp::copy_const_reference>()),
+      &S_t::setConstant,  // TODO check the setter
+      "the signal value.\n"
+      "warning: for Eigen objects, sig.value[0] = 1. may not work).");
+  return obj;
+}
+
+template <typename T, typename Time>
+auto exposeSignalWrapper(const std::string& name) {
+  namespace bp = boost::python;
+
+  typedef SignalWrapper<T, Time> S_t;
+  bp::class_<S_t, bp::bases<Signal<T, Time> >, boost::noncopyable> obj(
+      name.c_str(), bp::no_init);
+  return obj;
+}
+
+template <typename T, typename Time>
+auto exposeSignalPtr(const std::string& name) {
+  namespace bp = boost::python;
+
+  typedef SignalPtr<T, Time> S_t;
+  bp::class_<S_t, bp::bases<Signal<T, Time> >, boost::noncopyable> obj(
+      name.c_str(), bp::no_init);
+  return obj;
+}
+
+template <typename T, typename Time>
+auto exposeSignalTimeDependent(const std::string& name) {
+  namespace bp = boost::python;
+
+  typedef SignalTimeDependent<T, Time> S_t;
+  bp::class_<S_t, bp::bases<Signal<T, Time> >, boost::noncopyable> obj(
+      name.c_str(), bp::no_init);
+  return obj;
+}
+
+template <typename T, typename Time>
+void exposeSignalsOfType(const std::string& name) {
+  exposeSignal<T, Time>("Signal" + name);
+  exposeSignalPtr<T, Time>("SignalPtr" + name);
+  exposeSignalWrapper<T, Time>("SignalWrapper" + name);
+  exposeSignalTimeDependent<T, Time>("SignalTimeDependent" + name);
+}
+
+}  // namespace python
+}  // namespace dynamicgraph
diff --git a/package.xml b/package.xml
index 5338cfc13ac95a3536742efa07fdfd10678b610a..60a02df01607ba3c067d688e8687ebb5daa77d99 100644
--- a/package.xml
+++ b/package.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <package format="3">
   <name>dynamic-graph-python</name>
-  <version>3.5.3</version>
+  <version>4.0.0</version>
   <description>
     Dynamic graph library Python bindings
   </description>
@@ -20,7 +20,8 @@
   <exec_depend condition="$ROS_VERSION == 2">ament_cmake</exec_depend>
   <depend>dynamic-graph</depend>
   <depend>boost</depend>
-  
+  <depend>eigenpy</depend>
+
   <buildtool_depend>cmake</buildtool_depend>
 
   <export>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b8cf40d3eedd630f6dbe398e3772f5ed366a9e0f..98a95a689d37565dd64eba19566ac635219576fb 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -16,6 +16,8 @@ FOREACH(source ${PYTHON_SOURCES})
 ENDFOREACH(source)
 
 # --- ADD the wrap on the dg modules
-DYNAMIC_GRAPH_PYTHON_MODULE("tracer" dynamic-graph::tracer tracer-wrap)
-DYNAMIC_GRAPH_PYTHON_MODULE("tracer_real_time" dynamic-graph::tracer-real-time
-  tracer_real_time-wrap)
+LINK_DIRECTORIES(${DYNAMIC_GRAPH_PLUGINDIR})
+DYNAMIC_GRAPH_PYTHON_MODULE("tracer" dynamic-graph::tracer tracer-wrap
+  SOURCE_PYTHON_MODULE ${CMAKE_CURRENT_SOURCE_DIR}/dynamic_graph/tracer/wrap.cc)
+DYNAMIC_GRAPH_PYTHON_MODULE("tracer_real_time" dynamic-graph::tracer-real-time tracer_real_time-wrap
+  SOURCE_PYTHON_MODULE ${CMAKE_CURRENT_SOURCE_DIR}/dynamic_graph/tracer_real_time/wrap.cc)
diff --git a/src/dynamic_graph/CMakeLists.txt b/src/dynamic_graph/CMakeLists.txt
index adc8bd7f4eb17f79ee008e08ca7ea8e9bd158bbf..19e38cb49759cd9b40fbbce582e8428bc7f981b1 100644
--- a/src/dynamic_graph/CMakeLists.txt
+++ b/src/dynamic_graph/CMakeLists.txt
@@ -3,22 +3,16 @@
 SET(PYTHON_MODULE wrap)
 
 ADD_LIBRARY(${PYTHON_MODULE} MODULE
-  convert-dg-to-py.cc
   debug-py.cc
   dynamic-graph-py.cc
-  entity-py.cc
-  exception-python.cc
   factory-py.cc
   pool-py.cc
-  python-compat.cc
   signal-base-py.cc
-  signal-caster-py.cc
   signal-wrapper.cc
   )
 
-TARGET_INCLUDE_DIRECTORIES(${PYTHON_MODULE} SYSTEM PUBLIC ${PYTHON_INCLUDE_DIRS})
-TARGET_LINK_LIBRARIES(${PYTHON_MODULE} ${PYTHON_LIBRARY}
-  dynamic-graph::dynamic-graph)
+TARGET_LINK_LIBRARIES(${PYTHON_MODULE} PUBLIC ${PROJECT_NAME} eigenpy::eigenpy)
+TARGET_LINK_BOOST_PYTHON(${PYTHON_MODULE} PRIVATE)
 
 # Remove prefix lib
 SET_TARGET_PROPERTIES(${PYTHON_MODULE} PROPERTIES PREFIX "")
diff --git a/src/dynamic_graph/__init__.py b/src/dynamic_graph/__init__.py
index 0fe54015dafc853beff835183dcb7c8d8fffd0f5..c9a0924adb870e49b87d10fa78a4ceb022740382 100644
--- a/src/dynamic_graph/__init__.py
+++ b/src/dynamic_graph/__init__.py
@@ -10,26 +10,4 @@ import sys
 from . import entity  # noqa
 from . import signal_base  # noqa
 
-try:
-    from DLFCN import RTLD_NOW, RTLD_GLOBAL
-except ModuleNotFoundError:  # Python 3
-    from os import RTLD_NOW, RTLD_GLOBAL
-
-flags = sys.getdlopenflags()
-
-# Import C++ symbols in a global scope
-# This is necessary for signal compiled in different modules to be compatible
-
-sys.setdlopenflags(RTLD_NOW | RTLD_GLOBAL)
 from .wrap import *  # noqa
-
-# Recover previous flags
-sys.setdlopenflags(flags)
-
-
-def plug(signalOut, signalIn):
-    """
-    Plug an output signal into an input signal
-    """
-    # get signals and entities
-    w_plug(signalOut.obj, signalIn.obj)  # noqa
diff --git a/src/dynamic_graph/convert-dg-to-py.cc b/src/dynamic_graph/convert-dg-to-py.cc
index 7ad8f4cf5af1a8bc7787e4150af9fea12eaf3230..b9ff9e9b5e5e8615199045426c44bfe935abb895 100644
--- a/src/dynamic_graph/convert-dg-to-py.cc
+++ b/src/dynamic_graph/convert-dg-to-py.cc
@@ -3,6 +3,9 @@
 #include <iostream>
 #include <sstream>
 
+#include <boost/python.hpp>
+#include <boost/python/stl_iterator.hpp>
+
 #include <dynamic-graph/signal-base.h>
 #include <dynamic-graph/signal.h>
 #include <dynamic-graph/signal-caster.h>
@@ -17,209 +20,38 @@ using ::dynamicgraph::SignalBase;
 namespace python {
 namespace convert {
 
-void fillMatrixRow(Matrix& m, unsigned iRow, PyObject* sequence) {
-  if (PySequence_Size(sequence) != (int)m.cols()) {
-    throw ExceptionPython(ExceptionPython::MATRIX_PARSING, "lines of matrix have different sizes.");
-  }
-  for (int iCol = 0; iCol < m.cols(); iCol++) {
-    PyObject* pyDouble = PySequence_GetItem(sequence, iCol);
-    if (PyFloat_Check(pyDouble))
-      m(iRow, iCol) = PyFloat_AsDouble(pyDouble);
-    else if (PyLong_Check(pyDouble))
-      m(iRow, iCol) = (int)PyLong_AsLong(pyDouble) + 0.0;
-    else
-      throw ExceptionPython(ExceptionPython::MATRIX_PARSING,
-                            "element of matrix should be "
-                            "a floating point number.");
-  }
-}
-void fillMatrixRow(Eigen::Matrix4d& m, unsigned iRow, PyObject* sequence) {
-  if (PySequence_Size(sequence) != (int)m.cols()) {
-    throw ExceptionPython(ExceptionPython::MATRIX_PARSING, "lines of matrix have different sizes.");
-  }
-  for (int iCol = 0; iCol < m.cols(); iCol++) {
-    PyObject* pyDouble = PySequence_GetItem(sequence, iCol);
-    if (PyFloat_Check(pyDouble))
-      m(iRow, iCol) = PyFloat_AsDouble(pyDouble);
-    else if (PyLong_Check(pyDouble))
-      m(iRow, iCol) = (int)PyLong_AsLong(pyDouble) + 0.0;
-    else
-      throw ExceptionPython(ExceptionPython::MATRIX_PARSING,
-                            "element of matrix should be "
-                            "a floating point number.");
-  }
-}
+namespace bp = boost::python;
 
-command::Value pythonToValue(PyObject* pyObject, const command::Value::Type& valueType) {
+command::Value toValue(bp::object o, const command::Value::Type& valueType) {
   using command::Value;
-  bool bvalue;
-  unsigned uvalue;
-  int ivalue;
-  float fvalue;
-  double dvalue;
-  std::string svalue;
-  Vector v;
-  Matrix m;
-  Eigen::Matrix4d m4;
-  Py_ssize_t nCols;
-  Py_ssize_t size;
-  PyObject* row;
-  Py_ssize_t nRows;
-
   switch (valueType) {
     case (Value::BOOL):
-      if (!PyBool_Check(pyObject)) {
-        throw ExceptionPython(ExceptionPython::VALUE_PARSING, "bool");
-      }
-      bvalue = PyObject_IsTrue(pyObject);
-      return Value(bvalue);
-      break;
+      return Value(bp::extract<bool>(o));
     case (Value::UNSIGNED):
-      if (PyLong_Check(pyObject)) {
-        uvalue = (unsigned int)PyLong_AsUnsignedLongMask(pyObject);
-#if PY_MAJOR_VERSION == 2
-      } else if (PyInt_Check(pyObject)) {
-        uvalue = (unsigned int)PyInt_AsUnsignedLongMask(pyObject);
-#endif
-      } else {
-        throw ExceptionPython(ExceptionPython::VALUE_PARSING, "unsigned int");
-      }
-      return Value(uvalue);
-      break;
+      return Value(bp::extract<unsigned>(o));
     case (Value::INT):
-      if (PyLong_Check(pyObject)) {
-        ivalue = (int)PyLong_AsLong(pyObject);
-#if PY_MAJOR_VERSION == 2
-      } else if (PyInt_Check(pyObject)) {
-        ivalue = (int)PyInt_AsLong(pyObject);
-#endif
-      } else {
-        throw ExceptionPython(ExceptionPython::VALUE_PARSING, "int");
-      }
-      return Value(ivalue);
-      break;
+      return Value(bp::extract<int>(o));
     case (Value::FLOAT):
-      if (PyFloat_Check(pyObject)) {
-        fvalue = (float)PyFloat_AsDouble(pyObject);
-      } else if (PyLong_Check(pyObject)) {
-        fvalue = (float)PyLong_AsLong(pyObject);
-#if PY_MAJOR_VERSION == 2
-      } else if (PyInt_Check(pyObject)) {
-        fvalue = (float)PyInt_AsLong(pyObject);
-#endif
-      } else {
-        throw ExceptionPython(ExceptionPython::VALUE_PARSING, "float");
-      }
-      return Value(fvalue);
-      break;
+      return Value(bp::extract<float>(o));
     case (Value::DOUBLE):
-      if (PyFloat_Check(pyObject)) {
-        dvalue = PyFloat_AsDouble(pyObject);
-      } else if (PyLong_Check(pyObject)) {
-        dvalue = (double)PyLong_AsLong(pyObject);
-#if PY_MAJOR_VERSION == 2
-      } else if (PyInt_Check(pyObject)) {
-        dvalue = (double)PyInt_AsLong(pyObject);
-#endif
-      } else {
-        throw ExceptionPython(ExceptionPython::VALUE_PARSING, "double");
-      }
-      return Value(dvalue);
-      break;
+      return Value(bp::extract<double>(o));
     case (Value::STRING):
-      if (!PyUnicode_Check(pyObject)
-#if PY_MAJOR_VERSION == 2
-          && !PyString_Check(pyObject)
-#endif
-      ) {
-        throw ExceptionPython(ExceptionPython::VALUE_PARSING, "string");
-      }
-      svalue = obj_to_str(pyObject);
-      return Value(svalue);
-      break;
+      return Value(bp::extract<std::string>(o));
     case (Value::VECTOR):
-      // Check that argument is a tuple
-      if (!PySequence_Check(pyObject)) {
-        throw ExceptionPython(ExceptionPython::VALUE_PARSING, "vector");
-      }
-      size = PySequence_Size(pyObject);
-      v.resize(size);
-      for (Py_ssize_t i = 0; i < size; i++) {
-        PyObject* pyDouble = PySequence_GetItem(pyObject, i);
-        if (PyFloat_Check(pyDouble))
-          v(i) = PyFloat_AsDouble(pyDouble);
-        else if (PyLong_Check(pyDouble))
-          v(i) = (int)PyLong_AsLong(pyDouble) + 0.0;
-#if PY_MAJOR_VERSION == 2
-        else if (PyInt_Check(pyDouble))
-          v(i) = (int)PyInt_AsLong(pyDouble) + 0.0;
-#endif
-        else
-          throw ExceptionPython(ExceptionPython::VECTOR_PARSING,
-                                "element of vector should be a floating "
-                                "point number.");
-      }
-      return Value(v);
-      break;
+      // TODO for backward compatibility, support tuple or list ?
+      // I don't think so
+      return Value(bp::extract<Vector>(o));
     case (Value::MATRIX):
-      // Check that argument is a tuple
-      if (!PySequence_Check(pyObject)) {
-        throw ExceptionPython(ExceptionPython::VALUE_PARSING, "matrix");
-      }
-      nRows = PySequence_Size(pyObject);
-      if (nRows == 0) {
-        return Value(Matrix());
-      }
-      row = PySequence_GetItem(pyObject, 0);
-      if (!PySequence_Check(row)) {
-        throw ExceptionPython(ExceptionPython::MATRIX_PARSING, "matrix");
-      }
-      nCols = PySequence_Size(row);
-
-      m.resize((unsigned int)nRows, (unsigned int)nCols);
-      fillMatrixRow(m, 0, row);
-
-      for (Py_ssize_t iRow = 1; iRow < nRows; iRow++) {
-        row = PySequence_GetItem(pyObject, iRow);
-        if (!PySequence_Check(row)) {
-          throw ExceptionPython(ExceptionPython::MATRIX_PARSING, "matrix");
-        }
-        fillMatrixRow(m, static_cast<unsigned>(iRow), row);
-      }
-      return Value(m);
-      break;
+      // TODO for backward compatibility, support tuple or list ?
+      // I don't think so
+      return Value(bp::extract<Matrix>(o));
     case (Value::MATRIX4D):
-      // Check that argument is a tuple
-      if (!PySequence_Check(pyObject)) {
-        throw ExceptionPython(ExceptionPython::VALUE_PARSING, "matrix4d");
-      }
-      nRows = PySequence_Size(pyObject);
-      if (nRows == 0) {
-        return Value(Eigen::Matrix4d());
-      }
-      row = PySequence_GetItem(pyObject, 0);
-      if (!PySequence_Check(row)) {
-        throw ExceptionPython(ExceptionPython::MATRIX_PARSING, "matrix4d");
-      }
-      nCols = PySequence_Size(row);
-
-      m4.resize(nRows, nCols);
-      fillMatrixRow(m4, 0, row);
-
-      for (Py_ssize_t iRow = 1; iRow < nRows; iRow++) {
-        row = PySequence_GetItem(pyObject, iRow);
-        if (!PySequence_Check(row)) {
-          throw ExceptionPython(ExceptionPython::MATRIX_PARSING, "matrix");
-        }
-        fillMatrixRow(m4, static_cast<unsigned>(iRow), row);
-      }
-      return Value(m4);
-      break;
+      return Value(bp::extract<Eigen::Matrix4d>(o));
     case (Value::VALUES):
       // TODO the vector of values cannot be built since
       // - the value type inside the vector are not know
       // - inferring the value type from the Python type is not implemented.
-      throw ExceptionPython(ExceptionPython::VALUE_PARSING, "not implemented: cannot create a vector of values");
+      throw std::invalid_argument("not implemented: cannot create a vector of values");
       break;
     default:
       std::cerr << "Only int, double and string are supported." << std::endl;
@@ -227,98 +59,36 @@ command::Value pythonToValue(PyObject* pyObject, const command::Value::Type& val
   return Value();
 }
 
-PyObject* vectorToPython(const Vector& vector) {
-  PyObject* tuple = PyTuple_New(vector.size());
-  for (int index = 0; index < vector.size(); index++) {
-    PyObject* pyDouble = PyFloat_FromDouble(vector(index));
-    PyTuple_SET_ITEM(tuple, index, pyDouble);
-  }
-  return tuple;
-}
-
-PyObject* matrixToPython(const Matrix& matrix) {
-  PyObject* tuple = PyTuple_New(matrix.rows());
-  for (int iRow = 0; iRow < matrix.rows(); iRow++) {
-    PyObject* row = PyTuple_New(matrix.cols());
-    for (int iCol = 0; iCol < matrix.cols(); iCol++) {
-      PyObject* pyDouble = PyFloat_FromDouble(matrix(iRow, iCol));
-      PyTuple_SET_ITEM(row, iCol, pyDouble);
-    }
-    PyTuple_SET_ITEM(tuple, iRow, row);
-  }
-  return tuple;
-}
-
-PyObject* matrix4dToPython(const Eigen::Matrix4d& matrix) {
-  PyObject* tuple = PyTuple_New(matrix.rows());
-  for (int iRow = 0; iRow < matrix.rows(); iRow++) {
-    PyObject* row = PyTuple_New(matrix.cols());
-    for (int iCol = 0; iCol < matrix.cols(); iCol++) {
-      PyObject* pyDouble = PyFloat_FromDouble(matrix(iRow, iCol));
-      PyTuple_SET_ITEM(row, iCol, pyDouble);
-    }
-    PyTuple_SET_ITEM(tuple, iRow, row);
-  }
-  return tuple;
-}
-
-PyObject* valuesToPython(const dynamicgraph::command::Values& vector) {
-  PyObject* tuple = PyTuple_New(vector.size());
-  for (std::size_t index = 0; index < vector.size(); index++) {
-    PyObject* item = valueToPython(vector[index]);
-    PyTuple_SET_ITEM(tuple, index, item);
-  }
-  return tuple;
-}
-
-PyObject* valueToPython(const command::Value& value) {
+bp::object fromValue(const command::Value& value) {
   using command::Value;
-  bool boolValue;
-  unsigned unsignedValue;
-  int intValue;
-  float floatValue;
-  double doubleValue;
-  std::string stringValue;
-  Vector vectorValue;
-  Matrix matrixValue;
-  Eigen::Matrix4d matrix4dValue;
   switch (value.type()) {
     case (Value::BOOL):
-      boolValue = value.value();
-      if (boolValue) {
-        return PyBool_FromLong(1);
-      }
-      return PyBool_FromLong(0);
+      return bp::object(value.boolValue());
     case (Value::UNSIGNED):
-      unsignedValue = value.value();
-      return Py_BuildValue("I", unsignedValue);
+      return bp::object(value.unsignedValue());
     case (Value::INT):
-      intValue = value.value();
-      return Py_BuildValue("i", intValue);
+      return bp::object(value.intValue());
     case (Value::FLOAT):
-      floatValue = value.value();
-      return Py_BuildValue("f", floatValue);
+      return bp::object(value.floatValue());
     case (Value::DOUBLE):
-      doubleValue = value.value();
-      return Py_BuildValue("d", doubleValue);
+      return bp::object(value.doubleValue());
     case (Value::STRING):
-      stringValue = (std::string)value.value();
-      return Py_BuildValue("s", stringValue.c_str());
+      return bp::object(value.stringValue());
     case (Value::VECTOR):
-      vectorValue = value.value();
-      return vectorToPython(vectorValue);
+      return bp::object(value.vectorValue());
     case (Value::MATRIX):
-      matrixValue = value.value();
-      return matrixToPython(matrixValue);
+      return bp::object(value.matrixXdValue());
     case (Value::MATRIX4D):
-      matrix4dValue = value.value();
-      return matrix4dToPython(matrix4dValue);
-    case (Value::VALUES):
-      return valuesToPython(value.constValuesValue());
+      return bp::object(value.matrix4dValue());
+    case (Value::VALUES): {
+      bp::list list;
+      for (const Value& v : value.constValuesValue()) list.append(fromValue(v));
+      return list;
+    }
+    case (Value::NONE):
     default:
-      return Py_BuildValue("");
+      return bp::object();
   }
-  return Py_BuildValue("");
 }
 
 }  // namespace convert
diff --git a/src/dynamic_graph/debug-py.cc b/src/dynamic_graph/debug-py.cc
index e612f0d2858bb1527749968d2e66e1c0d24c9f8d..dc72df831f2d7904642569f46dfdcdd755139f8c 100644
--- a/src/dynamic_graph/debug-py.cc
+++ b/src/dynamic_graph/debug-py.cc
@@ -11,7 +11,6 @@
 #include <dynamic-graph/pool.h>
 #include <dynamic-graph/entity.h>
 #include <vector>
-#include "dynamic-graph/python/exception.hh"
 
 #include <boost/shared_ptr.hpp>
 #include "dynamic-graph/python/dynamic-graph-py.hh"
@@ -21,108 +20,31 @@ typedef boost::shared_ptr<std::ofstream> ofstreamShrPtr;
 namespace dynamicgraph {
 namespace python {
 
-#if PY_MAJOR_VERSION == 2
-extern PyObject* dgpyError;
-#endif
-
 namespace debug {
 
 std::map<std::string, ofstreamShrPtr> mapOfFiles_;
 
-PyObject* addLoggerFileOutputStream(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  char* filename;
-  if (!PyArg_ParseTuple(args, "s", &filename)) return NULL;
-  std::string sfilename(filename);
-  try {
-    std::ofstream* aofs = new std::ofstream;
-    ofstreamShrPtr ofs_shrptr = boost::shared_ptr<std::ofstream>(aofs);
-    aofs->open(filename, std::ofstream::out);
-    dynamicgraph::RealTimeLogger::instance();
-    dgADD_OSTREAM_TO_RTLOG(*aofs);
-    dgRTLOG() << "Added " << filename << " as an output stream \n";
-    mapOfFiles_[sfilename] = ofs_shrptr;
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-  return Py_BuildValue("");
+void addLoggerFileOutputStream(const char* filename) {
+  std::ofstream* aofs = new std::ofstream;
+  ofstreamShrPtr ofs_shrptr = boost::shared_ptr<std::ofstream>(aofs);
+  aofs->open(filename, std::ofstream::out);
+  dynamicgraph::RealTimeLogger::instance();
+  dgADD_OSTREAM_TO_RTLOG(*aofs);
+  dgRTLOG() << "Added " << filename << " as an output stream \n";
+  mapOfFiles_[filename] = ofs_shrptr;
 }
 
-PyObject* closeLoggerFileOutputStream(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject*
-#else
-    PyObject*, PyObject*
-#endif
-) {
-  try {
-    for (std::map<std::string, ofstreamShrPtr>::iterator it = mapOfFiles_.begin(); it != mapOfFiles_.end(); ++it) {
-      it->second->close();
-    }
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-  return Py_BuildValue("");
+void closeLoggerFileOutputStream() {
+  for (const auto& el : mapOfFiles_) el.second->close();
 }
 
-PyObject* addLoggerCoutOutputStream(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject*
-#else
-    PyObject*, PyObject*
-#endif
-) {
-  try {
-    dgADD_OSTREAM_TO_RTLOG(std::cout);
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-  return Py_BuildValue("");
-}
+void addLoggerCoutOutputStream() { dgADD_OSTREAM_TO_RTLOG(std::cout); }
 
-PyObject* realTimeLoggerDestroy(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject*
-#else
-    PyObject*, PyObject*
-#endif
-) {
-  try {
-    RealTimeLogger::destroy();
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-  return Py_BuildValue("");
-}
+void realTimeLoggerDestroy() { RealTimeLogger::destroy(); }
 
-PyObject* realTimeLoggerSpinOnce(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject*
-#else
-    PyObject*, PyObject*
-#endif
-) {
-  try {
-    RealTimeLogger::instance().spinOnce();
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-  return Py_BuildValue("");
-}
+void realTimeLoggerSpinOnce() { RealTimeLogger::instance().spinOnce(); }
 
-PyObject* realTimeLoggerInstance(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject*
-#else
-    PyObject*, PyObject*
-#endif
-) {
-  try {
-    RealTimeLogger::instance();
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-  return Py_BuildValue("");
-}
+void realTimeLoggerInstance() { RealTimeLogger::instance(); }
 
 }  // namespace debug
 }  // namespace python
diff --git a/src/dynamic_graph/dynamic-graph-py.cc b/src/dynamic_graph/dynamic-graph-py.cc
index e26b8bdc1abeb337f8462c900de1bc8f968e0c87..7a6f0f6d479faa2ad6d6372db4bb1d8e1703eba3 100644
--- a/src/dynamic_graph/dynamic-graph-py.cc
+++ b/src/dynamic_graph/dynamic-graph-py.cc
@@ -3,165 +3,258 @@
 #include <iostream>
 #include <sstream>
 
+#include <boost/python.hpp>
+#include <boost/python/suite/indexing/map_indexing_suite.hpp>
+
+#include <eigenpy/eigenpy.hpp>
+#include <Eigen/Geometry>
+#include <eigenpy/geometry.hpp>
+
 #include <dynamic-graph/debug.h>
 #include <dynamic-graph/exception-factory.h>
 #include <dynamic-graph/signal-base.h>
+#include <dynamic-graph/signal.h>
+#include <dynamic-graph/signal-time-dependent.h>
+#include <dynamic-graph/entity.h>
+#include <dynamic-graph/command.h>
+#include <dynamic-graph/factory.h>
+#include <dynamic-graph/pool.h>
+
+#include <dynamic-graph/tracer.h>
 
-#include "dynamic-graph/python/exception.hh"
 #include "dynamic-graph/python/dynamic-graph-py.hh"
 #include "dynamic-graph/python/signal-wrapper.hh"
+#include "dynamic-graph/python/convert-dg-to-py.hh"
+#include "dynamic-graph/python/module.hh"
 
 namespace dynamicgraph {
 namespace python {
 
-#if PY_MAJOR_VERSION == 2
-PyObject* dgpyError;
-#endif
-
 /**
    \brief plug a signal into another one.
 */
-PyObject* plug(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  PyObject* objOut = NULL;
-  PyObject* objIn = NULL;
-  void* pObjOut;
-  void* pObjIn;
-
-  if (!PyArg_ParseTuple(args, "OO", &objOut, &objIn)) return NULL;
-
-  if (!PyCapsule_CheckExact(objOut)) {
-    PyErr_SetString(PyExc_TypeError,
-                    "first argument should be a pointer to"
-                    " signalBase<int>.");
-    return NULL;
-  }
-  if (!PyCapsule_CheckExact(objIn)) {
-    PyErr_SetString(PyExc_TypeError,
-                    "second argument should be a pointer to"
-                    " signalBase<int>.");
-    return NULL;
-  }
+void plug(SignalBase<int>* signalOut, SignalBase<int>* signalIn) { signalIn->plug(signalOut); }
 
-  pObjIn = PyCapsule_GetPointer(objIn, "dynamic_graph.Signal");
-  SignalBase<int>* signalIn = (SignalBase<int>*)pObjIn;
-  if (signalIn == NULL) {
-    std::ostringstream oss;
-    oss << "dynamic_graph.plug(a, b): Argument 'b' must be of type 'dynamic_graph.Signal', but got "
-        << PyCapsule_GetName(objIn);
-    PyErr_SetString(PyExc_TypeError, oss.str().c_str());
-    return NULL;
-  }
-  pObjOut = PyCapsule_GetPointer(objOut, "dynamic_graph.Signal");
-  SignalBase<int>* signalOut = (SignalBase<int>*)pObjOut;
-  if (signalOut == NULL) {
-    std::ostringstream oss;
-    oss << "dynamic_graph.plug(a, b): Argument 'a' must be of type 'dynamic_graph.Signal', but got "
-        << PyCapsule_GetName(objOut);
-    PyErr_SetString(PyExc_TypeError, oss.str().c_str());
-    return NULL;
-  }
-  std::ostringstream os;
-
-  try {
-    signalIn->plug(signalOut);
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-  return Py_BuildValue("");
+void enableTrace(bool enable, const char* filename) {
+  if (enable)
+    DebugTrace::openFile(filename);
+  else
+    DebugTrace::closeFile(filename);
 }
 
-PyObject* enableTrace(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  PyObject* boolean;
-  char* filename = NULL;
-
-  if (PyArg_ParseTuple(args, "Os", &boolean, &filename)) {
-    if (!PyBool_Check(boolean)) {
-      PyErr_SetString(PyExc_TypeError,
-                      "enableTrace takes as first "
-                      "argument True or False,\n"
-                      "           and as "
-                      "second argument a filename.");
-      return NULL;
-    }
-    if (PyObject_IsTrue(boolean)) {
-      try {
-        DebugTrace::openFile(filename);
-      }
-      CATCH_ALL_EXCEPTIONS(m);
-    } else {
-      try {
-        DebugTrace::closeFile(filename);
-      }
-      CATCH_ALL_EXCEPTIONS(m);
-    }
-  } else {
-    return NULL;
+}  // namespace python
+}  // namespace dynamicgraph
+
+namespace bp = boost::python;
+namespace dg = dynamicgraph;
+
+typedef bp::return_value_policy<bp::manage_new_object> manage_new_object;
+typedef bp::return_value_policy<bp::reference_existing_object> reference_existing_object;
+
+typedef dg::PoolStorage::Entities MapOfEntities;
+
+struct MapOfEntitiesPairToPythonConverter {
+  static PyObject* convert(const MapOfEntities::value_type& pair) {
+    return bp::incref(bp::make_tuple(pair.first, bp::ptr(pair.second)).ptr());
   }
-  return Py_BuildValue("");
+};
+
+MapOfEntities* getEntityMap() { return const_cast<MapOfEntities*>(&dg::PoolStorage::getInstance()->getEntityMap()); }
+
+dg::SignalBase<int>* getSignal(dg::Entity& e, const std::string& name) { return &e.getSignal(name); }
+
+class PythonEntity : public dg::Entity {
+  DYNAMIC_GRAPH_ENTITY_DECL();
+
+ public:
+  using dg::Entity::Entity;
+
+  void signalRegistration(dg::SignalBase<int>& signal) { dg::Entity::signalRegistration(signal); }
+  void signalDeregistration(const std::string& name) { dg::Entity::signalDeregistration(name); }
+};
+
+DYNAMICGRAPH_FACTORY_ENTITY_PLUGIN(PythonEntity, "PythonEntity");
+
+void exposeEntityBase() {
+  using namespace dynamicgraph;
+  bp::enum_<LoggerVerbosity>("LoggerVerbosity")
+      .value("VERBOSITY_ALL", VERBOSITY_ALL)
+      .value("VERBOSITY_INFO_WARNING_ERROR", VERBOSITY_INFO_WARNING_ERROR)
+      .value("VERBOSITY_WARNING_ERROR", VERBOSITY_WARNING_ERROR)
+      .value("VERBOSITY_ERROR", VERBOSITY_ERROR)
+      .value("VERBOSITY_NONE", VERBOSITY_NONE)
+      .export_values();
+
+  bp::class_<Entity, boost::noncopyable>("Entity", bp::no_init)
+      .add_property("name", bp::make_function(&Entity::getName, bp::return_value_policy<bp::copy_const_reference>()))
+      .add_property("className",
+                    bp::make_function(&Entity::getClassName, bp::return_value_policy<bp::copy_const_reference>()),
+                    "the class name of the Entity")
+      .add_property("__doc__", &Entity::getDocString)
+
+      .def("setLoggerVerbosityLevel", &Entity::setLoggerVerbosityLevel)
+      .def("getLoggerVerbosityLevel", &Entity::getLoggerVerbosityLevel)
+      .add_property("loggerVerbosityLevel", &Entity::setLoggerVerbosityLevel, &Entity::getLoggerVerbosityLevel,
+                    "the verbosity level of the entity")
+      .def("setTimeSample", &Entity::setTimeSample)
+      .def("getTimeSample", &Entity::getTimeSample)
+      .add_property("timeSample", &Entity::getTimeSample, &Entity::setTimeSample,
+                    "the time sample for printing debugging information")
+      .def("setStreamPrintPeriod", &Entity::setStreamPrintPeriod)
+      .def("getStreamPrintPeriod", &Entity::getStreamPrintPeriod)
+      .add_property("streamPrintPeriod", &Entity::getStreamPrintPeriod, &Entity::setStreamPrintPeriod,
+                    "set the period at which debugging information are printed")
+
+      .def("__str__",
+           +[](const Entity& e) -> std::string {
+             std::ostringstream os;
+             e.display(os);
+             return os.str();
+           })
+      .def("signals",
+           +[](const Entity& e) -> bp::list {
+             bp::list ret;
+             for (auto& el : e.getSignalMap()) ret.append(bp::ptr(el.second));
+             return ret;
+           },
+           "Return the list of signals.")
+      //.def("signal", +[](Entity& e, const std::string &name) { return &e.getSignal(name); },
+      // reference_existing_object())
+      .def("signal", &getSignal, reference_existing_object(), "get signal by name from an Entity", bp::arg("name"))
+      .def("hasSignal", &Entity::hasSignal, "return True if the entity has a signal with the given name")
+
+      .def("displaySignals",
+           +[](const Entity& e) {
+             Entity::SignalMap signals(e.getSignalMap());
+             std::cout << "--- <" << e.getName();
+             if (signals.empty())
+               std::cout << "> has no signal\n";
+             else
+               std::cout << "> signal list:\n";
+             for (const auto& el : signals) el.second->display(std::cout << "    |-- <") << '\n';
+           },
+           "Print the list of signals into standard output: temporary.")
+
+      /*
+      .def("__getattr__", +[](Entity& e, const std::string &name) -> SignalBase<int>* { return &e.getSignal(name); },
+          reference_existing_object())
+      def __getattr__(self, name):
+          try:
+              return self.signal(name)
+          except Exception:
+              try:
+                  object.__getattr__(self, name)
+              except AttributeError:
+                  raise AttributeError("'%s' entity has no attribute %s\n" % (self.name, name) +
+                                       '  entity attributes are usually either\n' + '    - commands,\n' +
+                                       '    - signals or,\n' + '    - user defined attributes')
+                                       */
+      /*
+      .def("__setattr__", +[](bp::object self, const std::string &name, bp::object value) {
+            Entity& e = bp::extract<Entity&> (self);
+            if (e.hasSignal(name))
+              throw std::invalid_argument(name + " already designates a signal. "
+                  "It is not advised to set a new attribute of the same name.");
+            // TODO How do you do that ? I am sure it is possible.
+            //object.__setattr__(self, name, value)
+          })
+          */
+
+      /* TODO ?
+      def boundNewCommand(self, cmdName):
+          """
+          At construction, all existing commands are bound directly in the class.
+          This method enables to bound new commands dynamically. These new bounds
+          are not made with the class, but directly with the object instance.
+          """
+      def boundAllNewCommands(self):
+          """
+          For all commands that are not attribute of the object instance nor of the
+          class, a new attribute of the instance is created to bound the command.
+          """
+          */
+
+      // For backward compat
+      .add_static_property("entities", bp::make_function(&getEntityMap, reference_existing_object()));
+
+  python::exposeEntity<PythonEntity, bp::bases<Entity>, 0>()
+      .def("signalRegistration", &PythonEntity::signalRegistration)
+      .def("signalDeregistration", &PythonEntity::signalDeregistration);
+
+  python::exposeEntity<python::PythonSignalContainer, bp::bases<Entity>, 0>().def(
+      "rmSignal", &python::PythonSignalContainer::rmSignal, "Remove a signal", bp::arg("signal_name"));
 }
 
-PyObject* error_out(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject*
-#else
-    PyObject*, PyObject*
-#endif
-) {
-  PyErr_SetString(DGPYERROR(m), "something bad happened");
-  return NULL;
+void exposeCommand() {
+  using dg::command::Command;
+  bp::class_<Command, boost::noncopyable>("Command", bp::no_init)
+      .def("__call__", bp::raw_function(dg::python::entity::executeCmd, 1), "execute the command")
+      .add_property("__doc__", &Command::getDocstring);
 }
 
-}  // namespace python
-}  // namespace dynamicgraph
+void exposeOldAPI() {
+  bp::def("plug", dynamicgraph::python::plug, "plug an output signal into an input signal",
+          (bp::arg("signalOut"), "signalIn"));
+  bp::def("enableTrace", dynamicgraph::python::enableTrace, "Enable or disable tracing debug info in a file");
+  // Signals
+  bp::def("create_signal_wrapper", dynamicgraph::python::signalBase::createSignalWrapper, reference_existing_object(),
+          "create a SignalWrapper C++ object");
+  // Entity
+  bp::def("factory_get_entity_class_list", dynamicgraph::python::factory::getEntityClassList,
+          "return the list of entity classes");
+  bp::def("writeGraph", dynamicgraph::python::pool::writeGraph, "Write the graph of entities in a filename.");
+  bp::def("get_entity_list", dynamicgraph::python::pool::getEntityList, "return the list of instanciated entities");
+  bp::def("addLoggerFileOutputStream", dynamicgraph::python::debug::addLoggerFileOutputStream,
+          "add a output file stream to the logger by filename");
+  bp::def("addLoggerCoutOutputStream", dynamicgraph::python::debug::addLoggerCoutOutputStream,
+          "add std::cout as output stream to the logger");
+  bp::def("closeLoggerFileOutputStream", dynamicgraph::python::debug::closeLoggerFileOutputStream,
+          "close all the loggers file output streams.");
+  bp::def("real_time_logger_destroy", dynamicgraph::python::debug::realTimeLoggerDestroy,
+          "Destroy the real time logger.");
+  bp::def("real_time_logger_spin_once", dynamicgraph::python::debug::realTimeLoggerSpinOnce,
+          "Destroy the real time logger.");
+  bp::def("real_time_logger_instance", dynamicgraph::python::debug::realTimeLoggerInstance,
+          "Starts the real time logger.");
+}
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#if PY_MAJOR_VERSION >= 3
-PyMODINIT_FUNC PyInit_wrap(void)
-#else
-void initwrap(void)
-#endif
-{
-#if PY_MAJOR_VERSION >= 3
-  PyObject* m = PyModule_Create(&dynamicgraph::python::dynamicGraphModuleDef);
-#else
-  PyObject* m = Py_InitModule("wrap", dynamicgraph::python::dynamicGraphMethods);
-#endif
-
-  if (m == NULL) INITERROR;
-
-  DGPYERROR(m) = PyErr_NewException(const_cast<char*>("dynamic_graph.dgpyError"), NULL, NULL);
-  if (DGPYERROR(m) == NULL) {
-    Py_DECREF(m);
-    INITERROR;
-  }
+void enableEigenPy() {
+  eigenpy::enableEigenPy();
 
-  Py_XINCREF(DGPYERROR(m));
-  if (PyModule_AddObject(m, "dgpyError", DGPYERROR(m)) < 0) {
-    Py_XDECREF(DGPYERROR(m));
-    Py_CLEAR(DGPYERROR(m));
-    Py_DECREF(m);
-    INITERROR;
-  }
+  if (!eigenpy::register_symbolic_link_to_registered_type<Eigen::Quaterniond>()) eigenpy::exposeQuaternion();
+  if (!eigenpy::register_symbolic_link_to_registered_type<Eigen::AngleAxisd>()) eigenpy::exposeAngleAxis();
 
-#if PY_MAJOR_VERSION >= 3
-  return m;
-#endif
+  eigenpy::enableEigenPySpecific<Eigen::Matrix4d>();
 }
 
-#ifdef __cplusplus
-}  // extern "C"
-#endif
+BOOST_PYTHON_MODULE(wrap) {
+  enableEigenPy();
+
+  exposeOldAPI();
+
+  dg::python::exposeSignals();
+  exposeEntityBase();
+  exposeCommand();
+
+  typedef dg::PoolStorage::Entities MapOfEntities;
+  bp::class_<MapOfEntities>("MapOfEntities")
+      .def("__len__", &MapOfEntities::size)
+      .def("keys",
+           +[](const MapOfEntities& m) -> bp::tuple {
+             bp::list res;
+             for (const auto& el : m) res.append(el.first);
+             return bp::tuple(res);
+           })
+      .def("values",
+           +[](const MapOfEntities& m) -> bp::tuple {
+             bp::list res;
+             for (const auto& el : m) res.append(bp::ptr(el.second));
+             return bp::tuple(res);
+           })
+      .def("__getitem__", static_cast<dg::Entity*& (MapOfEntities::*)(const std::string& k)>(&MapOfEntities::at),
+           reference_existing_object())
+      .def("__setitem__", +[](MapOfEntities& m, const std::string& n, dg::Entity* e) { m.emplace(n, e); })
+      .def("__iter__", bp::iterator<MapOfEntities>())
+      .def("__contains__", +[](const MapOfEntities& m, const std::string& n) -> bool { return m.count(n); });
+  bp::to_python_converter<MapOfEntities::value_type, MapOfEntitiesPairToPythonConverter>();
+}
diff --git a/src/dynamic_graph/entity-py.cc b/src/dynamic_graph/entity-py.cc
index b8f2c2441d91929ae2a27dbc904f72622045efd5..f5993fd06f6a85e06c911de7c6827c1a17e692df 100644
--- a/src/dynamic_graph/entity-py.cc
+++ b/src/dynamic_graph/entity-py.cc
@@ -6,13 +6,12 @@
 #include <dynamic-graph/factory.h>
 
 #include <dynamic-graph/command.h>
-#include <dynamic-graph/value.h>
-#include <dynamic-graph/pool.h>
 #include <dynamic-graph/linear-algebra.h>
+#include <dynamic-graph/pool.h>
+#include <dynamic-graph/value.h>
 
-#include "dynamic-graph/python/dynamic-graph-py.hh"
 #include "dynamic-graph/python/convert-dg-to-py.hh"
-#include "dynamic-graph/python/exception.hh"
+#include "dynamic-graph/python/dynamic-graph-py.hh"
 
 // Ignore "dereferencing type-punned pointer will break strict-aliasing rules"
 // warnings on gcc caused by Py_RETURN_TRUE and Py_RETURN_FALSE.
@@ -20,6 +19,8 @@
 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
 #endif
 
+namespace bp = boost::python;
+
 using dynamicgraph::Entity;
 using dynamicgraph::Matrix;
 using dynamicgraph::SignalBase;
@@ -32,637 +33,55 @@ namespace python {
 
 using namespace convert;
 
-#if PY_MAJOR_VERSION == 2
-extern PyObject* dgpyError;
-#endif
-
 namespace entity {
 
+/// \param obj an Entity object
+void addCommands(bp::object obj) {
+  Entity& entity = bp::extract<Entity&>(obj);
+  for (const auto& el : entity.getNewStyleCommandMap()) obj.attr(el.first.c_str()) = bp::object(bp::ptr(el.second));
+}
+
+/// \param obj an Entity object
+void addSignals(bp::object obj) {
+  Entity& entity = bp::extract<Entity&>(obj);
+  for (const auto& el : entity.getSignalMap())
+    // obj.attr(el.first.c_str()) = bp::make_function(
+    //+[&entity,el]() { return &entity.getSignal(el.first); },
+    // bp::return_value_policy<bp::reference_existing_object>());
+    obj.attr(el.first.c_str()) = bp::object(bp::ptr(el.second));
+}
+
 /**
    \brief Create an instance of Entity
 */
-PyObject* create(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  char* className = NULL;
-  char* instanceName = NULL;
-
-  if (!PyArg_ParseTuple(args, "ss", &className, &instanceName)) return NULL;
-
+Entity* create(const char* className, const char* instanceName) {
   Entity* obj = NULL;
   /* Try to find if the corresponding object already exists. */
   if (dynamicgraph::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(m), msg.c_str());
-      return NULL;
+      throw std::invalid_argument("Found an object named " + std::string(instanceName) +
+                                  ",\n"
+                                  "but this object is of type " +
+                                  std::string(obj->getClassName()) + " and not " + std::string(className));
     }
   } else /* If not, create a new object. */
   {
-    try {
-      obj = dynamicgraph::FactoryStorage::getInstance()->newEntity(std::string(className), std::string(instanceName));
-    }
-    CATCH_ALL_EXCEPTIONS(m);
-  }
-
-  // Return the pointer as a PyCapsule
-  return PyCapsule_New((void*)obj, "dynamic_graph.Entity", NULL);
-}
-
-/**
-   \brief Get name of entity
-*/
-PyObject* getName(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  PyObject* object = NULL;
-  void* pointer = NULL;
-  std::string name;
-
-  if (!PyArg_ParseTuple(args, "O", &object)) return NULL;
-  if (!PyCapsule_CheckExact(object)) {
-    PyErr_SetString(PyExc_TypeError, "function takes a PyCapsule as argument");
-    return NULL;
-  }
-
-  pointer = PyCapsule_GetPointer(object, "dynamic_graph.Entity");
-  Entity* entity = (Entity*)pointer;
-
-  try {
-    name = entity->getName();
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-  return Py_BuildValue("s", name.c_str());
-}
-
-/**
-   \brief Get class name of entity
-*/
-PyObject* getClassName(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  PyObject* object = NULL;
-  void* pointer = NULL;
-  std::string name;
-
-  if (!PyArg_ParseTuple(args, "O", &object)) return NULL;
-  if (!PyCapsule_CheckExact(object)) {
-    PyErr_SetString(PyExc_TypeError, "function takes a PyCapsule as argument");
-    return NULL;
-  }
-
-  pointer = PyCapsule_GetPointer(object, "dynamic_graph.Entity");
-  Entity* entity = (Entity*)pointer;
-
-  try {
-    name = entity->getClassName();
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-  return Py_BuildValue("s", name.c_str());
-}
-
-/**
-   \brief Check if the entity has a signal with the given name
-*/
-PyObject* hasSignal(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  char* name = NULL;
-  PyObject* object = NULL;
-  void* pointer = NULL;
-
-  if (!PyArg_ParseTuple(args, "Os", &object, &name)) Py_RETURN_FALSE;
-
-  if (!PyCapsule_CheckExact(object)) {
-    PyErr_SetString(PyExc_TypeError, "function takes a PyCapsule as argument");
-    Py_RETURN_FALSE;
-  }
-
-  pointer = PyCapsule_GetPointer(object, "dynamic_graph.Entity");
-  Entity* entity = (Entity*)pointer;
-
-  bool hasSignal = false;
-  try {
-    hasSignal = entity->hasSignal(std::string(name));
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-
-  if (hasSignal)
-    Py_RETURN_TRUE;
-  else
-    Py_RETURN_FALSE;
-}
-
-/**
-   \brief Get a signal by name
-*/
-PyObject* getSignal(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  char* name = NULL;
-  PyObject* object = NULL;
-  void* pointer = NULL;
-
-  if (!PyArg_ParseTuple(args, "Os", &object, &name)) return NULL;
-
-  if (!PyCapsule_CheckExact(object)) {
-    PyErr_SetString(PyExc_TypeError, "function takes a PyCapsule as argument");
-    return NULL;
-  }
-
-  pointer = PyCapsule_GetPointer(object, "dynamic_graph.Entity");
-  Entity* entity = (Entity*)pointer;
-
-  SignalBase<int>* signal = NULL;
-  try {
-    signal = &(entity->getSignal(std::string(name)));
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-
-  // Return the pointer to the signal without destructor since the signal
-  // is not owned by the calling object but by the Entity.
-  return PyCapsule_New((void*)signal, "dynamic_graph.Signal", NULL);
-}
-
-PyObject* listSignals(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  void* pointer = NULL;
-  PyObject* object = NULL;
-
-  if (!PyArg_ParseTuple(args, "O", &object)) return NULL;
-
-  if (!PyCapsule_CheckExact(object)) return NULL;
-
-  pointer = PyCapsule_GetPointer(object, "dynamic_graph.Entity");
-  Entity* entity = (Entity*)pointer;
-
-  try {
-    Entity::SignalMap signalMap = entity->getSignalMap();
-    // Create a tuple of same size as the command map
-    PyObject* result = PyTuple_New(signalMap.size());
-    unsigned int count = 0;
-
-    for (Entity::SignalMap::iterator it = signalMap.begin(); it != signalMap.end(); it++) {
-      SignalBase<int>* signal = it->second;
-      PyObject* pySignal = PyCapsule_New((void*)signal, "dynamic_graph.Signal", NULL);
-      PyTuple_SET_ITEM(result, count, pySignal);
-      count++;
-    }
-    return result;
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-  return NULL;
-}
-
-PyObject* executeCommand(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  PyObject* object = NULL;
-  PyObject* argTuple = NULL;
-  char* commandName = NULL;
-  void* pointer = NULL;
-  if (!PyArg_ParseTuple(args, "OsO", &object, &commandName, &argTuple)) {
-    return NULL;
-  }
-  // Retrieve the entity instance
-  if (!PyCapsule_CheckExact(object)) {
-    PyErr_SetString(PyExc_TypeError, "first argument is not an object");
-    return NULL;
-  }
-  pointer = PyCapsule_GetPointer(object, "dynamic_graph.Entity");
-  Entity* entity = (Entity*)pointer;
-  // Retrieve the argument tuple
-  if (!PyTuple_Check(argTuple)) {
-    PyErr_SetString(PyExc_TypeError, "third argument is not a tuple");
-    return NULL;
-  }
-  Py_ssize_t size = PyTuple_Size(argTuple);
-  std::map<const std::string, Command*> commandMap = entity->getNewStyleCommandMap();
-  if (commandMap.count(std::string(commandName)) != 1) {
-    std::ostringstream oss;
-    oss << "'" << entity->getName() << "' entity has no command '" << commandName << "'.";
-    PyErr_SetString(PyExc_AttributeError, oss.str().c_str());
-    return NULL;
-  }
-  Command* command = commandMap[std::string(commandName)];
-  // Check that tuple size is equal to command number of arguments
-  const std::vector<Value::Type> typeVector = command->valueTypes();
-  if ((unsigned)size != typeVector.size()) {
-    std::stringstream ss;
-    ss << "command takes " << typeVector.size() << " parameters, " << size << " given.";
-    PyErr_SetString(DGPYERROR(m), ss.str().c_str());
-    return NULL;
-  }
-  std::vector<Value> valueVector;
-  for (Py_ssize_t iParam = 0; iParam < size; iParam++) {
-    PyObject* PyValue = PyTuple_GetItem(argTuple, iParam);
-    Value::Type valueType = typeVector[iParam];
-    try {
-      Value value = pythonToValue(PyValue, valueType);
-      valueVector.push_back(value);
-    } catch (const std::exception& exc) {
-      std::stringstream ss;
-      ss << "while parsing argument " << iParam + 1 << ": expecting " << exc.what() << ".";
-      PyErr_SetString(DGPYERROR(m), ss.str().c_str());
-      return NULL;
-    } catch (...) {
-      PyErr_SetString(DGPYERROR(m), "Unknown exception");
-      return NULL;
-    }
-  }
-  command->setParameterValues(valueVector);
-  try {
-    Value result = command->execute();
-    return valueToPython(result);
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-  return NULL;
-}
-
-PyObject* listCommands(PyObject* /*self*/, PyObject* args) {
-  PyObject* object = NULL;
-  if (!PyArg_ParseTuple(args, "O", &object)) {
-    return NULL;
+    obj = dynamicgraph::FactoryStorage::getInstance()->newEntity(std::string(className), std::string(instanceName));
   }
 
-  // Retrieve the entity instance
-  if (!PyCapsule_CheckExact(object)) {
-    PyErr_SetString(PyExc_TypeError, "function takes a PyCapsule as argument");
-    return NULL;
-  }
-  void* pointer = PyCapsule_GetPointer(object, "dynamic_graph.Entity");
-  Entity* entity = (Entity*)pointer;
-  typedef std::map<const std::string, command::Command*> CommandMap;
-  CommandMap map = entity->getNewStyleCommandMap();
-  Py_ssize_t nbCommands = (Py_ssize_t)map.size();
-  // Create a tuple of same size as the command map
-  PyObject* result = PyTuple_New(nbCommands);
-  unsigned int count = 0;
-  for (CommandMap::iterator it = map.begin(); it != map.end(); it++) {
-    std::string commandName = it->first;
-    PyObject* pyName = Py_BuildValue("s", commandName.c_str());
-    PyTuple_SET_ITEM(result, count, pyName);
-    count++;
-  }
-  return result;
+  return obj;
 }
-PyObject* getCommandDocstring(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  PyObject* object = NULL;
-  char* commandName;
-  if (!PyArg_ParseTuple(args, "Os", &object, &commandName)) {
-    return NULL;
-  }
-
-  // Retrieve the entity instance
-  if (!PyCapsule_CheckExact(object)) {
-    PyErr_SetString(DGPYERROR(m), "first argument is not an object");
-    return NULL;
-  }
-  void* pointer = PyCapsule_GetPointer(object, "dynamic_graph.Entity");
-  Entity* entity = (Entity*)pointer;
-  typedef std::map<const std::string, command::Command*> commandMap_t;
-  typedef std::map<const std::string, command::Command*>::iterator iterator_t;
-  commandMap_t map = entity->getNewStyleCommandMap();
-  command::Command* command = NULL;
-  iterator_t it = map.find(commandName);
-  if (it == map.end()) {
-    std::ostringstream oss;
-    oss << "'" << entity->getName() << "' entity has no command '" << commandName << "'.";
-    PyErr_SetString(PyExc_AttributeError, oss.str().c_str());
-    return NULL;
-  }
-  command = it->second;
-  std::string docstring = command->getDocstring();
-  return Py_BuildValue("s", docstring.c_str());
-}
-
-PyObject* getDocString(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  PyObject* object = NULL;
-  if (!PyArg_ParseTuple(args, "O", &object)) {
-    return NULL;
-  }
-
-  // Retrieve the entity instance
-  if (!PyCapsule_CheckExact(object)) {
-    PyErr_SetString(DGPYERROR(m), "first argument is not an object");
-    return NULL;
-  }
-  void* pointer = PyCapsule_GetPointer(object, "dynamic_graph.Entity");
-  Entity* entity = (Entity*)pointer;
-  try {
-    return Py_BuildValue("s", entity->getDocString().c_str());
-  } catch (const std::exception& exc) {
-    PyErr_SetString(DGPYERROR(m), exc.what());
-    return NULL;
-  } catch (...) {
-    PyErr_SetString(DGPYERROR(m), "Unknown exception");
-    return NULL;
-  }
-  return NULL;
-}
-
-PyObject* display(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  /* Retrieve the entity instance. */
-  PyObject* object = NULL;
-  if (!PyArg_ParseTuple(args, "O", &object) || (!PyCapsule_CheckExact(object))) {
-    PyErr_SetString(DGPYERROR(m), "first argument is not an object");
-    return NULL;
-  }
-  void* pointer = PyCapsule_GetPointer(object, "dynamic_graph.Entity");
-  Entity* entity = (Entity*)pointer;
-
-  /* Run the display function. */
-  std::ostringstream oss;
-  entity->display(oss);
-
-  /* Return the resulting string. */
-  return Py_BuildValue("s", oss.str().c_str());
-}
-
-/**
-   \brief Set verbosity Level
-*/
-PyObject* setLoggerVerbosityLevel(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  PyObject* object = NULL;
-  PyObject* objectVerbosityLevel = NULL;
-  if (!PyArg_ParseTuple(args, "OO", &object, &objectVerbosityLevel)) return NULL;
-
-  // Retrieve the entity instance
-  if (!PyCapsule_CheckExact(object)) {
-    PyErr_SetString(PyExc_TypeError, "First argument should be an object");
-    return NULL;
-  }
-
-  void* pointer = PyCapsule_GetPointer(object, "dynamic_graph.Entity");
-  Entity* entity = (Entity*)pointer;
-
-  // Retrieve object verbosity level
-  PyObject* valueOfVerbosityLevel = PyObject_GetAttrString(objectVerbosityLevel, "value");
-  long verbosityLevel = PyLong_AsLong(valueOfVerbosityLevel);
-
-  try {
-    switch (verbosityLevel) {
-      case 8:
-        entity->setLoggerVerbosityLevel(VERBOSITY_ALL);
-        break;
-      case 4:
-        entity->setLoggerVerbosityLevel(VERBOSITY_INFO_WARNING_ERROR);
-        break;
-      case 2:
-        entity->setLoggerVerbosityLevel(VERBOSITY_WARNING_ERROR);
-        break;
-      case 1:
-        entity->setLoggerVerbosityLevel(VERBOSITY_ERROR);
-        break;
-      case 0:
-        entity->setLoggerVerbosityLevel(VERBOSITY_NONE);
-        break;
-      default:
-        entity->setLoggerVerbosityLevel(VERBOSITY_NONE);
-        break;
-    }
-  } catch (const std::exception& exc) {
-    PyErr_SetString(DGPYERROR(m), exc.what());
-    return NULL;
-  } catch (const char* s) {
-    PyErr_SetString(DGPYERROR(m), s);
-    return NULL;
-  } catch (...) {
-    PyErr_SetString(DGPYERROR(m), "Unknown exception");
-    return NULL;
-  }
-
-  return Py_BuildValue("");
-}
-
-/**
-   \brief Get verbosity Level
-*/
-PyObject* getLoggerVerbosityLevel(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  PyObject* object = NULL;
-  if (!PyArg_ParseTuple(args, "O", &object)) return NULL;
-
-  // Retrieve the entity instance
-  if (!PyCapsule_CheckExact(object)) {
-    PyErr_SetString(PyExc_TypeError, "first argument is not an object");
-    return NULL;
-  }
-
-  void* pointer = PyCapsule_GetPointer(object, "dynamic_graph.Entity");
-  Entity* entity = (Entity*)pointer;
-
-  LoggerVerbosity alv;
-  try {
-    alv = entity->getLoggerVerbosityLevel();
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-
-  int ares = (int)alv;
-  return Py_BuildValue("i", ares);
-}
-
-/**
-   \brief Get stream print period
-*/
-PyObject* getStreamPrintPeriod(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  PyObject* object = NULL;
-  if (!PyArg_ParseTuple(args, "O", &object)) return NULL;
-
-  // Retrieve the entity instance
-  if (!PyCapsule_CheckExact(object)) {
-    PyErr_SetString(PyExc_TypeError, "first argument is not an object");
-    return NULL;
-  }
-
-  void* pointer = PyCapsule_GetPointer(object, "dynamic_graph.Entity");
-  Entity* entity = (Entity*)pointer;
-
-  double r;
-  try {
-    r = entity->getStreamPrintPeriod();
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-
-  return Py_BuildValue("d", r);
-}
-
-/**
-   \brief Set print period
-*/
-PyObject* setStreamPrintPeriod(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  PyObject* object = NULL;
-  double streamPrintPeriod = 0;
-  if (!PyArg_ParseTuple(args, "Od", &object, &streamPrintPeriod)) return NULL;
-
-  // Retrieve the entity instance
-  if (!PyCapsule_CheckExact(object)) {
-    PyErr_SetString(PyExc_TypeError, "First argument should be an object");
-    return NULL;
-  }
-
-  void* pointer = PyCapsule_GetPointer(object, "dynamic_graph.Entity");
-  Entity* entity = (Entity*)pointer;
-
-  try {
-    entity->setStreamPrintPeriod(streamPrintPeriod);
-
-  } catch (const std::exception& exc) {
-    PyErr_SetString(DGPYERROR(m), exc.what());
-    return NULL;
-  } catch (const char* s) {
-    PyErr_SetString(DGPYERROR(m), s);
-    return NULL;
-  } catch (...) {
-    PyErr_SetString(DGPYERROR(m), "Unknown exception");
-    return NULL;
-  }
-
-  return Py_BuildValue("");
-}
-
-/**
-   \brief Get stream print period
-*/
-PyObject* getTimeSample(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  PyObject* object = NULL;
-  if (!PyArg_ParseTuple(args, "O", &object)) return NULL;
-
-  // Retrieve the entity instance
-  if (!PyCapsule_CheckExact(object)) {
-    PyErr_SetString(PyExc_TypeError, "first argument is not an object");
-    return NULL;
-  }
-
-  void* pointer = PyCapsule_GetPointer(object, "dynamic_graph.Entity");
-  Entity* entity = (Entity*)pointer;
-
-  double r;
-  try {
-    r = entity->getTimeSample();
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-
-  return Py_BuildValue("d", r);
-}
-
-/**
-   \brief Set time sample
-*/
-PyObject* setTimeSample(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  PyObject* object = NULL;
-  double timeSample;
-  if (!PyArg_ParseTuple(args, "Od", &object, &timeSample)) return NULL;
-
-  // Retrieve the entity instance
-  if (!PyCapsule_CheckExact(object)) {
-    PyErr_SetString(PyExc_TypeError, "First argument should be an object");
-    return NULL;
-  }
-
-  void* pointer = PyCapsule_GetPointer(object, "dynamic_graph.Entity");
-  Entity* entity = (Entity*)pointer;
-
-  try {
-    entity->setTimeSample(timeSample);
-
-  } catch (const std::exception& exc) {
-    PyErr_SetString(DGPYERROR(m), exc.what());
-    return NULL;
-  } catch (const char* s) {
-    PyErr_SetString(DGPYERROR(m), s);
-    return NULL;
-  } catch (...) {
-    PyErr_SetString(DGPYERROR(m), "Unknown exception");
-    return NULL;
-  }
 
-  return Py_BuildValue("");
+bp::object executeCmd(bp::tuple args, bp::dict) {
+  Command& command = bp::template extract<Command&>(args[0]);
+  if (bp::len(args) != int(command.valueTypes().size() + 1))
+    // TODO Put expected and given number of args
+    throw std::out_of_range("Wrong number of arguments");
+  std::vector<Value> values;
+  values.reserve(command.valueTypes().size());
+  for (int i = 1; i < bp::len(args); ++i) values.push_back(convert::toValue(args[i], command.valueTypes()[i - 1]));
+  command.setParameterValues(values);
+  return convert::fromValue(command.execute());
 }
 
 }  // namespace entity
diff --git a/src/dynamic_graph/entity.py b/src/dynamic_graph/entity.py
index c59cfc168ca0b50ba230bafe9fc5f7c758576560..6374dc4e2be1221b51233c5cf2c8e63b4a32d718 100644
--- a/src/dynamic_graph/entity.py
+++ b/src/dynamic_graph/entity.py
@@ -1,306 +1,9 @@
-"""
-  Copyright (C) 2010 CNRS
+# Copyright (C) 2020 CNRS
+#
+# Author: Florent Lamiraux, Nicolas Mansard
 
-  Author: Florent Lamiraux, Nicolas Mansard
-"""
 from __future__ import print_function
 
-import types
-from enum import Enum
-
-from . import signal_base, wrap
-from .attrpath import setattrpath
-
-if 'display' not in globals().keys():
-
-    def display(s):
-        print(s)
-
-
-# --- FACTORY ------------------------------------------------------------------
-
-
-class PyEntityFactoryClass(type):
-    """
-    The class build dynamically a new class type, and return the reference
-    on the class-type object. The class type is not added to any context.
-    """
-    def __new__(factory, className, bases=(), dict={}):
-        if len(bases) == 0:
-            # Initialize a basic Entity class
-            EntityClass = type.__new__(factory, className, (Entity, ), dict)
-            EntityClass.className = className
-            EntityClass.__init__ = Entity.initEntity
-        else:
-            # Initialize a heritated class
-            EntityClass = type.__new__(factory, className, bases, dict)
-            for c in bases:
-                if issubclass(c, Entity):
-                    EntityClass.className = c.className
-                    break
-        EntityClass.commandCreated = False
-        return EntityClass
-
-
-def PyEntityFactory(className, context):
-    """
-    Build a new class type by calling the factory, and add it
-    to the given context.
-    """
-    EntityClass = PyEntityFactoryClass(className)
-    context[className] = EntityClass
-    return EntityClass
-
-
-def updateEntityClasses(dictionary):
-    """
-    For all c++entity types that are not in the pyentity class list
-    (entityClassNameList) run the factory and store the new type in the given
-    context (dictionary).
-    """
-    cxx_entityList = wrap.factory_get_entity_class_list()
-    for e in filter(lambda x: x not in Entity.entityClassNameList, cxx_entityList):
-        # Store new class in dictionary with class name
-        PyEntityFactory(e, dictionary)
-        # Store class name in local list
-        Entity.entityClassNameList.append(e)
-
-
-# --- ENTITY -------------------------------------------------------------------
-
-
-class VerbosityLevel(Enum):
-    """
-    Enum class for setVerbosityLevel
-    """
-    VERBOSITY_ALL = 8
-    VERBOSITY_INFO_WARNING_ERROR = 4
-    VERBOSITY_WARNING_ERROR = 2
-    VERBOSITY_ERROR = 1
-    VERBOSITY_NONE = 0
-
-
-class Entity(object):
-    """
-    This class binds dynamicgraph::Entity C++ class
-    """
-
-    obj = None
-    """
-    Store list of entities created via python
-    """
-    entities = dict()
-
-    def __init__(self, className, instanceName):
-        """
-        Constructor: if not called by a child class, create and store a pointer
-        to a C++ Entity object.
-        """
-        object.__setattr__(self, 'obj', wrap.create_entity(className, instanceName))
-        Entity.entities[instanceName] = self
-
-    @staticmethod
-    def initEntity(self, name):
-        """
-        Common constructor of specialized Entity classes. This function is bound
-        by the factory to each new class derivated from the Entity class as the
-        constructor of the new class.
-        """
-        Entity.__init__(self, self.className, name)
-        if not self.__class__.commandCreated:
-            self.boundClassCommands()
-            self.__class__.__doc__ = wrap.entity_get_docstring(self.obj)
-            self.__class__.commandCreated = True
-
-    @property
-    def name(self):
-        return wrap.entity_get_name(self.obj)
-
-    @property
-    def className(self):
-        return wrap.entity_get_class_name(self.obj)
-
-    def __str__(self):
-        return wrap.display_entity(self.obj)
-
-    def signal(self, name):
-        """
-        Get a signal of the entity from signal name
-        """
-        signalPt = wrap.entity_get_signal(self.obj, name)
-        return signal_base.SignalBase(name="", obj=signalPt)
-
-    def hasSignal(self, name):
-        """
-        Indicates if a signal with the given name exists in the entity
-        """
-        return wrap.entity_has_signal(self.obj, name)
-
-    def displaySignals(self):
-        """
-        Print the list of signals into standard output: temporary.
-        """
-        signals = list(self.signals())
-        if len(signals) == 0:
-            display("--- <" + self.name + "> has no signal")
-        else:
-            display("--- <" + self.name + "> signal list: ")
-            for s in signals[:-1]:
-                display("    |-- <" + str(s))
-            display("    `-- <" + str(signals[-1]))
-
-    def signals(self):
-        """
-        Return the list of signals
-        """
-        sl = wrap.entity_list_signals(self.obj)
-        return map(lambda pyObj: signal_base.SignalBase(obj=pyObj), sl)
-
-    def commands(self):
-        """
-        Return the list of commands.
-        """
-        return wrap.entity_list_commands(self.obj)
-
-    def globalHelp(self):
-        """
-        Print a short description of each command.
-        """
-        if self.__doc__:
-            print(self.__doc__)
-        print("List of commands:")
-        print("-----------------")
-        for cstr in self.commands():
-            ctitle = cstr + ':'
-            for i in range(len(cstr), 15):
-                ctitle += ' '
-            for docstr in wrap.entity_get_command_docstring(self.obj, cstr).split('\n'):
-                if (len(docstr) > 0) and (not docstr.isspace()):
-                    display(ctitle + "\t" + docstr)
-                    break
-
-    def help(self, comm=None):
-        """
-        With no arg, print the global help. With arg the name of
-        a specific command, print the help associated to the command.
-        """
-        if comm is None:
-            self.globalHelp()
-        else:
-            display(comm + ":\n" + wrap.entity_get_command_docstring(self.obj, comm))
-
-    def __getattr__(self, name):
-        try:
-            return self.signal(name)
-        except Exception:
-            try:
-                object.__getattr__(self, name)
-            except AttributeError:
-                raise AttributeError("'%s' entity has no attribute %s\n" % (self.name, name) +
-                                     '  entity attributes are usually either\n' + '    - commands,\n' +
-                                     '    - signals or,\n' + '    - user defined attributes')
-
-    def __setattr__(self, name, value):
-        if name in map(lambda s: s.getName().split(':')[-1], self.signals()):
-            raise NameError(name + " already designates a signal. "
-                            "It is not advised to set a new attribute of the same name.")
-        object.__setattr__(self, name, value)
-
-    # --- COMMANDS BINDER -----------------------------------------------------
-    # List of all the entity classes from the c++ factory, that have been bound
-    # bind the py factory.
-    entityClassNameList = []
-
-    # This function dynamically create the function object that runs the command.
-    @staticmethod
-    def createCommandBind(name, docstring):
-        def commandBind(self, *arg):
-            return wrap.entity_execute_command(self.obj, name, arg)
-
-        commandBind.__doc__ = docstring
-        return commandBind
-
-    def boundClassCommands(self):
-        """
-        This static function has to be called from a class heritating from Entity.
-        It should be called only once. It parses the list of commands obtained from
-        c++, and bind each of them to a python class method.
-        """
-        # Get list of commands of the Entity object
-        commands = wrap.entity_list_commands(self.obj)
-        # for each command, add a method with the name of the command
-        for cmdstr in commands:
-            docstr = wrap.entity_get_command_docstring(self.obj, cmdstr)
-            cmdpy = Entity.createCommandBind(cmdstr, docstr)
-            setattrpath(self.__class__, cmdstr, cmdpy)
-
-    def boundNewCommand(self, cmdName):
-        """
-        At construction, all existing commands are bound directly in the class.
-        This method enables to bound new commands dynamically. These new bounds
-        are not made with the class, but directly with the object instance.
-        """
-        if (cmdName in self.__dict__) | (cmdName in self.__class__.__dict__):
-            print("Warning: command ", cmdName, " will overwrite an object attribute.")
-        docstring = wrap.entity_get_command_docstring(self.obj, cmdName)
-        cmd = Entity.createCommandBind(cmdName, docstring)
-        # Limitation (todo): does not handle for path attribute name (see setattrpath).
-        setattr(self, cmdName, types.MethodType(cmd, self))
-
-    def boundAllNewCommands(self):
-        """
-        For all commands that are not attribute of the object instance nor of the
-        class, a new attribute of the instance is created to bound the command.
-        """
-        cmdList = wrap.entity_list_commands(self.obj)
-        cmdList = filter(lambda x: x not in self.__dict__, cmdList)
-        cmdList = filter(lambda x: x not in self.__class__.__dict__, cmdList)
-        for cmd in cmdList:
-            self.boundNewCommand(cmd)
-
-    def setLoggerVerbosityLevel(self, verbosity):
-        """
-        Specify for the entity the verbosity level.
-        - param verbosity should be one of the attribute of the enum
-                dynamic_graph.entity.VerbosityLevel
-        """
-        return wrap.entity_set_logger_verbosity(self.obj, verbosity)
-
-    def getLoggerVerbosityLevel(self):
-        """
-        Returns the entity's verbosity level (as a dynamic_graph.entity.VerbosityLevel)
-        """
-        r = wrap.entity_get_logger_verbosity(self.obj)
-        if r == 8:
-            return VerbosityLevel.VERBOSITY_ALL
-        elif r == 4:
-            return VerbosityLevel.VERBOSITY_INFO_WARNING_ERROR
-        elif r == 2:
-            return VerbosityLevel.VERBOSITY_WARNING_ERROR
-        elif r == 1:
-            return VerbosityLevel.VERBOSITY_ERROR
-        return VerbosityLevel.VERBOSITY_NONE
-
-    def setTimeSample(self, timeSample):
-        """
-        Specify for the entity the time at which call is counted.
-        """
-        return wrap.entity_set_time_sample(self.obj, timeSample)
-
-    def getTimeSample(self):
-        """
-        Returns for the entity the time at which call is counted.
-        """
-        return wrap.entity_get_time_sample(self.obj)
-
-    def setStreamPrintPeriod(self, streamPrintPeriod):
-        """
-        Specify for the entity the period at which debugging information is printed
-        """
-        return wrap.entity_set_stream_print_period(self.obj, streamPrintPeriod)
-
-    def getStreamPrintPeriod(self):
-        """
-        Returns for the entity the period at which debugging information is printed
-        """
-        return wrap.entity_get_stream_print_period(self.obj)
+# for backward compat
+from .wrap import LoggerVerbosity as VerbosityLevel
+from .wrap import Entity
diff --git a/src/dynamic_graph/exception-python.cc b/src/dynamic_graph/exception-python.cc
deleted file mode 100644
index 490cda381f47d4c1b16bb98f4674eb7bbfc170a6..0000000000000000000000000000000000000000
--- a/src/dynamic_graph/exception-python.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2010,
- * François Bleibel,
- * Olivier Stasse,
- *
- * CNRS/AIST
- *
- */
-
-#include "dynamic-graph/python/exception-python.hh"
-#include <dynamic-graph/debug.h>
-#include <stdarg.h>
-#include <cstdio>
-
-namespace dynamicgraph {
-namespace python {
-
-/* --------------------------------------------------------------------- */
-/* --- CLASS ----------------------------------------------------------- */
-/* --------------------------------------------------------------------- */
-
-const std::string ExceptionPython::EXCEPTION_NAME = "Python";
-
-ExceptionPython::ExceptionPython(const ExceptionPython::ErrorCodeEnum& errcode, const std::string& msg)
-    : ExceptionAbstract(errcode, msg) {
-  dgDEBUGF(15, "Created with message <%s>.", msg.c_str());
-  dgDEBUG(1) << "Created with message <%s>." << msg << std::endl;
-}
-
-ExceptionPython::ExceptionPython(const ExceptionPython::ErrorCodeEnum& errcode, const std::string& msg,
-                                 const char* format, ...)
-    : ExceptionAbstract(errcode, msg) {
-  va_list args;
-  va_start(args, format);
-
-  const unsigned int SIZE = 256;
-  char buffer[SIZE];
-  vsnprintf(buffer, SIZE, format, args);
-
-  dgDEBUG(15) << "Created "
-              << " with message <" << msg << "> and buffer <" << buffer << ">. " << std::endl;
-
-  message += buffer;
-
-  va_end(args);
-
-  dgDEBUG(1) << "Throw exception " << EXCEPTION_NAME << "[#" << errcode << "]: "
-             << "<" << message << ">." << std::endl;
-}
-
-}  // namespace python
-}  // namespace dynamicgraph
-
-/*
- * Local variables:
- * c-basic-offset: 2
- * End:
- */
diff --git a/src/dynamic_graph/factory-py.cc b/src/dynamic_graph/factory-py.cc
index d6ce919b148fdc8527a702134604a8303bba793d..8fbdbcafb71110df48d4b8439bec2cd65484bac8 100644
--- a/src/dynamic_graph/factory-py.cc
+++ b/src/dynamic_graph/factory-py.cc
@@ -16,22 +16,10 @@ namespace factory {
 /**
    \brief Get name of entity
 */
-PyObject* getEntityClassList(PyObject* /*self*/, PyObject* args) {
-  if (!PyArg_ParseTuple(args, "")) return NULL;
-
+bp::tuple getEntityClassList() {
   std::vector<std::string> classNames;
   dynamicgraph::FactoryStorage::getInstance()->listEntities(classNames);
-
-  Py_ssize_t classNumber = classNames.size();
-  // Build a tuple object
-  PyObject* classTuple = PyTuple_New(classNumber);
-
-  for (Py_ssize_t iEntity = 0; iEntity < (Py_ssize_t)classNames.size(); ++iEntity) {
-    PyObject* className = Py_BuildValue("s", classNames[iEntity].c_str());
-    PyTuple_SetItem(classTuple, iEntity, className);
-  }
-
-  return Py_BuildValue("O", classTuple);
+  return to_py_tuple(classNames.begin(), classNames.end());
 }
 
 }  // namespace factory
diff --git a/src/dynamic_graph/pool-py.cc b/src/dynamic_graph/pool-py.cc
index 68f0ce698f3f3c9811d5b40b4642a4dcea913e8b..2c1659deed9e31aa6f706f7c90c372ab13ac20b2 100644
--- a/src/dynamic_graph/pool-py.cc
+++ b/src/dynamic_graph/pool-py.cc
@@ -4,67 +4,27 @@
 #include <dynamic-graph/entity.h>
 #include <vector>
 
-#include "dynamic-graph/python/exception.hh"
 #include "dynamic-graph/python/dynamic-graph-py.hh"
 
 namespace dynamicgraph {
 namespace python {
 
-#if PY_MAJOR_VERSION == 2
-extern PyObject* dgpyError;
-#endif
-
 namespace pool {
 
-PyObject* writeGraph(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  char* filename;
-  if (!PyArg_ParseTuple(args, "s", &filename)) return NULL;
-  try {
-    PoolStorage::getInstance()->writeGraph(filename);
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-  return Py_BuildValue("");
-}
+void writeGraph(const char* filename) { PoolStorage::getInstance()->writeGraph(filename); }
+
+const std::map<std::string, Entity*>* getEntityMap() { return &PoolStorage::getInstance()->getEntityMap(); }
 
 /**
    \brief Get list of entities
 */
-PyObject* getEntityList(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  if (!PyArg_ParseTuple(args, "")) return NULL;
-
+bp::list getEntityList() {
   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();
+  bp::list res;
+  const PoolStorage::Entities& listOfEntities = PoolStorage::getInstance()->getEntityMap();
 
-      PyObject* className = Py_BuildValue("s", aname.c_str());
-      PyTuple_SetItem(classTuple, iEntity, className);
-      iEntity++;
-    }
-    return Py_BuildValue("O", classTuple);
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-  return NULL;
+  for (const auto& el : listOfEntities) res.append(el.second->getName());
+  return res;
 }
 
 }  // namespace pool
diff --git a/src/dynamic_graph/signal-base-py.cc b/src/dynamic_graph/signal-base-py.cc
index 743cb1f7aba7274142510503df6b4ccdc2049fc3..c839742df2b7f7c0c4159766c5415dbd5eca0b21 100644
--- a/src/dynamic_graph/signal-base-py.cc
+++ b/src/dynamic_graph/signal-base-py.cc
@@ -3,51 +3,119 @@
 #include <iostream>
 #include <sstream>
 
+#include <boost/python.hpp>
+
+#include "dynamic-graph/python/signal.hh"
+
 #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/signal-time-dependent.h>
 #include <dynamic-graph/linear-algebra.h>
-#include <dynamic-graph/pool.h>
-#include <dynamic-graph/factory.h>
+#include <dynamic-graph/value.h>
 
 #include "dynamic-graph/python/dynamic-graph-py.hh"
-#include "dynamic-graph/python/convert-dg-to-py.hh"
-#include "dynamic-graph/python/exception.hh"
 #include "dynamic-graph/python/signal-wrapper.hh"
 
 using dynamicgraph::SignalBase;
 
+namespace bp = boost::python;
+
 namespace dynamicgraph {
 namespace python {
 
-#if PY_MAJOR_VERSION == 2
-extern PyObject* dgpyError;
-#endif
-
-using namespace convert;
-
-namespace signalBase {
+typedef int time_type;
+
+typedef Eigen::AngleAxis<double> VectorUTheta;
+typedef Eigen::Quaternion<double> Quaternion;
+
+typedef Eigen::VectorXd Vector;
+typedef Eigen::Vector3d Vector3;
+typedef Eigen::Matrix<double, 7, 1> Vector7;
+
+typedef Eigen::MatrixXd Matrix;
+typedef Eigen::Matrix<double, 3, 3> MatrixRotation;
+typedef Eigen::Matrix<double, 4, 4> Matrix4;
+typedef Eigen::Transform<double, 3, Eigen::Affine> MatrixHomogeneous;
+typedef Eigen::Matrix<double, 6, 6> MatrixTwist;
+
+template <typename Time>
+void exposeSignalBase(const char* name) {
+  typedef SignalBase<Time> S_t;
+  bp::class_<S_t, boost::noncopyable>(name, bp::no_init)
+      .add_property("time", bp::make_function(&S_t::getTime, bp::return_value_policy<bp::copy_const_reference>()),
+                    &S_t::setTime)
+      .add_property("name", bp::make_function(&S_t::getName, bp::return_value_policy<bp::copy_const_reference>()))
+
+      .def("getName", &S_t::getName, bp::return_value_policy<bp::copy_const_reference>())
+      .def("getClassName",
+           +[](const S_t& s) -> std::string {
+             std::string ret;
+             s.getClassName(ret);
+             return ret;
+           })
+
+      .def("plug", &S_t::plug, "Plug the signal to another signal")
+      .def("unplug", &S_t::unplug, "Unplug the signal")
+      .def("isPlugged", &S_t::isPlugged, "Whether the signal is plugged")
+      .def("getPlugged", &S_t::getPluged, bp::return_value_policy<bp::reference_existing_object>(),
+           "To which signal the signal is plugged")
+
+      .def("recompute", &S_t::recompute, "Recompute the signal at given time")
+
+      .def("__str__",
+           +[](const S_t& s) -> std::string {
+             std::ostringstream oss;
+             s.display(oss);
+             return oss.str();
+           })
+      .def("displayDependencies",
+           +[](const S_t& s, int time) -> std::string {
+             std::ostringstream oss;
+             s.displayDependencies(oss, time);
+             return oss.str();
+           },
+           "Print the signal dependencies in a string");
+}
+
+template <>
+auto exposeSignal<MatrixHomogeneous, time_type>(const std::string& name) {
+  typedef Signal<MatrixHomogeneous, time_type> S_t;
+  bp::class_<S_t, bp::bases<SignalBase<time_type> >, boost::noncopyable> obj(name.c_str(), bp::init<std::string>());
+  obj.add_property("value", +[](const S_t& signal) -> Matrix4 { return signal.accessCopy().matrix(); },
+                   +[](S_t& signal, const Matrix4& v) {
+                     // TODO it isn't hard to support pinocchio::SE3 type here.
+                     // However, this adds a dependency to pinocchio.
+                     signal.setConstant(MatrixHomogeneous(v));
+                   },
+                   "the signal value.");
+  return obj;
+}
 
-static void destroy(PyObject* self);
+void exposeSignals() {
+  exposeSignalBase<time_type>("SignalBase");
 
-/**
-   \brief Create an instance of SignalBase
-*/
-PyObject* create(PyObject* /*self*/, PyObject* args) {
-  char* name = NULL;
+  exposeSignalsOfType<bool, time_type>("Bool");
+  exposeSignalsOfType<int, time_type>("Int");
+  exposeSignalsOfType<double, time_type>("Double");
 
-  if (!PyArg_ParseTuple(args, "s", &name)) return NULL;
+  exposeSignalsOfType<Vector, time_type>("Vector");
+  exposeSignalsOfType<Vector3, time_type>("Vector3");
+  exposeSignalsOfType<Vector7, time_type>("Vector7");
 
-  SignalBase<int>* obj = NULL;
-  obj = new SignalBase<int>(std::string(name));
+  exposeSignalsOfType<Matrix, time_type>("Matrix");
+  exposeSignalsOfType<MatrixRotation, time_type>("MatrixRotation");
+  exposeSignalsOfType<MatrixHomogeneous, time_type>("MatrixHomogeneous");
+  exposeSignalsOfType<MatrixTwist, time_type>("MatrixTwist");
 
-  // Return the pointer
-  return PyCapsule_New((void*)obj, "dynamic_graph.Signal", destroy);
+  exposeSignalsOfType<Quaternion, time_type>("Quaternion");
+  exposeSignalsOfType<VectorUTheta, time_type>("VectorUTheta");
 }
 
+namespace signalBase {
+
 template <class T>
-SignalWrapper<T, int>* createSignalWrapperTpl(const char* name, PyObject* o, std::string& error) {
+SignalWrapper<T, int>* createSignalWrapperTpl(const char* name, bp::object o, std::string& error) {
   typedef SignalWrapper<T, int> SignalWrapper_t;
   if (!SignalWrapper_t::checkCallable(o, error)) {
     return NULL;
@@ -58,27 +126,7 @@ SignalWrapper<T, int>* createSignalWrapperTpl(const char* name, PyObject* o, std
 }
 
 PythonSignalContainer* getPythonSignalContainer() {
-  const std::string instanceName = "python_signals";
-  const std::string className = "PythonSignalContainer";
-  Entity* obj;
-#if PY_MAJOR_VERSION >= 3
-  PyObject* m = PyState_FindModule(&dynamicgraph::python::dynamicGraphModuleDef);
-#endif
-  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(m), msg.c_str());
-      return NULL;
-    }
-  } else {
-    try {
-      obj = FactoryStorage::getInstance()->newEntity(std::string(className), std::string(instanceName));
-    }
-    CATCH_ALL_EXCEPTIONS(m);
-  }
+  Entity* obj = entity::create("PythonSignalContainer", "python_signals");
   return dynamic_cast<PythonSignalContainer*>(obj);
 }
 
@@ -90,22 +138,10 @@ PythonSignalContainer* getPythonSignalContainer() {
 /**
    \brief Create an instance of SignalWrapper
 */
-PyObject* createSignalWrapper(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
+SignalBase<int>* createSignalWrapper(const char* name, const char* type, bp::object object) {
   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)
@@ -121,380 +157,14 @@ PyObject* createSignalWrapper(
     error = "Type not understood";
   }
 
-  if (obj == NULL) {
-    PyErr_SetString(DGPYERROR(m), error.c_str());
-    return NULL;
-  }
+  if (obj == NULL) throw std::runtime_error(error);
   // Register signal into the python signal container
   psc->signalRegistration(*obj);
 
   // Return the pointer
-  return PyCapsule_New((void*)obj, "dynamic_graph.SignalWrapper", destroy);
-}
-
-/**
-   \brief Destroy an instance of InvertedPendulum
-*/
-static void destroy(PyObject* 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 (!PyCapsule_CheckExact(object)) return NULL;
-
-  pointer = PyCapsule_GetPointer(object, "dynamic_graph.Signal");
-  SignalBase<int>* obj = (SignalBase<int>*)pointer;
-
-  int time = obj->getTime();
-  return Py_BuildValue("i", time);
-}
-
-PyObject* setTime(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  void* pointer = NULL;
-  PyObject* object = NULL;
-  int time;
-  if (!PyArg_ParseTuple(args, "Oi", &object, &time)) return NULL;
-  if (!PyCapsule_CheckExact(object)) {
-    PyErr_SetString(DGPYERROR(m), "object should be a C object");
-    return NULL;
-  }
-
-  pointer = PyCapsule_GetPointer(object, "dynamic_graph.Signal");
-  SignalBase<int>* obj = (SignalBase<int>*)pointer;
-
-  obj->setTime(time);
-  return Py_BuildValue("");
-}
-
-PyObject* display(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  void* pointer = NULL;
-  PyObject* object = NULL;
-  if (!PyArg_ParseTuple(args, "O", &object)) return NULL;
-  if (!PyCapsule_CheckExact(object)) return NULL;
-
-  pointer = PyCapsule_GetPointer(object, "dynamic_graph.Signal");
-  SignalBase<int>* obj = (SignalBase<int>*)pointer;
-
-  std::ostringstream oss;
-  try {
-    obj->display(oss);
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-
-  return Py_BuildValue("s", oss.str().c_str());
-}
-
-PyObject* displayDependencies(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  void* pointer = NULL;
-  PyObject* object = NULL;
-  int time;
-  if (!PyArg_ParseTuple(args, "OI", &object, &time)) return NULL;
-  if (!PyCapsule_CheckExact(object)) return NULL;
-
-  pointer = PyCapsule_GetPointer(object, "dynamic_graph.Signal");
-  SignalBase<int>* obj = (SignalBase<int>*)pointer;
-
-  std::ostringstream oss;
-  try {
-    obj->displayDependencies(oss, time);
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-  return Py_BuildValue("s", oss.str().c_str());
-}
-
-PyObject* getValue(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  void* pointer = NULL;
-  PyObject* object = NULL;
-  if (!PyArg_ParseTuple(args, "O", &object)) return NULL;
-  if (!PyCapsule_CheckExact(object)) return NULL;
-
-  pointer = PyCapsule_GetPointer(object, "dynamic_graph.Signal");
-  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(m);
-
-  /* Non specific signal: use a generic way. */
-  std::ostringstream value;
-  try {
-    signal->get(value);
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-
-  std::string valueString = value.str();
-  return Py_BuildValue("s", valueString.c_str());
-}
-
-PyObject* getName(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  void* pointer = NULL;
-  PyObject* object = NULL;
-  if (!PyArg_ParseTuple(args, "O", &object)) return NULL;
-  if (!PyCapsule_CheckExact(object)) return NULL;
-
-  pointer = PyCapsule_GetPointer(object, "dynamic_graph.Signal");
-  SignalBase<int>* signal = (SignalBase<int>*)pointer;
-
-  std::string name;
-  try {
-    name = signal->getName();
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-
-  return Py_BuildValue("s", name.c_str());
-}
-
-PyObject* getClassName(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  void* pointer = NULL;
-  PyObject* object = NULL;
-  if (!PyArg_ParseTuple(args, "O", &object)) return NULL;
-  if (!PyCapsule_CheckExact(object)) return NULL;
-
-  pointer = PyCapsule_GetPointer(object, "dynamic_graph.Signal");
-  SignalBase<int>* signal = (SignalBase<int>*)pointer;
-
-  std::string name;
-  try {
-    signal->getClassName(name);
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-
-  return Py_BuildValue("s", name.c_str());
-}
-
-PyObject* setValue(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  void* pointer = NULL;
-  PyObject* object = NULL;
-  char* valueString = NULL;
-
-  if (!PyArg_ParseTuple(args, "Os", &object, &valueString)) return NULL;
-  if (!PyCapsule_CheckExact(object)) return NULL;
-
-  pointer = PyCapsule_GetPointer(object, "dynamic_graph.Signal");
-  SignalBase<int>* signal = (SignalBase<int>*)pointer;
-  std::ostringstream os;
-  os << valueString;
-  std::istringstream value(os.str());
-
-  try {
-    signal->set(value);
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-  return Py_BuildValue("");
-}
-
-PyObject* recompute(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  void* pointer = NULL;
-  PyObject* object = NULL;
-  unsigned int time;
-  if (!PyArg_ParseTuple(args, "OI", &object, &time)) return NULL;
-  if (!PyCapsule_CheckExact(object)) return NULL;
-
-  pointer = PyCapsule_GetPointer(object, "dynamic_graph.Signal");
-  SignalBase<int>* signal = (SignalBase<int>*)pointer;
-  try {
-    signal->recompute(time);
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-  return Py_BuildValue("");
-}
-
-PyObject* unplug(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  void* pointer = NULL;
-  PyObject* object = NULL;
-  if (!PyArg_ParseTuple(args, "O", &object)) return NULL;
-  if (!PyCapsule_CheckExact(object)) return NULL;
-
-  pointer = PyCapsule_GetPointer(object, "dynamic_graph.Signal");
-  SignalBase<int>* signal = (SignalBase<int>*)pointer;
-  try {
-    signal->unplug();
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-  return Py_BuildValue("");
-}
-
-PyObject* isPlugged(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  void* pointer = NULL;
-  PyObject* object = NULL;
-  if (!PyArg_ParseTuple(args, "O", &object)) return NULL;
-  if (!PyCapsule_CheckExact(object)) return NULL;
-
-  pointer = PyCapsule_GetPointer(object, "dynamic_graph.Signal");
-  SignalBase<int>* signal = (SignalBase<int>*)pointer;
-  bool plugged = false;
-  try {
-    plugged = signal->isPlugged();
-  }
-  CATCH_ALL_EXCEPTIONS(m);
-  if (plugged)
-    return PyBool_FromLong(1);
-  else
-    return PyBool_FromLong(0);
+  return obj;
 }
 
-PyObject* getPlugged(
-#if PY_MAJOR_VERSION >= 3
-    PyObject* m, PyObject* args
-#else
-    PyObject*, PyObject* args
-#endif
-) {
-  void* pointer = NULL;
-  PyObject* object = NULL;
-  if (!PyArg_ParseTuple(args, "O", &object)) return NULL;
-  if (!PyCapsule_CheckExact(object)) return NULL;
-
-  pointer = PyCapsule_GetPointer(object, "dynamic_graph.Signal");
-  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(m);
-  // Return the pointer to the signal without destructor since the signal
-  // is not owned by the calling object.
-  return PyCapsule_New((void*)otherSignal, "dynamic_graph.Signal", NULL);
-}
 }  // namespace signalBase
 }  // namespace python
 }  // namespace dynamicgraph
diff --git a/src/dynamic_graph/signal-caster-py.cc b/src/dynamic_graph/signal-caster-py.cc
deleted file mode 100644
index 73b854c0b68adbabdc13009b3ec8f0aa66e6b36b..0000000000000000000000000000000000000000
--- a/src/dynamic_graph/signal-caster-py.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2010, Florent Lamiraux, Thomas Moulard, LAAS-CNRS.
-
-#include <iostream>
-#include <sstream>
-
-#include <dynamic-graph/signal-caster.h>
-
-#include "dynamic-graph/python/dynamic-graph-py.hh"
-
-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
diff --git a/src/dynamic_graph/signal-wrapper.cc b/src/dynamic_graph/signal-wrapper.cc
index a6c7df28f88698820824feef68c92cc33208ae1b..731e898e74d2b43e7e2f95b6c78bddedad04b689 100644
--- a/src/dynamic_graph/signal-wrapper.cc
+++ b/src/dynamic_graph/signal-wrapper.cc
@@ -8,30 +8,6 @@
 
 namespace dynamicgraph {
 namespace python {
-namespace signalWrapper {
-void convert(PyObject* o, bool& v) { v = (o == Py_True); }
-void convert(PyObject* o, int& v) { v = (int)PyLong_AsLong(o); }
-void convert(PyObject* o, float& v) { v = (float)PyFloat_AS_DOUBLE(o); }
-void convert(PyObject* o, double& v) { v = PyFloat_AS_DOUBLE(o); }
-void convert(PyObject* o, Vector& v) {
-  v.resize(PyTuple_Size(o));
-  for (int i = 0; i < v.size(); ++i) convert(PyTuple_GetItem(o, i), v[i]);
-}
-}  // namespace signalWrapper
-
-PythonSignalContainer::PythonSignalContainer(const std::string& name) : Entity(name) {
-  std::string docstring;
-
-  docstring =
-      "    \n"
-      "    Remove a signal\n"
-      "    \n"
-      "      Input:\n"
-      "        - name of the signal\n"
-      "    \n";
-  addCommand("rmSignal", command::makeCommandVoid1(*this, &PythonSignalContainer::rmSignal, docstring));
-}
-
 void PythonSignalContainer::signalRegistration(const SignalArray<int>& signals) {
   Entity::signalRegistration(signals);
 }
@@ -41,12 +17,10 @@ void PythonSignalContainer::rmSignal(const std::string& name) { Entity::signalDe
 DYNAMICGRAPH_FACTORY_ENTITY_PLUGIN(PythonSignalContainer, "PythonSignalContainer");
 
 template <class T, class Time>
-bool SignalWrapper<T, Time>::checkCallable(PyObject* c, std::string& error) {
-  if (PyCallable_Check(c) == 0) {
-    PyObject* str = PyObject_Str(c);
-    error = obj_to_str(str);
+bool SignalWrapper<T, Time>::checkCallable(pyobject c, std::string& error) {
+  if (PyCallable_Check(c.ptr()) == 0) {
+    error = boost::python::extract<std::string>(c.attr("__str__")());
     error += " is not callable";
-    Py_DECREF(str);
     return false;
   }
   return true;
diff --git a/src/dynamic_graph/signal_base.py b/src/dynamic_graph/signal_base.py
index e48838c1bb5a15ecb408d490e3c462c4c8e54061..ba40575e0fac4631d57c7d668fb6b060d20d0bc8 100644
--- a/src/dynamic_graph/signal_base.py
+++ b/src/dynamic_graph/signal_base.py
@@ -1,18 +1,14 @@
-"""
-  Copyright (C) 2010 CNRS
-
-  Author: Florent Lamiraux
-"""
+# Copyright (C) 2020 CNRS
+#
+# Author: Joseph Mirabel
 
 from __future__ import print_function
 
-import re
-
-from .wrap import (create_signal_wrapper, signal_base_display, signal_base_display_dependencies,
-                   signal_base_get_class_name, signal_base_get_name, signal_base_get_time, signal_base_get_value,
-                   signal_base_getPlugged, signal_base_isPlugged, signal_base_recompute, signal_base_set_time,
-                   signal_base_set_value, signal_base_unplug)
+from .wrap import SignalBase, create_signal_wrapper as SignalWrapper
 
+# I kept what follows for backward compatibility but I think it should be
+# removed
+import re
 
 def stringToTuple(vector):
     """
@@ -153,136 +149,3 @@ def stringToObject(string):
         return float(string)
     except Exception:
         return string
-
-
-class SignalBase(object):
-    """
-    This class binds dynamicgraph::SignalBase<int> C++ class
-    """
-
-    obj = None
-
-    def __init__(self, name="", obj=None):
-        """
-        Constructor: if not called by a child class, create and store a pointer
-        to a C++ SignalBase<int> object.
-        """
-        if obj:
-            self.obj = obj
-        else:
-            raise RuntimeError("A pointer is required to create SignalBase object.")
-
-        if obj is None:
-            self.className = self.getClassName()
-            self.name = self.getName()
-
-    @property
-    def time(self):
-        """
-        Get time of signal
-        """
-        return signal_base_get_time(self.obj)
-
-    @time.setter
-    def time(self, val):
-        """
-        Set Time of signal
-
-          Input:
-            - an integer
-        """
-        return signal_base_set_time(self.obj, val)
-
-    @property
-    def value(self):
-        """
-        Setter and getter for the value of a signal
-
-        Binds C++ SignalBase<int>::get() and set() methods. Values are passed
-        through string streams.
-        A string is interpreted as respectively:
-        * a matrix (tuple of tuple) if string fits '[n,m]((x_11,x_12,...,x_1m),...,(x_n1,x_n2,...,x_nm))' format where
-          n and m are integers, x_ij are floating point numbers,
-        * a tuple if string fits '[n](x_1, x_2, ..., x_n)' format,
-        * an integer,
-        * a floating point number.
-
-        If string fits none of the above formats, no conversion is performed.
-
-        For instance, is s binds a signal of type vector,
-        >>> s.value = (2.5, .1, 1e2)
-        will call SignalBase<int>::set("[3](2.5,0.1,100.0)") and
-        >>> s.value
-        (2.5, 0.1, 100.0)
-        """
-        string = signal_base_get_value(self.obj)
-        return stringToObject(string)
-
-    @value.setter
-    def value(self, val):
-        """
-        Set the signal as a constant signal with given value.
-        If the signal is plugged, it will be unplugged
-        """
-        string = objectToString(val)
-        return signal_base_set_value(self.obj, string)
-
-    def getName(self):
-        """
-        Get name of signal
-        """
-        return signal_base_get_name(self.obj)
-
-    @property
-    def name(self):
-        """
-        Get name of signal
-        """
-        return signal_base_get_name(self.obj)
-
-    def getClassName(self):
-        """
-        Get class name of signal
-        """
-        return signal_base_get_class_name(self.obj)
-
-    def recompute(self, time):
-        """
-        Force signal to recompute the value at given time.
-        """
-        return signal_base_recompute(self.obj, time)
-
-    def unplug(self):
-        """
-        Unplug a PTR signal.
-        """
-        return signal_base_unplug(self.obj)
-
-    def isPlugged(self):
-        """
-        Return whether a signal is plugged.
-        """
-        return signal_base_isPlugged(self.obj)
-
-    def getPlugged(self):
-        """
-        Return the plugged signal.
-        """
-        return SignalBase(obj=signal_base_getPlugged(self.obj))
-
-    def __str__(self):
-        """
-        Print signal in a string
-        """
-        return signal_base_display(self.obj)
-
-    def displayDependencies(self, iter):
-        """
-        Print signal dependencies in a string
-        """
-        return (signal_base_display_dependencies(self.obj, iter))
-
-
-class SignalWrapper(SignalBase):
-    def __init__(self, name, type, func):
-        super(SignalWrapper, self).__init__(name, create_signal_wrapper(name, type, func))
diff --git a/src/dynamic_graph/tracer/wrap.cc b/src/dynamic_graph/tracer/wrap.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a790c98bfd7e5845e973983c0349531865eb6604
--- /dev/null
+++ b/src/dynamic_graph/tracer/wrap.cc
@@ -0,0 +1,10 @@
+#include "dynamic-graph/python/module.hh"
+
+#include <dynamic-graph/tracer.h>
+
+BOOST_PYTHON_MODULE(wrap) {
+  using dynamicgraph::Tracer;
+
+  bp::import("dynamic_graph");
+  dynamicgraph::python::exposeEntity<Tracer>().def("addSignal", &Tracer::addSignalToTrace);
+}
diff --git a/src/dynamic_graph/tracer_real_time/wrap.cc b/src/dynamic_graph/tracer_real_time/wrap.cc
new file mode 100644
index 0000000000000000000000000000000000000000..9fb24ec86b802d1c163fba74ed36d0954e9ddab1
--- /dev/null
+++ b/src/dynamic_graph/tracer_real_time/wrap.cc
@@ -0,0 +1,11 @@
+#include "dynamic-graph/python/module.hh"
+
+#include <dynamic-graph/tracer-real-time.h>
+
+BOOST_PYTHON_MODULE(wrap) {
+  using dynamicgraph::Tracer;
+  using dynamicgraph::TracerRealTime;
+
+  bp::import("dynamic_graph.tracer");
+  dynamicgraph::python::exposeEntity<TracerRealTime, bp::bases<Tracer> >();
+}
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 7f74b238181809148d5264abee72eb59901fba2a..fe5754567262d996af870ff60d5cd6a282b061d3 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -2,11 +2,11 @@
 
 # Test the interpreter
 ADD_UNIT_TEST(interpreter-test interpreter-test.cc)
-TARGET_LINK_LIBRARIES(interpreter-test ${PROJECT_NAME})
+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 ${PYTHON_LIBRARY} ${Boost_LIBRARIES} ${PROJECT_NAME})
+TARGET_LINK_LIBRARIES(interpreter-test-runfile PRIVATE ${PROJECT_NAME} Boost::unit_test_framework)
 TARGET_COMPILE_DEFINITIONS(interpreter-test-runfile PRIVATE PATH="${CMAKE_CURRENT_LIST_DIR}/")
 
 # Test the module generation
@@ -19,7 +19,7 @@ IF(SUFFIX_SO_VERSION)
     PROPERTIES SOVERSION ${PROJECT_VERSION})
 ENDIF(SUFFIX_SO_VERSION)
 
-target_link_libraries(${LIBRARY_NAME} dynamic-graph::dynamic-graph)
+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")
@@ -29,18 +29,26 @@ CONFIGURE_FILE(
   ${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}/__init__.py
   )
 SET(PYTHON_MODULE "${LIBRARY_NAME}-wrap")
-SET(SOURCE_PYTHON_MODULE "cmake/dynamic_graph/python-module-py.cc")
-ADD_LIBRARY(${PYTHON_MODULE} MODULE ${PROJECT_SOURCE_DIR}/${SOURCE_PYTHON_MODULE})
+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} "-Wl,--no-as-needed")
+  TARGET_LINK_LIBRARIES(${PYTHON_MODULE} PRIVATE "-Wl,--no-as-needed")
 ENDIF(UNIX AND NOT APPLE)
 
-TARGET_LINK_LIBRARIES(${PYTHON_MODULE} ${LIBRARY_NAME} ${PYTHON_LIBRARY})
-TARGET_INCLUDE_DIRECTORIES(${PYTHON_MODULE} SYSTEM PUBLIC ${PYTHON_INCLUDE_DIRS})
+TARGET_LINK_LIBRARIES(${PYTHON_MODULE} PRIVATE
+  ${LIBRARY_NAME} dynamic-graph-python
+  ${PYTHON_LIBRARY})
+TARGET_LINK_BOOST_PYTHON(${PYTHON_MODULE} PRIVATE)
+TARGET_INCLUDE_DIRECTORIES(${PYTHON_MODULE} SYSTEM PRIVATE ${PYTHON_INCLUDE_DIRS})
 
 ## Test it
 ADD_PYTHON_UNIT_TEST("test-custom-entity" "tests/test_custom_entity.py" src tests)
diff --git a/tests/custom_entity.cpp b/tests/custom_entity.cpp
index 90632d7ada1ad224e7466faf335c87fd39f5bdf6..1d4bf52b71ee84cec945b96150525b169cfe8978 100644
--- a/tests/custom_entity.cpp
+++ b/tests/custom_entity.cpp
@@ -5,7 +5,9 @@
 
 #define ENABLE_RT_LOG
 
-#include <sstream>
+#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>
@@ -13,54 +15,47 @@
 #include <dynamic-graph/real-time-logger.h>
 #include <dynamic-graph/signal-ptr.h>
 #include <dynamic-graph/signal-time-dependent.h>
-#include <dynamic-graph/command-bind.h>
 
 namespace dynamicgraph {
-class CustomEntity : public Entity {
- public:
-  dynamicgraph::SignalPtr<double, int> m_sigdSIN;
-  dynamicgraph::SignalTimeDependent<double, int> m_sigdTimeDepSOUT;
-
-  static const std::string CLASS_NAME;
-  virtual const std::string &getClassName() const { return CLASS_NAME; }
-  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 addSignal() { signalRegistration(m_sigdSIN << m_sigdTimeDepSOUT); }
-
-  void rmValidSignal() {
-    signalDeregistration("in_double");
-    signalDeregistration("out_double");
-  }
 
-  double &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;
-  }
+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(); }
 
-  void act() { m_sigdSIN.accessCopy(); }
-};
 DYNAMICGRAPH_FACTORY_ENTITY_PLUGIN(CustomEntity, "CustomEntity");
 }  // namespace dynamicgraph
diff --git a/tests/custom_entity.h b/tests/custom_entity.h
new file mode 100644
index 0000000000000000000000000000000000000000..b382fe49827582826e4d82c469fd12e8deb20e8c
--- /dev/null
+++ b/tests/custom_entity.h
@@ -0,0 +1,30 @@
+/* Copyright 2020 LAAS, CNRS
+ * Joseph Mirabel
+ *
+ */
+
+#define ENABLE_RT_LOG
+
+#include <sstream>
+#include <dynamic-graph/entity.h>
+#include <dynamic-graph/signal-ptr.h>
+#include <dynamic-graph/signal-time-dependent.h>
+
+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
diff --git a/tests/custom_entity_module.h b/tests/custom_entity_module.h
new file mode 100644
index 0000000000000000000000000000000000000000..b4f2169f0ff628f9c3f16c528ff9624470aeb4ea
--- /dev/null
+++ b/tests/custom_entity_module.h
@@ -0,0 +1,3 @@
+#include "custom_entity.h"
+
+typedef boost::mpl::vector<dynamicgraph::CustomEntity> entities_t;
diff --git a/tests/test_bindings.py b/tests/test_bindings.py
index 8b022ce88deb4d6ea9e4c41a3777f3af157a170a..92ea4a5ad415ea8b3f05edcc2ea5f09ee5fa89bb 100644
--- a/tests/test_bindings.py
+++ b/tests/test_bindings.py
@@ -1,18 +1,15 @@
 import unittest
 
 import dynamic_graph as dg
-
 from custom_entity import CustomEntity
 
-ERR = "dynamic_graph.plug(a, b): Argument '%s' must be of type 'dynamic_graph.Signal', but got dynamic_graph.Entity"
+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_bindings(self):
-        with self.assertRaises(dg.dgpyError) as cm:
-            dg.error_out()
-        self.assertEqual(str(cm.exception), "something bad happened")
-
     def test_type_check(self):
         """
         test the type checking in signal plugs
@@ -25,12 +22,12 @@ class BindingsTests(unittest.TestCase):
         # 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 % 'b')
+        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 % 'a')
+        self.assertEqual(str(cm_out.exception), ERR % ("CustomEntity", "SignalPtrDouble"))
 
     def test_dg_exc(self):
         """
@@ -38,7 +35,7 @@ class BindingsTests(unittest.TestCase):
         """
         ent = CustomEntity('test_dg_exc')
         # check that accessing a non initialized signal raises
-        with self.assertRaises(dg.dgpyError) as cm:
+        with self.assertRaises(RuntimeError) as cm:
             ent.act()
         self.assertEqual(
             str(cm.exception),