Source

py.js / doc / index.rst

Full commit

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 :func:`py.eval`. :func:`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 :func:`py.tokenize`, :func:`py.parse` and :func:`py.evaluate` directly.

API

Core functions

Utility functions

These are functions provided to implement py objects which can be used within a py.js evaluation, they're essentially py.js's version of the Python C API. They are prefixed with PY_

Type wrapping javascript functions into py.js callables. The wrapped function follows :ref:`the py.js calling conventions <calling-conventions-python>`

param Function fn:
 the javascript function to wrap
returns:a callable py.js object

Implementation details

Conversions from Javascript to Python

py.js will automatically attempt to convert non-:class:`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 :class:`py.object` instances.

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

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

Conversions from Python to Javascript

py.js types (extensions of :js:class:`py.object`) can be converted back to javascript by calling their :js:func:`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 :js:func:`py.object.toJSON` implementation out of the box.

Calling convention

Because Python supports keyword arguments and javascript does not, moving arguments from the python domain to the javascript domain is not completely straightforward.

py.js implements two different conventions for that case (…)

  • Dunder methods (pre- and post-fixed with __) of the Python Data Model — fully and knowingly called in javascript — receive their arguments unwrapped and positionally, as normal arguments to a javascript functions. These arguments will still generally be :class:`py.object`.
  • Arbitrary user-defined callables receive two parameters args and kwargs, holding respectively the positional and keyword arguments passed to the callable from the expression.

    These can be parsed into a JS object via :func:`py.PY_parseArgs`.

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'").

[1]Python 2, which py.js currently implements, does not support Python-level keyword-only parameters (it can be done through the C-API), but it seemed neat and easy enough so there.