NodeJS and Python interoperability!14 Jun 2019
Calling python functions from node
NodeJS <-> Python interoperability is
relatively easy doable. I’m not talking about using string interpolated system calls and parsing command line returns, or some other janky method. Both languages are written in C/C++, so interop is possible via their native bindings. Follow me on my journey of using the low level API’s of two languages I really dont even like that much! 🙁🤣 (full disclosure… just being honest)
And in the spirit of this blog, BEER ⇓
A somewhat similar concept exists for Python - Embedding Python. You can run snippets of Python code or open existing files (modules) and call functions directly, again converting between C++ and Python types for parameters and return values. A Python interpreter is still required for this to work, however portability can still be achieved, more on this later. A very good blog post over at awasu.com gives a very detailed explanation with examples of writing a Python wrapper in C++.
Full source code available here
First thing in the
Initialize function we set up some search paths so Python can find the interpreter and required libraries and pass them to
Py_SetPath. Next we initialize the Python interpreter, and append the current directory Python’s system path so it can found our local python module. Finally we can tell Python to decode our
tools.py file and import it so we can call it later on.
We’ve added a
multiply function to our node module exports, which will call our
Multiply c++ function. After checking our arguments, we create a couple of
double variables from them using the handy
Nan::To helper methods. We load our python function using
PyObject_GetAttrString and make sure we’ve found a callable function with
multiply function, the next setp is to convert these two
double variables into python function arguments. We create a new Python tuple with a size of 2, and then add those
double variables to the tuple. And now the magic moment we’ve been waiting for:
pValue = PyObject_CallObject(pFunc, pArgs);. Assuming
NULL, we’ve successfully called the python function from node and have received a return value. We convert
pValue to a
long and then set the return value for our node function!
Pretty freakin cool IMO
In this code example I have downloaded and built Python 3.7.3 locally, if you check out the
binding.gyp file you’ll notice the local folder includes. It is also possible to build a portable Python distribution to ship with the node application. This could be useful for an Electron application. Another detailed blog post by João Ventura describes how to do so in OSX.
This certainly is much more work than using
child_process.spawn to run python. Is the extra effort worth it? I dont really know.
Its a more direct call with the benefit of having the ability to check argument and return types. It’s even possible to create a hexdump of our python file as a c
char variable and then include it at compile time using
xxd -i tools.py.
I’m going to be playing around more with this idea to find out what else might be possible.