1
Write a post

Integrating Node.js & Python to Write Cross-Language Modules using pyExecJs

Published Apr 30, 2015Last updated May 11, 2017
Integrating Node.js & Python to Write Cross-Language Modules using pyExecJs

The Purpose

To answer the inevitable first question of "why" I'm doing this tutorial, it's because I have been writing more and more JavaScript lately, and I have been using paver, which is a Python module, to automate my JavaScript build tasks (e.g. compiling CoffeeScript, and minification.)

Now, in an attempt to minimize the actual amount of JavaScript I write, I would like to add ng-annotate as a step in my build process. This will allow me to write all of my angular code without thinking about its dependency injection, but still taking advantage of it. However, there currently is no Python module that I can use to run code through ng-annotate, which is a JavaScript library.

PyExecJs

How are we going to write cross-language modules?

With the Python module PyexecJs! PyexecJs allows us to write functions in JavaScript, which we can then call from our Python code. The JavaScript functions we write can return data right to our Python code. PyExecJS also lets us choose the JavaScript engine to use, which helps as we will need to take advantage of the Node engine to work with ng-annotate, since ng-annotate is a Node module.

So to start lets use pip to install the pyexecjs library:

$ pip install pyexecjs

Next we will start our module file (which we will call ng_annotate.py) by importing the get function from execjs, which allows us to request a specific JavaScript engine to run our code.

from execjs import get
import os

runtime = get('Node')

Now that we have a runtime engine to work with, we have what we need.

Next, we will need to be sure that if someone installs this, it will have access to the node module ng-annotate. So, create a file called package.json, and put this in it:

{
    "dependencies":{
        "ng-annotate":"*"
    }
}

Finally, we just need to add an instruction to run npm install before using it.

And now back in our module file, we need to write our JavaScript function that our module will use, as well as make sure it can access the node_modules folder that will be added by npm install. We do this by wrapping our JavaScript in a call to runtime.compile so we can access the functions inside from our Python code.

context = runtime.compile('''
    module.paths.push('%s');
    ng = require('ng-annotate');
    function annotate(src,cfg){
          return ng(src,cfg);  
    }
''' % os.path.join(os.path.dirname(__file__),'node_modules'))

Now we can call the annotate function from our Python code with
context.call('annotate',*args), and it will return the result just like it would in the JavaScript. So to make that into a standard Python function we can use, it's as easy as:

def ng_annotate(src,cfg=None):
    if cfg is None:
        cfg = dict(add=True)    
    return context.call('annotate',src,cfg)

Now we can use it from our Python code with from ng_annotate import ng_annotate and calling that function, but let's also make it a convenient command line tool:

import sys

def main():
    print ng_annotate(open(sys.argv[-1],'r').read()) 

if __name__ == "__main__":
    main()

Here's the whole ng_annotate.py file for reference:

import os
import sys
from execjs import get

runtime = get('Node')
context = runtime.compile('''
    module.paths.push('%s');
    var ng = require('ng-annotate');
    function annotate(src,cfg){
        return ng(src,cfg);
    }
''' % os.path.join(os.path.dirname(__file__),'node_modules'))

def ng_annotate(src,cfg=None):
    if cfg is None:
        cfg = dict(add=True)
    return context.call('annotate',src,cfg)

def main():
    print ng_annotate(open(sys.argv[-1],'r').read())

if __name__ == "__main__":
    main()
Discover and read more posts from Kyle J. Roux
get started
Enjoy this post?

Leave a like and comment for Kyle

3
2
2Replies
Lee Eames
a month ago

Thanks for the post. Worked perfectly.

Kyle J. Roux
12 days ago

Haha, I’m very selfish in that if I didn’t ever need this at some point it may not have been written, but it’s nice to hear others have found uses for it, that is exactly why I wrote this post. Thanks for letting me know it worked out for u.

Subscribe to our weekly newsletter