Dash: Hot reloading

Created on 4 Jul 2017  路  9Comments  路  Source: plotly/dash

I love that the Python module reloads automatically on save but I still need to refresh my browser when I make a change to the Python code, which means I lose the state I was in and need to fiddle with the UI to get the view back to how it was before I made the change. It would be great if we could leverage a system like React Hot Loader or something similar for totally seamless multi-monitor development. Just type code on one monitor, save, and turn your head to to see the changes on the other monitor ;)

dash-meta-sponsored

Most helpful comment

I thought about this a little bit more last night and I think we can get pretty close with just dash_core_components.Interval and debug=True. Check this out 馃樅

live-reload

Here's the code:

import dash
from dash.dependencies import Event, Input, Output
import dash_core_components as dcc
import dash_html_components as html

app = dash.Dash()

# Edit this object!
layout = html.Div([
    html.H1('Hello Dash!'),
    html.H3('Dash with live-reload'),
])

# Barebones layout
app.layout = html.Div([
    dcc.Interval(id='refresh', interval=200),
    html.Div(id='content', className="container")
])

# Update the `content` div with the `layout` object.
# When you save this file, `debug=True` will re-run
# this script, serving the new layout
@app.callback(
    Output('content', 'children'),
    events=[Event('refresh', 'interval')])
def display_layout():
    return layout

app.css.append_css({"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"})

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

All 9 comments

Definitely! Would love to add this.

If any company out there would like to help sponsor and prioritize this work, please reach out.

I thought about this a little bit more last night and I think we can get pretty close with just dash_core_components.Interval and debug=True. Check this out 馃樅

live-reload

Here's the code:

import dash
from dash.dependencies import Event, Input, Output
import dash_core_components as dcc
import dash_html_components as html

app = dash.Dash()

# Edit this object!
layout = html.Div([
    html.H1('Hello Dash!'),
    html.H3('Dash with live-reload'),
])

# Barebones layout
app.layout = html.Div([
    dcc.Interval(id='refresh', interval=200),
    html.Div(id='content', className="container")
])

# Update the `content` div with the `layout` object.
# When you save this file, `debug=True` will re-run
# this script, serving the new layout
@app.callback(
    Output('content', 'children'),
    events=[Event('refresh', 'interval')])
def display_layout():
    return layout

app.css.append_css({"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"})

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

oooh nice :)

OK so I tried this with some actual statefulness in the app (sliders etc) and a couple of notes:

  1. I had to add app.config.supress_callback_exceptions=True as per the helpful warning :)
  2. It doesn't seem like this approach preserves/respects the state of the sliders in my little app, which is a large part of the value of hot-reload (although quick-preview of default state is already a big step!) ... am I interpreting this wrong? I'll try to make a toy example.

It doesn't seem like this approach preserves/respects the state of the sliders in my little app, which is a large part of the value of hot-reload (although quick-preview of default state is already a big step!) ... am I interpreting this wrong? I'll try to make a toy example.

Yes, I think that is true. Dang! Almost there. It sounds like there is still precedent for a proper hot-reload.

Here's a stripped-down example that shows the issue... I should add that it blinks/flickers pretty noticeably on my screen, also :) But it's a good start!

# -*- coding: utf-8 -*-
import dash, dash_core_components as dcc, dash_html_components as html
import plotly.graph_objs as go
import math, pandas as pd, numpy as np

data = np.random.normal(0,1,10000)

layout = html.Div([
        dcc.Slider(id='spread', min=0.5, max=5.0, value=2.5, step=0.5),
        dcc.Graph(id='hist'),
])

app = dash.Dash()
app.config.supress_callback_exceptions=True

app.layout = html.Div([
    dcc.Interval(id='refresh', interval=200),
    html.Div(id='content', className="container")
])

@app.callback(
    dash.dependencies.Output('content', 'children'),
    events=[dash.dependencies.Event('refresh', 'interval')])
def display_layout():
    return layout

@app.callback(
    dash.dependencies.Output('hist', 'figure'),
    [dash.dependencies.Input('spread', 'value')])
def update_hist(spread):
    kwargs = dict( opacity=0.75, autobinx=False,
        xbins=dict( start=-10.0, end=10, size=0.1 )
        )
    return dict(
        data = [
            go.Histogram(x=data, **kwargs),
            go.Histogram(x=data*spread, **kwargs)
        ],
        layout = go.Layout(
            barmode='overlay',
            xaxis=dict( range=[-10, 10] ),
            yaxis=dict( range=[0, 500] )
        )
    )

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

Good news! The development for this issue has been sponsored by an organization as part of our "Pleasant and Productive Developer Experience" initiative.

I came across this issue and had an idea on how to implement it.

I thought that flask already reload itself when it detect changes in debug mode.
Let's use that with a hash generated at startup and periodically request the
server for the hash, if it has changed, it's time to reload. Put that request loop code
in a webworker so it won't tamper with the rest of the app.

So I made a quick prototype with flask only, it worked. But I needed to change
the index rendering of dash to use jinja2 so I could put a context and load the
worker only if hot-reload is enabled. This didn't take long and I had a working
prototype of dash with hot-reload.

I now plan to separate the integration of hot-reload into several components which
can be useful in other parts of dash.

  • Dash should use jinja2 for its index. #281

  • Dash static files and templates must be structured so they work from the lib and can be
    customized by the user. #265

I'm curious about the coupling of your solution to Jinja... is there a specific dependency on Jinja or do you just need some kind of access to the index.html, i.e. by overriding Dash's index() function?

Was this page helpful?
0 / 5 - 0 ratings