Transcrypt - Transpiling Python to Javascript

While most of the code in a Dash app is Python, i tend to always end up writing at least a few lines of JavaScript. Either as client side callbacks, or for mapping of Dash arguments (string, numbers, etc.) into the data types expected by the underlying React component (most often function handles). Looking for a way to avoid this, i stumbled upon the transcrypt project, which transpiles Python to JavaScript.

After playing around with it a little (it works surprising good), i decided to write some bindings to Dash (they are available in `dash-transcrypt==0.0.6) to ease the integration. I am targeting two main usecases,

  • Clientside callbacks
  • Functional properties (e.g. props of Dash components that takes a function as the value)

Clientside callbacks

As a small example, consider a (simple) calculator that adds two numbers. The clientside function(s) must by placed in a separate file, say calculator_csf.py,

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

Before the add function can be used as a clientside callback, the calculator_csf module must be passed through the to_clientside_functions function. In addition to transpiling the module into JavaScript, it replaces the functional attributes of the module with appropriate ClientsideFunction objects so that they can be used in clientside callbacks,

import calculator_csf
from dash_extensions.transpile import module_to_clientside_functions, inject_js
...
js_index = module_to_clientside_functions(calculator_csf)  # the call generates the js
app.clientside_callback(calculator_csf.add, ...)  # pass the functions as you would normally do
inject_js(app, js_index)  # the call adds the js to the app

The to_clientside_functions returns the path to a JavaScript index file, which must be made available by the app (that’s what inject_js does). For completeness, here is the full example app,

import dash
import dash_core_components as dcc
import dash_html_components as html

import calculator_csf
from dash.dependencies import Output, Input
from dash_extensions.transpile import module_to_clientside_functions, inject_js

# Create example app.
app = dash.Dash()
app.layout = html.Div([
    dcc.Input(id="a", value=2, type="number"), html.Div("+"),
    dcc.Input(id="b", value=2, type="number"), html.Div("="), html.Div(id="c"),
])
# Create clientside callback.
inject_js(app, module_to_clientside_functions(calculator_csf))
app.clientside_callback(calculator_csf.add, Output("c", "children"), [Input("a", "value"), Input("b", "value")])

if __name__ == '__main__':
    app.run_server()

Functional properties

If a components supports translating the full path of a function (a string) to the function itself, dash-transcrypt can help build these strings. An example of a component that supports this flow is the GeoJSON component in dash-leaflet. The relevant function(s) must be placed in a separate module (file), say scatter_js.py ,

def point_to_layer(feature, latlng, context):
    return L.circleMarker(latlng, dict(radius=feature.properties.value*10))

Before the function(s) can be assigned as a property, the module must be passed through the module_to_props function. In addition to transpiling the module into JavaScript, it replaces the function attributes of the module with the appropriate strings,

import scatter_js as sjs 
import dash_leaflet as dl
from dash_transcrypt import inject_js, module_to_props 
... 
js = module_to_props(sjs)
geojson = dl.GeoJSON(data=data, options=dict(pointToLayer=sjs.point_to_layer))  # pass function as prop
...
inject_js(app, js)  

Have others played with something like this? What is your experience? :slightly_smiling_face:

EDIT: I have added the second use case and created a separate dash-transcrypt package

6 Likes

Wow, this is very sweet!

1 Like

I’ve been using Transcrypt to create React/MaterialUI apps that are 99% completely written in Python. It works amazingly well. You just have to know the API for the JavaScript library you want to integrate with, and you can code to it in Python.

I also saw a blurb that Cloudflare started using it for Python coded Cloudflare Workers, so hopefully being endorsed by a larger company will give it a bit of a visibility boost.

1 Like

Very sweet, thanks for sharing @JennaSys!

Have either of you tried out the Numpy port? http://www.transcrypt.org/numscrypt/numscrypt.html

No, I haven’t had a need for the Numpy port myself yet.

I have now moved the functionality for a separate package, dash-transcrypt. I have also added a new feature; a module_to_props function that outputs the full path to a function rather than a clientside function object. It can be used to pass functions as props of Dash components, which is used extensively in the new release of Dash Leaflet.

2 Likes

Wow, very cool to see function-as-props come together with dash-transcript. Brilliant!

1 Like

Really cool!

2 Questions:

  1. To dash maintainers … are you planning to make functional props a first class item? There are so many great javascript tables for example, but most heavily use functional properties and this is a big stumbling block
  2. to emil: transcrypt looks very unmaintained (last major update > 2 years ago). There are many other similar projects. Skulpt used by Anvil - (sorry for mentioning a competitor here), Brython. For me the coolest by far however is GitHub - pyodide/pyodide: Python with the scientific stack, compiled to WebAssembly.. Have you looked at if they would be a good fit? especially pyodide provides 75 python packages including scikit-image and sklearn that could be run clientside …

And thanks for you excellent work in this space.

There are a lot of other options than transcript available. At the time I wrote the code, transcrypt seemed like the best option for integration with Dash, which requires compiling the Python code into JS before the app is launched. It might have changed in the mean time, but at a glance e.g. pyodide runs Python in the browser, which is not really what is needed for Dash integration.

I think for now, the best option is to write small JavaScript functions and pass them as functional props.

1 Like

I am happy to inform you that development on Transcrypt has picked up again. Version 3.9.0 is out, working in conjunction with Python 3.9.x. Features like the walrus-operator will be build in gradually in the minor versions. Personally I am very interested in applications in the quantitative sciences, since that is my own working area.

Any wishes, remarks, contributions to Transcrypt in that area are very welcome. The primary reason for Transcrypt’s existence is having a single language on client and server for scientific applications. For me that’s mainly medical imaging and oil and gas prospecting.

Kind regards
Jacques de Hooge

3 Likes

Very nice! I just bought the book how to write React in Python.

Thanks for developing transcrypt!

1 Like