npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@fridgerator/pynode

v0.5.2

Published

Node <-> Python interopability

Downloads

42

Readme

PyNode

Build Status

Call python code from node.js

Node v10 or above is required

Tested with python 3.6 - 3.8.

Installation

BEFORE NPM INSTALL OR YARN INSTALL

  • Make sure python in your system PATH is the correct one: python --version. You may use a virtualenv to do this.
  • Install gyp-next: git clone https://github.com/nodejs/gyp-next; cd gyp-next; python setup.py install
  • yarn add @fridgerator/pynode or npm install @fridgerator/pynode
  • If your default python is version 2.7, then you may have to yarn install using additional env variables: PY_INCLUDE=$(python3.6 build_include.py) PY_LIBS=$(python3.6 build_ldflags.py) yarn

Usage

Async API

In a python file example.py:

def add(a, b):
  return a + b

in node:

const pynode = require('@fridgerator/pynode')

// Workaround for linking issue in linux:
// https://bugs.python.org/issue4434
// if you get: `undefined symbol: PyExc_ValueError` or `undefined symbol: PyExc_SystemError`
pynode.dlOpen('libpython3.6m.so') // your libpython shared library

// optionally pass a path to use as Python module search path
pynode.startInterpreter()

// add current path as Python module search path, so it finds our test.py
pynode.appendSysPath('./')

// open the python file (module)
pynode.openFile('example')

// call the python function and get a return value
pynode.call('add', 1, 2, (err, result) => {
  if (err) return console.log('error : ', err)
  result === 3 // true
})

Full Object API

The Full Object API allows full interaction between JavaScript objects within Python code, and Python objects within JavaScript code. In Python, this works very transparently without needing to know if a value is a Python or JS object. In JavaScript, the interface is (currently) a bit more primitive, but supports getting object members and calling objects using .get() and .call().

Primitives are generally converted between JS and Python values, so passing strings, numbers, lists, and dicts generally works as expected. The wrappers described above only kick in for non-primitives.

In a python file test_files/objects.py:


def does_things_with_js(jsobject):
    '''This function demonstrates that we can treat passed-in
    JavaScript objects (almost) as if they were native Python objects.
    '''
    # We can print out JS objects; toString is called automatically
    # (it is mapped to Python's __str__):
    print("does_things_with_js:", jsobject)
    
    # We can call any function on the JS object. Objects passed
    # in are converted to JS if they are primitives, or wrapped in the
    # Full Object API if not, so JavaScript can also call back into Python:
    result = jsobject.arbitraryFunction("nostrongtypinghere")
    
    # The result from JavaScript is also converted back into Python:
    print(result)
    
    # The result will be converted back into JavaScript by PyNode:
    return result

class PythonClass(object):
    '''A simple class to demonstrate creating and calling an instance from JavaScript.'''

    def __init__(self, a, b):
        '''Constructor. a and b are converted to Python types by PyNode'''
        self.a = a
        self.b = b

    def collate(self, callback):
        '''A function that is given a callback to call into. This can be a JavaScript function'''
        print('PythonClass.collate()')
        print('callback:', callback)
	
	# Arguments are converted into JavaScript objects, and the return value
	# from the callback is converted back into Python objects.
        return callback(self.a, self.b, "hello", 4)

    def __repr__(self):
        return 'PythonClass(a=%r, b=%r)' % (self.a, self.b)

In Node:

const pynode = require('@fridgerator/pynode')

pynode.startInterpreter();
pynode.appendSysPath('./test_files/');

/* Modules are imported as JS objects, implementing the Full Object API */
const objectsmodule = pynode.import('objects'); /* test module in test_files */
const python_builtins = pynode.import('builtins'); /* access to python builtins such as str, all, etc, if you want them. */

/* Define an example JavaScript class: */
class JSClass {
    constructor(item) {
        this.item = item
    }
    arbitraryFunction(value) {
        console.log("arbitraryFunction called; item = " + this.item);
        console.log("                         value = " + value);
        return value + 12;
    }
    toString() {
        return "JSClass{item=" + this.item + "}"
    }
}

/* Create an instance of JSClass which Python will have access to: */
jsclassinstance = new JSClass(['some', 'data']);
console.log(jsclassinstance);

/* Get the 'does_things_with_js' Python function from the objects module,
   and call it, passing in the JSClass object: */
result = objectsmodule.get('does_things_with_js').call(jsclassinstance);

/* Python objects are automatically converted back to JS, or returned as
   PyNodeWrappedPythonObject instances (see below( if they are
   non-primitive types:
*/
console.log(result);

/* Create a Python class. In Python this is done by calling the class
   object with constructor arguments (ie, don't use `new`): */
somedict = {'some': 'dict'};
pyclassinstance = objectsmodule.get('PythonClass').call(somedict, '2');

/* The resulting object is returned as a PyNodeWrappedPythonObject instance,
   which implements the Full Object API: */
console.log(pyclassinstance);

/* The PyNodeWrappedPythonObject instance can have members retrieved from it
   and functions called. Below is the equivalent of (in Python) 
   `getattr(pyclassinstance, 'collate').__call__(<some javascript function>)`
   or put simply:
   `pyclassinstance.collate(<some javascript function>)`.
   The JavaScript function is also converted to a callable Python object */
*/
result = pyclassinstance.get('collate').call(function(a, b, c, d) {
    console.log(arguments);
    return c * d; /* JavaScript return values are converted back to Python types by PyNode */
});
console.log(result);