py.js, a Python expressions parser and evaluator

py.js is a parser and evaluator of Python expressions, written in pure javascript.

py.js is not intended to implement a full Python interpreter, its specification document is the Python 2.7 Expressions spec (along with the lexical analysis part) as well as the Python builtins.

Usage

To evaluate a Python expression, simply call py.eval(). py.eval() takes a mandatory Python expression parameter, as a string, and an optional evaluation context (namespace for the expression’s free variables), and returns a javascript value:

> py.eval("t in ('a', 'b', 'c') and foo", {t: 'c', foo: true});
true

If the expression needs to be repeatedly evaluated, or the result of the expression is needed in its “python” form without being converted back to javascript, you can use the underlying triplet of functions py.tokenize(), py.parse() and py.evaluate() directly.

API

Core functions

py.eval(expr[, context])

“Do everything” function, to use for one-shot evaluation of Python expressions. Chains tokenizing, parsing and evaluating the expression then converts the result back to javascript

Parameters:
  • expr (String) – Python expression to evaluate
  • context (Object) – evaluation context for the expression’s free variables
Returns:

the expression’s result, converted back to javascript

py.tokenize(expr)

Expression tokenizer

Parameters:expr (String) – Python expression to tokenize
Returns:token stream
py.parse(tokens)

Parses a token stream and returns the corresponding parse tree.

The parse tree is stateless and can be memoized and reused for frequently evaluated expressions.

Parameters:tokens – token stream from py.tokenize()
Returns:parse tree
py.evaluate(tree[, context])

Evaluates the expression represented by the provided parse tree, using the provided context for the exprssion’s free variables.

Parameters:
  • tree – parse tree returned by py.parse()
  • context – evaluation context
Returns:

the “python object” resulting from the expression’s evaluation

Return type:

py.object

Conversions from Javascript to Python

py.js will automatically attempt to convert non-py.object values into their py.js equivalent in the following situations:

Notably, py.js will not attempt an automatic conversion of values returned by functions or methods, these must be py.object instances.

The automatic conversions performed by py.js are the following:

  • null is converted to py.None
  • true is converted to py.True
  • false is converted to py.False
  • numbers are converted to py.float
  • strings are converted to py.str
  • functions are wrapped into py.PY_dev
  • Array instances are converted to py.list

The rest generates an error, except for undefined which specifically generates a NameError.

Conversions from Python to Javascript

py.js types (extensions of py.object()) can be converted back to javascript by calling their py.object.toJSON() method.

The default implementation raises an error, as arbitrary objects can not be converted back to javascript.

Most built-in objects provide a py.object.toJSON() implementation out of the box.

Javascript-level exceptions

Javascript allows throwing arbitrary things, but runtimes don’t seem to provide any useful information (when they ever do) if what is thrown isn’t a direct instance of Error. As a result, while py.js tries to match the exception-throwing semantics of Python it only ever throws bare Error at the javascript-level. Instead, it prefixes the error message with the name of the Python expression, a colon, a space, and the actual message.

For instance, where Python would throw KeyError("'foo'") when accessing an invalid key on a dict, py.js will throw Error("KeyError: 'foo'").