Last time we looked at how to write a server application in C++, and call it from Python. This time, we’ll do things the other way around: implementing a service in Python, and calling from C++.
Python server
Unlike our C++ server example, Thrift doesn’t help us out so much — we don’t get a helpfully pre-written skeleton, and we have to do all of the work ourselves. That said, it’s still pretty straightforward.
Essentially all we need to do is to create a class, derived from the service.Iface class that Thrift writes for us, to represent our service — defining methods as required by the service.
As a reminder here is our Thrift service definition file (LoggerService.thrift)
So our class needs to contain definitions for the four methods — plus any initialization code we need.
Note that unlike our C++ example, the Python implementation of get_last_log_entry
uses a conventional return for the string; rather than the pass-by-reference technique we needed to use in C++.
Then all that we need to do, is to import the various files that Thrift has written for us, which define the underlaying functionality; and start the service running.
This next code snippet is more or less boilerplate code — though obviously you will need to change the references to the Service and Namespace names.
So the final code looks like this:
C++ Client
Now for the C++ Client.
This is pretty much exactly a C++ conversion of the previous Python client application. The only significant difference is that we’ll use boost to create the socket / transport…
Note once again, that we’re back with having to use the pass-by-reference for the string that’s returned by get_last_log_entry.
To build the C++ client, we need (essentially) the same build process as we used for the server. Since we already have a CMake file to hand, let’s use that to generate the build-script for the client.
In comparison to the previous file, all that we need to do is add the CLIENT_FILES definition, and add (and link) the LoggerClient executable. If you’re only building the server, then of course you don’t need the LoggerServer lines…
ThriftPy
There is one other approach that you can take when using Thrift with Python, and that is to use the ThriftPy library.
ThriftPy aims to take a more pythonic approach to Thrift. Unlike the conventional approach — when using ThriftPy you don’t need to run thrift —gen py my file.thrift to statically generate the Python code: rather you can load the .thrift file into your Python program, and dynamically create the support functions. It also removes the requirement for the (binary) Thrift libraries to be installed on the machine running the ThriftPy code. The only installation step required is pip install thriftpy.
For example, to use ThriftPy with our LoggerService.thrift file we use:
From here — we simply implement the client application in exactly the same way as we did previously in Python.
The only change is if we’re using exception handling — where instead of catching our LoggerExceptions as Thrift.TException — we need to use thriftpy.thrift.TException.
So the full code for the ThriftPy client becomes:
Implementing the server can be don win a similar manner.
Once again we load our .thrift file at runtime, create a server object, and start it running.
The slight difference when using ThriftPy to using the more Thrift generated Python code is that instead of implementing our server methods in a class that inherits fromLogger.Iface — we put them in a Dispatcher class which inherits from the Python base-object object. This class is then passed as a parameter to make_server.
For example:
Note that there’s no requirement for the Dispatcher to be called Dispatcher() — you can name it anything you like.
The implementation of the methods within Dispatcher() are identical to the previous method.
So the full version of the ThriftPy server would look like this:
Whether or not you think using ThriftPy is easier or not, is up to you — but if you want your code to be implemented in pure Python, or prefer the more pythonic approach to doing things, then it’s certainly worth a look.
I’ve tested my simple LoggerService example with all combinations of clients and servers implemented using all three of the methods described; and regardless of the implementation, all three approaches are fully interoperable with each other: so, as the saying goes, you pays your money and you takes your choice…
I hope that’s been interesting, and/or useful. If you have any questions of comments then please let me know.
If you want to try this for yourself, then the complete source code for both client & server, in both Python, ThriftPy, and C++ can be found at: https://github.com/Auctoris/ThriftDemo