Skip to content

Odd behavior with reactive inputs/outputs and reading/writing values from file #126

@jwhendy

Description

@jwhendy

I re-created an odd issue I just ran into. Here's my reproducible example:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import dash
import dash_core_components as dcc
import dash_html_components as html


def generate_inputs():

    print('getting value!')
    f = open('./value.txt', 'r')
    current_value = str(f.read())
    f.close()

    print('current_value: ' + str(current_value))

    input_box = [html.Label(html.Strong('value to write; currently: ')),
                dcc.Input(type='number', value=current_value, id='input_value')]

    return input_box


app = dash.Dash()

app.layout = html.Div([
    html.H2('debug: getting and setting'),

    html.Div(
         generate_inputs()
    ),

    html.Div(id='file_value_written')

]) # app.layout


@app.callback(
    dash.dependencies.Output(component_id='file_value_written', component_property='children'),
    [dash.dependencies.Input(component_id='input_value', component_property='value')])
def update_file_value(value_to_write):

    print('set value!')
    print('value_to_write: ' + str(value_to_write))
    f = open('./value.txt', 'w')
    f.write(str(value_to_write))
    f.close()
    
    return value_to_write


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

I walked through my process step by step as well:

| step | description                     | printed                              |   cat | gui   |        |
|      |                                 |                                      |       | box   | output |
|------+---------------------------------+--------------------------------------+-------+-------+--------|
|    0 | touch value.txt                 | -                                    |     - | -     | -      |
|    1 | echo "1" > value.txt            | -                                    |     1 | -     | -      |
|    2 | python debug-get-set.py         | getting value! current_value: 1 (2x) |     1 | -     | -      |
|    3 | open localhost:8050             | set value! value_to_write: 1         |     1 | 1     | 1      |
|    4 | click in box; delete 1          | set value! written value: empty      | empty | blank | blank  |
|    5 | type 2 in box                   | set value! written value: 2          |     2 | 2     | 2      |
|    6 | click to remove cursor from box | no message                           |     2 | 2     | 2      |
|    6 | Ctrl+R; reload page             | set value! value_to_write: 1         |     1 | 1     | 1      |

This seems odd to me, but I'm very new to dash, so please let me know where my thinking is awry. Throughout the changes, we can check four key values:

  • the value read from the file, stored as current_value
  • the dcc.Input default value, passed as value=current_value, to be shown as the prompt in the text input box
  • the value passed to the callback and written to the file, value_to_write, which we call if our dcc.Input value changes (not sure how dash checks for a change)
  • the return from the callback, put on the page with html.Div(id='file_value_written')

Upon load, they all agree on the value being 1. It's printed, the file contains it, and it's displayed in the box and below the box. We change the box contents to 2, and the effect pushes through to the html.Div on the screen and the file (checked with cat). It's also still in the box, obviously.

When we reload we're told we're setting the file contents, but that should only change if we're in callback. That only happens if the current text box changes, and we didn't touch it. It almost behaves like the value currently in the box might be cached (or in some sense not really changed), and even when we delete/replace it with 2, the cached/stored value is 1. On refresh, things work in reverse: 2 is treated as the last value seen and the cached value, 1, shows up as a user change and is written. Is that a crazy interpretation?

What I expected: reloading would re-run things top to bottom, it should read 2 from the file, use 2 as the default value in the box, and wait for a callback trigger if the user changes what's in the box 2.

This was a wild chase to just work through in my head and I am coming up clueless. Thanks for taking a look!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions