Package: dash-transcrypt
The purpose of dash-transcrypt is making it easy toWrite clientside callbacks in PythonPass function handles as component propertiesUnder the hood, it utilizestranscryptfor the transpiling of Python to JavaScript.Getting startedThe recommended way to install dash-transcrypt is via pip,pip install dash-transcryptIn addition, a working java installation is required (it's used in the minification process). To run the examples related to function properties, dash-leaflet and geobuf are also needed,pip install geobuf dash-leafletClientside callbacksThe functions to be used as clientside callbacks must be placed in a separate module (file), saycalculator_cf.py. In this example, we will consider a simpleaddfunction,def add(a, b): 
    return a + bBefore theaddfunction can be used as a clientside callback, thecalculator_cfmodule must be passed to theto_clientside_functionsfunction. In addition to transpiling the module into JavaScript, it replaces the function attributes of the module withClientsideFunctionobjects so that they can be used in clientside callbacks,import caculator_cf as ccf 
 from dash_transcrypt import module_to_clientside_functions, inject_js 
 ... 
 inject_js(app, module_to_clientside_functions(ccf))
 app.clientside_callback(ccf.add, ...)Theto_clientside_functionsreturns the path to a JavaScript index file, which must be made available to the app (that's whatinject_jsdoes). For completeness, here is the full (apart fromcaculator_cf.py)  example app,import dash  
import dash_core_components as dcc  
import dash_html_components as html  
import caculator_cf as ccf  

from dash.dependencies import Output, Input  
from dash_transcrypt 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(ccf))  
app.clientside_callback(ccf.add, Output("c", "children"), [Input("a", "value"), Input("b", "value")])  

if __name__ == '__main__':  
    app.run_server()Functions as propertiesAs you might already know, it isnotpossible to pass function handles as component properties in Dash. To circumvent this limitation, dash-transcrypt passes thefull path to the functionas a string. It's then up to the component to create the function.An example of a component that supports this flow is theGeoJSONcomponent indash-leaflet. One of the function properties is thepointToLayeroption, which must be a function (or rather a full path to a function) that matches the signature of thepointToLayeroption of the underlyingLeaflet GeoJSON object. The relevant function(s) must be placed in a separate module (file), sayscatter_js.py,def point_to_layer(feature, latlng, context):
    radius = feature.properties.value*10
    return L.circleMarker(latlng, dict(radius=radius))Before the function(s) can be assigned as a property, the module must be passed through themodule_to_propsfunction. 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)For completeness, here is the full example appimport random
import dash
import dash_html_components as html
import dash_leaflet as dl
import scatter_js as sjs
import dash_leaflet.express as dlx

from dash_transcrypt import inject_js, module_to_props

# Create some markers.
points = [dict(lat=55.5 + random.random(), lon=9.5 + random.random(), value=random.random()) for i in range(100)]
data = dlx.dicts_to_geojson(points)
# Create geojson.
js = module_to_props(sjs)  # compiles the js
geojson = dl.GeoJSON(data=data, options=dict(pointToLayer=sjs.point_to_layer))  # pass function as prop
# Create the app.
app = dash.Dash()
app.layout = html.Div([dl.Map([dl.TileLayer(), geojson], center=(56, 10), zoom=8, style={'height': '50vh'})])
inject_js(app, js)  # adds the js to the app

if __name__ == '__main__':
    app.run_server()Passing arguments at compile timeVariable assignments followed by# <kwarg>are overwritten at compile time by the dash-transcypt preprocessor. As an extension of the previous example, say one would like to be able to vary the scaling of the radius. This could be achieved by modifyingscatter_js.pytoscale = 10  # <kwarg>

def point_to_layer(feature, latlng, context):
    radius = feature.properties.value * scale
    return L.circleMarker(latlng, dict(radius=radius))The defaultscale10 as before, but the value can now be modified by changing a single line in the application code,js = module_to_props(cjs, scale=20)  # compiles the jsPassing arguments at runtimeWhile not enforced by dash-transcrypt, it is recommended that a context (typically a reference tothis) is passed to all functional properties. Furthermore, it is recommended that ahideoutproperty is added whichdoes nothing, but serves as a container arguments at runtime. TheGeoJSONcomponent from the previous example(s) follows these guidelines. Hence by modifyingscatter_js.pytodef point_to_layer(feature, latlng, context):  
    scale = context.props.hideout.scale  
    radius = feature.properties.value * scale  
    return L.circleMarker(latlng, dict(radius=radius))thescalecan now be changed on runtime. That is, the map visualization can now beinteractive. Here is a small app, where a slider changes thescaleand thus the size of the makers,import random
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_leaflet as dl
import scatter_rt_js as rjs
import dash_leaflet.express as dlx

from dash.dependencies import Input, Output
from dash_transcrypt import inject_js, module_to_props

# Create some markers.
points = [dict(lat=55.5 + random.random(), lon=9.5 + random.random(), value=random.random()) for i in range(100)]
data = dlx.dicts_to_geojson(points)
# Create geojson.
js = module_to_props(rjs)  # compiles the js
geojson = dl.GeoJSON(data=data, options=dict(pointToLayer=rjs.point_to_layer),  # pass function as prop
                     hideout=dict(scale=10), id="geojson")  # pass variables to function
# Create the app.
app = dash.Dash()
app.layout = html.Div([
    dl.Map([dl.TileLayer(), geojson], center=(56, 10), zoom=8, style={'height': '50vh'}),
    dcc.Slider(min=1, max=100, value=10, id="slider")
])
inject_js(app, js)  # adds the js to the app

@app.callback(Output("geojson", "hideout"), [Input("slider", "value")], prevent_initial_call=False)
def update_scale(value):
    return dict(scale=value)

if __name__ == '__main__':
    app.run_server()NotesBrowsers tend to cache javascript assets. When changes have been made to the python functions, it might therefore be necessary to force a reload of the page (ctrl+F5) to get the updated function definitions.