Skip to content
Snippets Groups Projects

Compare revisions

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

Source

Select target project
No results found

Target

Select target project
  • ostasse/dynamic-graph-python
  • gsaurel/dynamic-graph-python
  • stack-of-tasks/dynamic-graph-python
3 results
Show changes
Showing
with 679 additions and 1690 deletions
// 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
/*
* Copyright CNRS 2021
*
* Author: Florent Lamiraux
*
* This file is part of sot-core.
*/
#ifndef DYNAMIC_GRAPH_PYTHON_FWD_HH
#define DYNAMIC_GRAPH_PYTHON_FWD_HH
#include <dynamic-graph/fwd.hh>
namespace dynamicgraph {
namespace python {
class Interpreter;
typedef shared_ptr<Interpreter> InterpreterPtr_t;
} // namespace python
} // namespace dynamicgraph
#endif // DYNAMIC_GRAPH_PYTHON_FWD_HH
...@@ -6,11 +6,10 @@ ...@@ -6,11 +6,10 @@
#undef _POSIX_C_SOURCE #undef _POSIX_C_SOURCE
#undef _XOPEN_SOURCE #undef _XOPEN_SOURCE
#include "dynamic-graph/python/api.hh" #include <dynamic-graph/python/fwd.hh>
#include "dynamic-graph/python/deprecated.hh"
#include "dynamic-graph/python/python-compat.hh"
#include "dynamic-graph/python/api.hh" #include "dynamic-graph/python/api.hh"
#include "dynamic-graph/python/python-compat.hh"
namespace dynamicgraph { namespace dynamicgraph {
namespace python { namespace python {
...@@ -26,11 +25,13 @@ class DYNAMIC_GRAPH_PYTHON_DLLAPI Interpreter { ...@@ -26,11 +25,13 @@ class DYNAMIC_GRAPH_PYTHON_DLLAPI Interpreter {
/// \brief Method to start python interperter. /// \brief Method to start python interperter.
/// \param command string to execute /// \param command string to execute
/// Method deprecated, you *SHOULD* handle error messages. /// Method deprecated, you *SHOULD* handle error messages.
DYNAMIC_GRAPH_PYTHON_DEPRECATED std::string python(const std::string& command); [[deprecated("you *SHOULD* handle error messages")]] std::string python(
const std::string& command);
/// \brief Method to start python interperter. /// \brief Method to start python interperter.
/// \param command string to execute, result, stdout, stderr strings /// \param command string to execute, result, stdout, stderr strings
void python(const std::string& command, std::string& result, std::string& out, std::string& err); void python(const std::string& command, std::string& result, std::string& out,
std::string& err);
/// \brief Method to exectue a python script. /// \brief Method to exectue a python script.
/// \param filename the filename /// \param filename the filename
......
// -*- mode: c++ -*-
// Copyright 2011, Florent Lamiraux, CNRS.
#ifdef WIN32
#include <Windows.h>
#else
#include <dlfcn.h>
#endif
namespace dynamicgraph {
namespace python {
std::string libpython("@PYTHON_LIBRARY@");
} // namespace python
} // namespace dynamicgraph
#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 <dynamic-graph/entity.h>
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/python.hpp>
#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
...@@ -4,29 +4,23 @@ ...@@ -4,29 +4,23 @@
#ifndef DGPY_SIGNAL_WRAPPER #ifndef DGPY_SIGNAL_WRAPPER
#define DGPY_SIGNAL_WRAPPER #define DGPY_SIGNAL_WRAPPER
#include <dynamic-graph/entity.h>
#include <dynamic-graph/linear-algebra.h> #include <dynamic-graph/linear-algebra.h>
#include <dynamic-graph/signal.h> #include <dynamic-graph/signal.h>
#include <dynamic-graph/entity.h>
#include <boost/bind.hpp>
#include <boost/python.hpp>
#include "dynamic-graph/python/python-compat.hh" #include "dynamic-graph/python/python-compat.hh"
namespace dynamicgraph { namespace dynamicgraph {
namespace python { 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 { class PythonSignalContainer : public Entity {
DYNAMIC_GRAPH_ENTITY_DECL(); DYNAMIC_GRAPH_ENTITY_DECL();
public: public:
PythonSignalContainer(const std::string& name); using Entity::Entity;
void signalRegistration(const SignalArray<int>& signals); void signalRegistration(const SignalArray<int>& signals);
...@@ -37,17 +31,18 @@ template <class T, class Time> ...@@ -37,17 +31,18 @@ template <class T, class Time>
class SignalWrapper : public Signal<T, Time> { class SignalWrapper : public Signal<T, Time> {
public: public:
typedef Signal<T, Time> parent_t; 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; typedef boost::function2<T&, T&, Time> function_t;
Py_INCREF(callable);
function_t f = boost::bind(&SignalWrapper::call, this, _1, _2); function_t f = boost::bind(&SignalWrapper::call, this, _1, _2);
this->setFunction(f); this->setFunction(f);
} }
virtual ~SignalWrapper() { Py_DECREF(callable); }; virtual ~SignalWrapper(){};
private: private:
T& call(T& value, Time t) { T& call(T& value, Time t) {
...@@ -56,18 +51,12 @@ class SignalWrapper : public Signal<T, Time> { ...@@ -56,18 +51,12 @@ class SignalWrapper : public Signal<T, Time> {
if (PyGILState_GetThisThreadState() == NULL) { if (PyGILState_GetThisThreadState() == NULL) {
dgDEBUG(10) << "python thread not initialized" << std::endl; dgDEBUG(10) << "python thread not initialized" << std::endl;
} }
char format[] = "i"; pyobject obj = callable(t);
PyObject* obj = PyObject_CallFunction(callable, format, t); value = boost::python::extract<T>(obj);
if (obj == NULL) {
dgERROR << "Could not call callable" << std::endl;
} else {
signalWrapper::convert(obj, value);
Py_DECREF(obj);
}
PyGILState_Release(gstate); PyGILState_Release(gstate);
return value; return value;
} }
PyObject* callable; pyobject callable;
}; };
} // namespace python } // namespace python
......
// Copyright 2020, Joseph Mirabel, LAAS-CNRS.
#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 <boost/python.hpp>
#include <sstream>
#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
<package format="2"> <?xml version="1.0"?>
<package format="3">
<name>dynamic-graph-python</name> <name>dynamic-graph-python</name>
<version>3.4.2</version> <version>4.0.11</version>
<description> <description>Dynamic graph library Python bindings</description>
Dynamic graph library Python bindings <maintainer email="guilhem.saurel@laas.fr">Guilhem Saurel</maintainer>
</description>
<maintainer email="ostasse@laas.fr">Olivier Stasse</maintainer>
<license>BSD</license>
<url>http://github.com/stack-of-tasks/dynamic-graph-python</url>
<author>Nicolas Mansard</author> <author>Nicolas Mansard</author>
<author>Olivier Stasse</author> <author>Olivier Stasse</author>
<license>BSD</license>
<build_depend>roscpp</build_depend> <url type="website">https://github.com/stack-of-tasks/dynamic-graph-python</url>
<build_depend>dynamic-graph</build_depend>
<exec_depend>roscpp</exec_depend>
<exec_depend>dynamic-graph</exec_depend>
<build_export_depend>roscpp</build_export_depend> <build_depend>git</build_depend>
<build_export_depend>dynamic-graph</build_export_depend> <build_depend>doxygen</build_depend>
<buildtool_depend>catkin</buildtool_depend> <!-- The following tag is recommended by REP-136 -->
<exec_depend condition="$ROS_VERSION == 1">catkin</exec_depend>
<doc_depend>doxygen</doc_depend> <depend condition="$ROS_PYTHON_VERSION == 2">python</depend>
<depend condition="$ROS_PYTHON_VERSION == 3">python3</depend>
<depend condition="$ROS_PYTHON_VERSION == 2">python-numpy</depend>
<depend condition="$ROS_PYTHON_VERSION == 3">python3-numpy</depend>
<depend>eigen</depend>
<depend>boost</depend>
<depend>eigenpy</depend>
<depend>dynamic-graph</depend>
<buildtool_depend>cmake</buildtool_depend>
<export>
<build_type>cmake</build_type>
</export>
</package> </package>
[tool.black]
exclude = "cmake"
extend-exclude = "tests/test_python-syntax_error.py"
[flake8]
exclude = cmake,tests/test_python-syntax_error.py
max-line-length = 88
ignore = E226, E704, E24, E121, W504, E126, E123, W503, E203
# Copyright 2010-2020, Florent Lamiraux, Thomas Moulard, Olivier Stasse, Guilhem Saurel, JRL, CNRS/AIST, LAAS-CNRS # Python bindings
# add_subdirectory(dynamic_graph)
#
# Python interpreter
#
#
SET(LIBRARY_NAME ${PROJECT_NAME}) set(PYTHON_SOURCES __init__.py attrpath.py entity.py signal_base.py
ADD_LIBRARY(${LIBRARY_NAME} SHARED interpreter.cc dynamic_graph/python-compat.cc) script_shortcuts.py tools.py)
TARGET_INCLUDE_DIRECTORIES(${LIBRARY_NAME} foreach(source ${PYTHON_SOURCES})
PUBLIC python_install_on_site(dynamic_graph ${source})
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> endforeach(source)
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/../include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/>
INTERFACE
$<INSTALL_INTERFACE:include>
)
TARGET_LINK_LIBRARIES(${LIBRARY_NAME} ${PYTHON_LIBRARY})
IF(UNIX)
TARGET_LINK_LIBRARIES(${LIBRARY_NAME} ${Boost_LIBRARIES})
TARGET_LINK_LIBRARIES(${LIBRARY_NAME} ${Boost_PYTHON_LIBRARIES})
TARGET_LINK_LIBRARIES(${LIBRARY_NAME} ${CMAKE_DL_LIBS})
ENDIF(UNIX)
SET_TARGET_PROPERTIES(${LIBRARY_NAME} PROPERTIES SOVERSION ${PROJECT_VERSION})
TARGET_LINK_LIBRARIES(${LIBRARY_NAME} dynamic-graph::dynamic-graph)
INSTALL(TARGETS ${LIBRARY_NAME}
EXPORT ${TARGETS_EXPORT_NAME}
PUBLIC_HEADER
INCLUDES DESTINATION include
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
#
#
# Python bindings
#
#
ADD_SUBDIRECTORY(dynamic_graph)
SET (PYTHON_SOURCES
__init__.py
attrpath.py
entity.py
signal_base.py
script_shortcuts.py
tools.py
)
FOREACH (SOURCE ${PYTHON_SOURCES})
PYTHON_INSTALL_ON_SITE(dynamic_graph ${SOURCE})
ENDFOREACH (SOURCE)
# --- ADD the wrap on the dg modules # --- ADD the wrap on the dg modules
# Tracer plugin link_directories(${DYNAMIC_GRAPH_PLUGINDIR})
IF(WIN32) dynamic_graph_python_module(
SET(TRACER_PLUGIN ${DYNAMIC_GRAPH_PLUGINDIR}/tracer${CMAKE_STATIC_LIBRARY_SUFFIX}) "tracer" dynamic-graph::tracer tracer-wrap SOURCE_PYTHON_MODULE
ELSE(WIN32) ${CMAKE_CURRENT_SOURCE_DIR}/dynamic_graph/tracer/wrap.cc)
SET(TRACER_PLUGIN ${DYNAMIC_GRAPH_PLUGINDIR}/tracer${CMAKE_SHARED_LIBRARY_SUFFIX}) dynamic_graph_python_module(
ENDIF(WIN32) "tracer_real_time" dynamic-graph::tracer-real-time tracer_real_time-wrap
DYNAMIC_GRAPH_PYTHON_MODULE("tracer" ${TRACER_PLUGIN} tracer-wrap) SOURCE_PYTHON_MODULE
${CMAKE_CURRENT_SOURCE_DIR}/dynamic_graph/tracer_real_time/wrap.cc)
# TracerRealTime plugin
IF(WIN32)
SET(TRACERREALTIME_PLUGIN ${DYNAMIC_GRAPH_PLUGINDIR}/tracer-real-time${CMAKE_STATIC_LIBRARY_SUFFIX})
ELSE(WIN32)
SET(TRACERREALTIME_PLUGIN ${DYNAMIC_GRAPH_PLUGINDIR}/tracer-real-time${CMAKE_SHARED_LIBRARY_SUFFIX})
ENDIF(WIN32)
DYNAMIC_GRAPH_PYTHON_MODULE("tracer_real_time" ${TRACERREALTIME_PLUGIN} tracer_real_time-wrap)
# Copyright 2010-2020, Florent Lamiraux, Thomas Moulard, Olivier Stasse, Guilhem Saurel, JRL, CNRS/AIST, LAAS-CNRS # Copyright 2010-2021, Florent Lamiraux, Thomas Moulard, Olivier Stasse, Guilhem
# Saurel, JRL, CNRS/AIST, LAAS-CNRS
SET(PYTHON_MODULE wrap) set(PYTHON_MODULE wrap)
ADD_LIBRARY(${PYTHON_MODULE} add_library(
MODULE ${PYTHON_MODULE} MODULE debug-py.cc dynamic-graph-py.cc factory-py.cc
convert-dg-to-py.cc pool-py.cc signal-base-py.cc signal-wrapper.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_LINK_LIBRARIES(${PYTHON_MODULE} ${PYTHON_LIBRARY}) target_link_libraries(${PYTHON_MODULE} PUBLIC ${PROJECT_NAME} eigenpy::eigenpy)
TARGET_LINK_LIBRARIES(${PYTHON_MODULE} dynamic-graph::dynamic-graph)
# Remove prefix lib # Remove prefix lib
SET_TARGET_PROPERTIES(${PYTHON_MODULE} set_target_properties(${PYTHON_MODULE} PROPERTIES PREFIX "")
PROPERTIES PREFIX "")
INSTALL(TARGETS ${PYTHON_MODULE} if(UNIX AND NOT APPLE)
set_target_properties(${PYTHON_MODULE} PROPERTIES INSTALL_RPATH
"\$ORIGIN/../../..")
endif()
install(
TARGETS ${PYTHON_MODULE}
EXPORT ${TARGETS_EXPORT_NAME} EXPORT ${TARGETS_EXPORT_NAME}
DESTINATION ${PYTHON_SITELIB}/dynamic_graph) DESTINATION ${PYTHON_SITELIB}/dynamic_graph)
...@@ -5,31 +5,6 @@ Author: Florent Lamiraux ...@@ -5,31 +5,6 @@ Author: Florent Lamiraux
from __future__ import print_function from __future__ import print_function
import sys
from . import entity # noqa from . import entity # noqa
from . import signal_base # 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 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
...@@ -18,10 +18,11 @@ class CommandPath(object): ...@@ -18,10 +18,11 @@ class CommandPath(object):
to store entity commands. It has no members except those automatically to store entity commands. It has no members except those automatically
defined at run time (which should be CommandPath or functions). defined at run time (which should be CommandPath or functions).
""" """
mother = None mother = None
def __getattr__(self, name): def __getattr__(self, name):
privateName = name + '_obj' privateName = name + "_obj"
if privateName in self.__dict__: if privateName in self.__dict__:
obj = getattr(self, privateName) obj = getattr(self, privateName)
obj.mother = self.mother obj.mother = self.mother
...@@ -38,7 +39,7 @@ def createCommandModule(target, name): ...@@ -38,7 +39,7 @@ def createCommandModule(target, name):
return __ return __
privateName = name + '_obj' privateName = name + "_obj"
setattr(target, privateName, CommandPath()) setattr(target, privateName, CommandPath())
if not isinstance(target, CommandPath): if not isinstance(target, CommandPath):
...@@ -46,8 +47,8 @@ def createCommandModule(target, name): ...@@ -46,8 +47,8 @@ def createCommandModule(target, name):
class CommandLauncher(object): class CommandLauncher(object):
""" """ """
"""
mother = None mother = None
fun = None fun = None
...@@ -60,7 +61,7 @@ class CommandLauncher(object): ...@@ -60,7 +61,7 @@ class CommandLauncher(object):
def createCommandLauncher(target, name, fun): def createCommandLauncher(target, name, fun):
if isinstance(target, CommandPath): if isinstance(target, CommandPath):
privateName = name + '_obj' privateName = name + "_obj"
setattr(target, privateName, CommandLauncher(fun)) setattr(target, privateName, CommandLauncher(fun))
else: else:
setattr(target, name, fun) setattr(target, name, fun)
...@@ -74,7 +75,7 @@ def setattrpath(target, path, attribute): ...@@ -74,7 +75,7 @@ def setattrpath(target, path, attribute):
pathk = target pathk = target
read = True read = True
if isinstance(path, str): if isinstance(path, str):
path = path.split('.') path = path.split(".")
for tokenk in path[0:-1]: for tokenk in path[0:-1]:
if (not read) | (tokenk not in pathk.__dict__): if (not read) | (tokenk not in pathk.__dict__):
read = False read = False
...@@ -94,7 +95,7 @@ def getattrpath(target, path): ...@@ -94,7 +95,7 @@ def getattrpath(target, path):
""" """
pathk = target pathk = target
if isinstance(path, str): if isinstance(path, str):
path = path.split('.') path = path.split(".")
for tokenk in path: for tokenk in path:
privateName = tokenk + "_obj" privateName = tokenk + "_obj"
if hasattr(pathk, privateName): if hasattr(pathk, privateName):
...@@ -103,7 +104,12 @@ def getattrpath(target, path): ...@@ -103,7 +104,12 @@ def getattrpath(target, path):
if hasattr(pathk, tokenk): if hasattr(pathk, tokenk):
pathk = getattr(pathk, tokenk) pathk = getattr(pathk, tokenk)
else: else:
raise Exception('Path does not exist -- while accessing "' + tokenk + '" in ' + '.'.join(path)) raise Exception(
'Path does not exist -- while accessing "'
+ tokenk
+ '" in '
+ ".".join(path)
)
return pathk return pathk
...@@ -114,11 +120,11 @@ def existattrpath(target, path): ...@@ -114,11 +120,11 @@ def existattrpath(target, path):
""" """
pathk = target pathk = target
if isinstance(path, str): if isinstance(path, str):
path = path.split('.') path = path.split(".")
for tokenk in path[0:-1]: for tokenk in path[0:-1]:
print('check ', tokenk) print("check ", tokenk)
privateName = tokenk + "_obj" privateName = tokenk + "_obj"
if (privateName not in pathk.__dict__): if privateName not in pathk.__dict__:
return False return False
pathk = getattr(pathk, privateName) pathk = getattr(pathk, privateName)
name = path[-1] name = path[-1]
......
// Copyright 2010, Florent Lamiraux, Thomas Moulard, LAAS-CNRS. // Copyright 2010, Florent Lamiraux, Thomas Moulard, LAAS-CNRS.
#include <iostream> #include "dynamic-graph/python/convert-dg-to-py.hh"
#include <sstream>
#include <dynamic-graph/signal-base.h> #include <dynamic-graph/signal-base.h>
#include <dynamic-graph/signal.h>
#include <dynamic-graph/signal-caster.h> #include <dynamic-graph/signal-caster.h>
#include <dynamic-graph/signal.h>
#include <boost/python.hpp>
#include <boost/python/stl_iterator.hpp>
#include <iostream>
#include <sstream>
#include "dynamic-graph/python/convert-dg-to-py.hh"
#include "dynamic-graph/python/python-compat.hh" #include "dynamic-graph/python/python-compat.hh"
namespace dynamicgraph { namespace dynamicgraph {
...@@ -17,203 +20,39 @@ using ::dynamicgraph::SignalBase; ...@@ -17,203 +20,39 @@ using ::dynamicgraph::SignalBase;
namespace python { namespace python {
namespace convert { namespace convert {
void fillMatrixRow(Matrix& m, unsigned iRow, PyObject* sequence) { namespace bp = boost::python;
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.");
}
}
command::Value pythonToValue(PyObject* pyObject, const command::Value::Type& valueType) { command::Value toValue(bp::object o, const command::Value::Type& valueType) {
using command::Value; 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) { switch (valueType) {
case (Value::BOOL): case (Value::BOOL):
if (!PyBool_Check(pyObject)) { return Value(bp::extract<bool>(o));
throw ExceptionPython(ExceptionPython::VALUE_PARSING, "bool");
}
bvalue = PyObject_IsTrue(pyObject);
return Value(bvalue);
break;
case (Value::UNSIGNED): case (Value::UNSIGNED):
if (PyLong_Check(pyObject)) { return Value(bp::extract<unsigned>(o));
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;
case (Value::INT): case (Value::INT):
if (PyLong_Check(pyObject)) { return Value(bp::extract<int>(o));
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;
case (Value::FLOAT): case (Value::FLOAT):
if (PyFloat_Check(pyObject)) { return Value(bp::extract<float>(o));
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;
case (Value::DOUBLE): case (Value::DOUBLE):
if (PyFloat_Check(pyObject)) { return Value(bp::extract<double>(o));
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;
case (Value::STRING): case (Value::STRING):
if (!PyUnicode_Check(pyObject) return Value(bp::extract<std::string>(o));
#if PY_MAJOR_VERSION == 2
&& !PyString_Check(pyObject)
#endif
) {
throw ExceptionPython(ExceptionPython::VALUE_PARSING, "string");
}
svalue = obj_to_str(pyObject);
return Value(svalue);
break;
case (Value::VECTOR): case (Value::VECTOR):
// Check that argument is a tuple // TODO for backward compatibility, support tuple or list ?
if (!PySequence_Check(pyObject)) { // I don't think so
throw ExceptionPython(ExceptionPython::VALUE_PARSING, "vector"); return Value(bp::extract<Vector>(o));
}
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;
case (Value::MATRIX): case (Value::MATRIX):
// Check that argument is a tuple // TODO for backward compatibility, support tuple or list ?
if (!PySequence_Check(pyObject)) { // I don't think so
throw ExceptionPython(ExceptionPython::VALUE_PARSING, "matrix"); return Value(bp::extract<Matrix>(o));
}
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;
case (Value::MATRIX4D): case (Value::MATRIX4D):
// Check that argument is a tuple return Value(bp::extract<Eigen::Matrix4d>(o));
if (!PySequence_Check(pyObject)) { case (Value::VALUES):
throw ExceptionPython(ExceptionPython::VALUE_PARSING, "matrix4d"); // TODO the vector of values cannot be built since
} // - the value type inside the vector are not know
nRows = PySequence_Size(pyObject); // - inferring the value type from the Python type is not implemented.
if (nRows == 0) { throw std::invalid_argument(
return Value(Eigen::Matrix4d()); "not implemented: cannot create a vector of values");
}
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; break;
default: default:
std::cerr << "Only int, double and string are supported." << std::endl; std::cerr << "Only int, double and string are supported." << std::endl;
...@@ -221,87 +60,36 @@ command::Value pythonToValue(PyObject* pyObject, const command::Value::Type& val ...@@ -221,87 +60,36 @@ command::Value pythonToValue(PyObject* pyObject, const command::Value::Type& val
return Value(); return Value();
} }
PyObject* vectorToPython(const Vector& vector) { bp::object fromValue(const command::Value& value) {
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* valueToPython(const command::Value& value) {
using command::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()) { switch (value.type()) {
case (Value::BOOL): case (Value::BOOL):
boolValue = value.value(); return bp::object(value.boolValue());
if (boolValue) {
return PyBool_FromLong(1);
}
return PyBool_FromLong(0);
case (Value::UNSIGNED): case (Value::UNSIGNED):
unsignedValue = value.value(); return bp::object(value.unsignedValue());
return Py_BuildValue("I", unsignedValue);
case (Value::INT): case (Value::INT):
intValue = value.value(); return bp::object(value.intValue());
return Py_BuildValue("i", intValue);
case (Value::FLOAT): case (Value::FLOAT):
floatValue = value.value(); return bp::object(value.floatValue());
return Py_BuildValue("f", floatValue);
case (Value::DOUBLE): case (Value::DOUBLE):
doubleValue = value.value(); return bp::object(value.doubleValue());
return Py_BuildValue("d", doubleValue);
case (Value::STRING): case (Value::STRING):
stringValue = (std::string)value.value(); return bp::object(value.stringValue());
return Py_BuildValue("s", stringValue.c_str());
case (Value::VECTOR): case (Value::VECTOR):
vectorValue = value.value(); return bp::object(value.vectorValue());
return vectorToPython(vectorValue);
case (Value::MATRIX): case (Value::MATRIX):
matrixValue = value.value(); return bp::object(value.matrixXdValue());
return matrixToPython(matrixValue);
case (Value::MATRIX4D): case (Value::MATRIX4D):
matrix4dValue = value.value(); return bp::object(value.matrix4dValue());
return matrix4dToPython(matrix4dValue); case (Value::VALUES): {
bp::list list;
for (const Value& v : value.constValuesValue()) list.append(fromValue(v));
return list;
}
case (Value::NONE):
default: default:
return Py_BuildValue(""); return bp::object();
} }
return Py_BuildValue("");
} }
} // namespace convert } // namespace convert
......
...@@ -5,15 +5,14 @@ ...@@ -5,15 +5,14 @@
#include <iostream> #include <iostream>
#define ENABLE_RT_LOG #define ENABLE_RT_LOG
#include <dynamic-graph/entity.h>
#include <dynamic-graph/pool.h>
#include <dynamic-graph/real-time-logger.h> #include <dynamic-graph/real-time-logger.h>
#include <boost/shared_ptr.hpp>
#include <map> #include <map>
#include <dynamic-graph/pool.h>
#include <dynamic-graph/entity.h>
#include <vector> #include <vector>
#include "dynamic-graph/python/exception.hh"
#include <boost/shared_ptr.hpp>
#include "dynamic-graph/python/dynamic-graph-py.hh" #include "dynamic-graph/python/dynamic-graph-py.hh"
typedef boost::shared_ptr<std::ofstream> ofstreamShrPtr; typedef boost::shared_ptr<std::ofstream> ofstreamShrPtr;
...@@ -21,108 +20,31 @@ typedef boost::shared_ptr<std::ofstream> ofstreamShrPtr; ...@@ -21,108 +20,31 @@ typedef boost::shared_ptr<std::ofstream> ofstreamShrPtr;
namespace dynamicgraph { namespace dynamicgraph {
namespace python { namespace python {
#if PY_MAJOR_VERSION == 2
extern PyObject* dgpyError;
# endif
namespace debug { namespace debug {
std::map<std::string, ofstreamShrPtr> mapOfFiles_; std::map<std::string, ofstreamShrPtr> mapOfFiles_;
PyObject* addLoggerFileOutputStream( void addLoggerFileOutputStream(const char* filename) {
#if PY_MAJOR_VERSION >= 3 std::ofstream* aofs = new std::ofstream;
PyObject* m, PyObject* args ofstreamShrPtr ofs_shrptr = boost::shared_ptr<std::ofstream>(aofs);
#else aofs->open(filename, std::ofstream::out);
PyObject*, PyObject* args dynamicgraph::RealTimeLogger::instance();
#endif dgADD_OSTREAM_TO_RTLOG(*aofs);
) { dgRTLOG() << "Added " << filename << " as an output stream \n";
char* filename; mapOfFiles_[filename] = ofs_shrptr;
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("");
} }
PyObject* closeLoggerFileOutputStream( void closeLoggerFileOutputStream() {
#if PY_MAJOR_VERSION >= 3 for (const auto& el : mapOfFiles_) el.second->close();
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("");
} }
PyObject* addLoggerCoutOutputStream( void addLoggerCoutOutputStream() { dgADD_OSTREAM_TO_RTLOG(std::cout); }
#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("");
}
PyObject* realTimeLoggerDestroy( void realTimeLoggerDestroy() { RealTimeLogger::destroy(); }
#if PY_MAJOR_VERSION >= 3
PyObject* m, PyObject*
#else
PyObject*, PyObject*
#endif
) {
try {
RealTimeLogger::destroy();
}
CATCH_ALL_EXCEPTIONS(m);
return Py_BuildValue("");
}
PyObject* realTimeLoggerSpinOnce( void realTimeLoggerSpinOnce() { RealTimeLogger::instance().spinOnce(); }
#if PY_MAJOR_VERSION >= 3
PyObject* m, PyObject*
#else
PyObject*, PyObject*
#endif
) {
try {
RealTimeLogger::instance().spinOnce();
}
CATCH_ALL_EXCEPTIONS(m);
return Py_BuildValue("");
}
PyObject* realTimeLoggerInstance( void realTimeLoggerInstance() { RealTimeLogger::instance(); }
#if PY_MAJOR_VERSION >= 3
PyObject* m, PyObject*
#else
PyObject*, PyObject*
#endif
) {
try {
RealTimeLogger::instance();
}
CATCH_ALL_EXCEPTIONS(m);
return Py_BuildValue("");
}
} // namespace debug } // namespace debug
} // namespace python } // namespace python
......
// Copyright 2010, Florent Lamiraux, Thomas Moulard, LAAS-CNRS. // Copyright 2010, Florent Lamiraux, Thomas Moulard, LAAS-CNRS.
#include <iostream> #include "dynamic-graph/python/dynamic-graph-py.hh"
#include <sstream>
#include <dynamic-graph/command.h>
#include <dynamic-graph/debug.h> #include <dynamic-graph/debug.h>
#include <dynamic-graph/entity.h>
#include <dynamic-graph/exception-factory.h> #include <dynamic-graph/exception-factory.h>
#include <dynamic-graph/factory.h>
#include <dynamic-graph/pool.h>
#include <dynamic-graph/signal-base.h> #include <dynamic-graph/signal-base.h>
#include <dynamic-graph/signal-time-dependent.h>
#include <dynamic-graph/signal.h>
#include <dynamic-graph/tracer.h>
#include "dynamic-graph/python/exception.hh" #include <Eigen/Geometry>
#include "dynamic-graph/python/dynamic-graph-py.hh" #include <boost/python.hpp>
#include <boost/python/raw_function.hpp>
#include <boost/python/suite/indexing/map_indexing_suite.hpp>
#include <eigenpy/eigenpy.hpp>
#include <eigenpy/geometry.hpp>
#include <iostream>
#include <sstream>
#include "dynamic-graph/python/convert-dg-to-py.hh"
#include "dynamic-graph/python/module.hh"
#include "dynamic-graph/python/signal-wrapper.hh" #include "dynamic-graph/python/signal-wrapper.hh"
namespace dynamicgraph { namespace dynamicgraph {
namespace python { namespace python {
#if PY_MAJOR_VERSION == 2
PyObject* dgpyError;
# endif
/** /**
\brief plug a signal into another one. \brief plug a signal into another one.
*/ */
PyObject* plug( void plug(SignalBase<int>* signalOut, SignalBase<int>* signalIn) {
#if PY_MAJOR_VERSION >= 3 signalIn->plug(signalOut);
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;
}
pObjIn = PyCapsule_GetPointer(objIn, "dynamic_graph.Signal"); void enableTrace(bool enable, const char* filename) {
SignalBase<int>* signalIn = (SignalBase<int>*)pObjIn; if (enable)
if (signalIn == NULL) { DebugTrace::openFile(filename);
std::ostringstream oss; else
oss << "dynamic_graph.plug(a, b): Argument 'b' must be of type 'dynamic_graph.Signal', but got " DebugTrace::closeFile(filename);
<< PyCapsule_GetName(objIn); }
PyErr_SetString(PyExc_TypeError, oss.str().c_str());
return NULL; } // namespace python
} } // namespace dynamicgraph
pObjOut = PyCapsule_GetPointer(objOut, "dynamic_graph.Signal");
SignalBase<int>* signalOut = (SignalBase<int>*)pObjOut; namespace bp = boost::python;
if (signalOut == NULL) { namespace dg = dynamicgraph;
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 { typedef bp::return_value_policy<bp::manage_new_object> manage_new_object;
signalIn->plug(signalOut); 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());
} }
CATCH_ALL_EXCEPTIONS(m); };
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);
} }
PyObject* enableTrace( class PythonEntity : public dg::Entity {
#if PY_MAJOR_VERSION >= 3 DYNAMIC_GRAPH_ENTITY_DECL();
PyObject* m, PyObject* args
#else public:
PyObject*, PyObject* args using dg::Entity::Entity;
#endif
) { void signalRegistration(dg::SignalBase<int>& signal) {
PyObject* boolean; dg::Entity::signalRegistration(signal);
char* filename = NULL; }
void signalDeregistration(const std::string& name) {
if (PyArg_ParseTuple(args, "Os", &boolean, &filename)) { dg::Entity::signalDeregistration(name);
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;
} }
return Py_BuildValue(""); };
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( void exposeCommand() {
#if PY_MAJOR_VERSION >= 3 using dg::command::Command;
PyObject* m, PyObject* bp::class_<Command, boost::noncopyable>("Command", bp::no_init)
#else .def("__call__", bp::raw_function(dg::python::entity::executeCmd, 1),
PyObject*, PyObject* "execute the command")
#endif .add_property("__doc__", &Command::getDocstring);
) {
PyErr_SetString(DGPYERROR(m), "something bad happened");
return NULL;
} }
} // namespace python void exposeOldAPI() {
} // namespace dynamicgraph 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 void enableEigenPy() {
extern "C" { eigenpy::enableEigenPy();
#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;
}
Py_XINCREF(DGPYERROR(m)); if (!eigenpy::register_symbolic_link_to_registered_type<Eigen::Quaterniond>())
if (PyModule_AddObject(m, "dgpyError", DGPYERROR(m)) < 0) { eigenpy::exposeQuaternion();
Py_XDECREF(DGPYERROR(m)); if (!eigenpy::register_symbolic_link_to_registered_type<Eigen::AngleAxisd>())
Py_CLEAR(DGPYERROR(m)); eigenpy::exposeAngleAxis();
Py_DECREF(m);
INITERROR;
}
#if PY_MAJOR_VERSION >= 3 eigenpy::enableEigenPySpecific<Eigen::Matrix4d>();
return m;
#endif
} }
#ifdef __cplusplus BOOST_PYTHON_MODULE(wrap) {
} // extern "C" enableEigenPy();
#endif
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>();
}
This diff is collapsed.
""" # Copyright (C) 2020 CNRS
Copyright (C) 2010 CNRS #
# Author: Florent Lamiraux, Nicolas Mansard
Author: Florent Lamiraux, Nicolas Mansard
"""
from __future__ import print_function from __future__ import print_function
import types # for backward compat
from enum import Enum from .wrap import LoggerVerbosity as VerbosityLevel # noqa
from .wrap import Entity # noqa
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 = 0
VERBOSITY_INFO_WARNING_ERROR = 1
VERBOSITY_WARNING_ERROR = 2
VERBOSITY_ERROR = 3
VERBOSITY_NONE = 4
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 == 0:
return VerbosityLevel.VERBOSITY_ALL
elif r == 1:
return VerbosityLevel.VERBOSITY_INFO_WARNING_ERROR
elif r == 2:
return VerbosityLevel.VERBOSITY_WARNING_ERROR
elif r == 3:
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)
/*
* 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:
*/