Python bindings for IDL-generated services #2757
Replies: 3 comments 4 replies
-
fyi @S-Dafarra @GiulioRomualdi @RiccardoGrieco @lrapetti @kouroshD |
Beta Was this translation helpful? Give feedback.
-
While it's rather easy to write such a client in Python, I can't see a way to make this work for server responders. I think it's caused by polymorphism not spanning through the C++/Python glue code barrier. Even if I define a |
Beta Was this translation helpful? Give feedback.
-
Hi @PeterBowman, One of my goal for YARP 3.6 would be to have the .i file autogenerated by the thrift compiler, would you like to help me defining how this file should be generated? I would also like to generate a CMakeLists.txt file or a CMake macro to generate a library and the bindings. |
Beta Was this translation helpful? Give feedback.
-
I have been recently fiddling with Python bindings in the context of YARP+IDL. Since I see no related discussion nor code examples devoted for this matter, I wanted to post here my results and findings in case the quirks described along these lines are useful for someone, or there is a better way to achieve the same result.
So, my goal is to create a compiled library that encapsulates a Thrift service conveniently translated into C++ code via
yarpidl_thrift
through YARP's CMake utilities. See speech.thrift and the associated CMakeLists.txt. I'm creating a CMakeSHARED
target for reuse by other local targets, or to be exported and consumed in other CMake projects. As a result, this library comprehends the .h header I want to expose as a Python module via SWIG, and the compiled .so library against which the glue code will be linked.Speaking of the bindings themselves, I pretty much replicated the same CMake code and structure present in the YARP repository: bindings tree. It is interesting to take a closer look into the .i file in which a
roboticslab::TextToSpeechIDL
service is defined:We see two mandatory standard
%include
s that mySpeechIDL.h
interface requires, this will vary across projects and is mostly relevant on runtime. More interestingly, I noticed SWIG itself will fail without the remaining lines:The Thrift-generated interface is a C++ class named
SpeechIDL
which inherits fromyarp::os::Wire
, and this in turn is derived fromyarp::os::PortReader
. SWIG will complain that nothing is known about the base class (Wire
) unless it is instructed to look into its definition, and similarly it needs to know about its parentPortReader
as well. Note these two classes are abstract. The two following lines allow SWIG to correctly deduce the inheritance chain ofSpeechIDL
and relate it to the YARP Python module (there will be aimport yarp
in the resulting .py file, andSpeechIDL
will be marked as a descendant ofyarp.Wire
):A more subtle bug relates to the dynamic dispatch of polymorphic calls across the inheritance chain when a server is implemented through deriving the IDL-generated class. The
read
method defined in the C++ wrapper code, i.e. the one which forwards remote calls to the local implementation of this IDL service, is not aware of derived classes in Python code unless the following lines are added:Moreover, SWIG has some trouble parsing the
PortReader
base class because of certain macros. I noticed thatYARP_os_API
(in PortReader.h) andYARP_DEPRECATED_MSG
(in ConnectionReader.h) lead to cryptic errors, easily hacked around with these additional includes (along with a memory leak we'd get warned about on runtime):Lastly, there is a forward declaration of
yarp::os::Type
in PortReader.h. It is used as a return type ingetReadType()
, and the compiler complains about it while building the generated wrapper code. We need to include its header explicitly.And that's all. The remote YARP/Thrift service can be accessed now from Python code as in this example client app. It is also possible to write a server-side implementation.
Beta Was this translation helpful? Give feedback.
All reactions