Black Lives Matter. Please consider donating to Black Girls Code today.

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

5 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