✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
🧬 Learn how to build RNA-Seq data apps with Python & Dash. Register for the May 20 Webinar!

Dash-extensions 0.0.45 to 0.0.47 - GroupTransform to MultiplexerTransform and Inputs trigger

Could you try constructing your app like this,

app = DashProxy(transforms=[MultiplexerTransform(proxy_location=None)])

i.e. add the proxy_location=None argument?

Just did, and I’m back to having the flags “prevent_initial_call=True” being ineffective.

Could you post an mwe? My test example looks like this,

import time
import dash_html_components as html
import dash_core_components as dcc
from dash_extensions.enrich import Output, DashProxy, Input, MultiplexerTransform

app = DashProxy(transforms=[MultiplexerTransform(proxy_location=None)])
app.layout = html.Div([html.Button("left", id="left", n_clicks=0),
                       html.Button("right", id="right", n_clicks=0),
                       html.Div("Initial value", id="log")])


@app.callback(Output("log", "children"), Input("left", "n_clicks"))
def left(n_clicks):
    time.sleep(1)  
    return f"left ({n_clicks})"


@app.callback(Output("log", "children"), Input("right", "n_clicks"), prevent_initial_call=True)
def right(n_clicks):
    return f"right ({n_clicks})"


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

and it works as intended on the 0.0.49 release.

EDIT: For some context, under the hood, proxy components (dcc.Store) are used, and the proxy_location keyword argument determines where these proxies are placed in the app layout. The default value “inplace” means that the original component is replace by a Div element that wraps the original component and its proxies. The benefit of this approach is that dcc.Loading will work, but it also means that if you replace the layout of a component higher in the tree in a callback, you might end up deleting the proxies (!), which will break your app. For this particular case, you can set proxy_location to a custom component (or None to use the layout root), to place the proxies here instead. To use dcc.Loading for this particular case, the proxy_location must be wrapped by dcc.Loading instead.

I see, I call my app.layout via method, so I was able to reproduce it using your example:

def serve_layout():
    html.Div([html.Button("left", id="left", n_clicks=0),
              html.Button("right", id="right", n_clicks=0),
              html.Div("Initial value", id="log")])


app.layout = serve_layout

Which is something I had to do for my deployment.
Edit: no, I was not able to reproduce it with this example. For some reason the above fails when I use it in your example.

I have moved all my dcc.Store components to the serve_layout() method, but the prevent_initial_call still have no effect, whether I set MultiplexerTransform proxy_location argument to None or nothing. By setting it to a custom location, or wrapped by dcc.Loading, do you mean having something like this:?

proxy_location=dcc.Loading(id=‘custom’)

No, i mean something like this,

proxy_location = html.Div()  # proxy elements will be put inside this Div
app = DashProxy(transforms=[MultiplexerTransform(proxy_location=proxy_location)])
app.layout = html.Div([html.Button("left", id="left", n_clicks=0),
                       html.Button("right", id="right", n_clicks=0),
                       html.Div("Initial value", id="log"),
                       proxy_location])

But i don’t think it will make a different as compared to just putting in None. I didn’t understand from your previous post what happened if you ran my example? I tried using function for the layout also, but my example still worked.

Ah, ok, I’m catching up slowly. I did this, and I do get something different, from those problematic callbacks, what I get now is this:


Multiple objects were found for an `Output` of a callback that only takes one value. 
The id spec is {"id":"cached-new-tags","idx":1,"prop":"data"} and the property is `data`. 
The objects we found are: [{"id":{"id":"cached-new-tags","idx":1,"prop":"data"},"property":"data"},{"id":{"id":"cached-new-tags","idx":1,"prop":"data"},"property":"data"}]

I looks like there is some duplication of the dcc.Store component with id “cached-new-tags”, but I can’t find where. I am not sure if that helps. I’m trying to make that MWE.

Edit: Interestingly, every time I refresh the page of my app, the same error message shows up but one more line is added to the multiple objects found.

Is looks like the proxy elements are added multiple times. I can get that behavior by doing this,

proxy_location = html.Div()


def layout():
    return html.Div([html.Button("left", id="left", n_clicks=0),
                     html.Button("right", id="right", n_clicks=0),
                     html.Div("Initial value", id="log"),
                     proxy_location])


app = DashProxy(transforms=[MultiplexerTransform(proxy_location=proxy_location)])
app.layout = layout

The reason is that the proxy_location is now a global variable (which is a no-go i Dash), so the proxy elements are added again each time the layout is rendered. But if i just set proxy_location=None it works as intended.

I see, it might be something around read only global variables, which I still have. They are basically objects that contain all the elements of other tabs in my app. I’m going to rewrite the app so no global variable remains, and see how that goes.

Ah, yes. If you have global variables that hold layout elements that would explain why you would see this kind of issue.

I removed all gobal variables, proxy_location=None still has no effect on the callbacks sharing output. Will try and make that MWE, and hopefully find the issue while doing so :sweat_smile:

Finally, got to reproduce with this small example :

import dash_html_components as html
import dash_core_components as dcc
from dash_extensions.enrich import Output, DashProxy, Input, MultiplexerTransform

app = DashProxy(transforms=[MultiplexerTransform()])

def serve_layout():
    return html.Div([
    dcc.Store(id='store'),
    dcc.Location(id='url'),
    html.Div(id='page-content')])


app.layout = serve_layout


@app.callback(Output('page-content', 'children'),
              [Input('url', 'pathname')])
def display_page(pathname):
    return satellite_layout()


@app.callback([Output("store", "data")], Input("b1", "n_clicks"), prevent_initial_call=True)
def left(n_clicks):
    time.sleep(1)
    return {'k': 'v'}


@app.callback([Output("store", "data")], Input("b2", "n_clicks"), prevent_initial_call=True)
def right(n_clicks):
    return {'k': 'v'}


def satellite_layout():

    layout = html.Div([
        html.Button("Button1", id="b1"),
        html.Button("Button2", id="b2")
    ]
        , style={'padding': 10})
    return layout

if __name__ == '__main__':
    app.run_server(dev_tools_hot_reload=True, debug=True, port=7777)

In my app, I use the Location component to link the layout to a url, which then calls satellite_layout().
Also, I get an error with “ID not found in layout”, but use app.config.suppress_callback_exceptions = True. I imagine there is something not right there(?)

When i run your example, i get the “ID not found” error. But if i change the app definition to,

app = DashProxy(transforms=[MultiplexerTransform()], suppress_callback_exceptions=True)

i.e. add suppress_callback_exceptions=True, the error disappears. AFAIK this is expected behavior when you are using a dynamic layout in this form. I also get an import error for time, which can be solved by importing time,

import time

and i end up with the following code,

import time
import dash_html_components as html
import dash_core_components as dcc
from dash_extensions.enrich import Output, DashProxy, Input, MultiplexerTransform

app = DashProxy(transforms=[MultiplexerTransform()], suppress_callback_exceptions=True)


def serve_layout():
    return html.Div([
        dcc.Store(id='store'),
        dcc.Location(id='url'),
        html.Div(id='page-content')])


app.layout = serve_layout


@app.callback(Output('page-content', 'children'),
              [Input('url', 'pathname')])
def display_page(pathname):
    return satellite_layout()


@app.callback([Output("store", "data")], Input("b1", "n_clicks"), prevent_initial_call=True)
def left(n_clicks):
    time.sleep(1)
    return {'k': 'v'}


@app.callback([Output("store", "data")], Input("b2", "n_clicks"), prevent_initial_call=True)
def right(n_clicks):
    return {'k': 'v'}


def satellite_layout():
    layout = html.Div([
        html.Button("Button1", id="b1"),
        html.Button("Button2", id="b2")
    ]
        , style={'padding': 10})
    return layout


if __name__ == '__main__':
    app.run_server(dev_tools_hot_reload=True, debug=True, port=7777)

which runs without errors. Note that the callbacks are invoked when the components are added to the page, i.e. when you load your satellite layout, no matter if you use dash-extensions or not. Note also that the output should not be a list when there is only one output.

It does run without error, but both callbacks are called at start/refresh despite prevent_initial_call=True, aren’t they called for you?

Version 0.0.49 still has problems in my application

A nonexistent object was used in an Output of a Dash callback. The id of this object is {“id”:“query-table”,“idx”:0,“prop”:“data”} and the property is data. The wildcard ids currently available are logged above.

Does the same happen if you construct the transform like this,

MultiplexerTransform(proxy_location=None)

Is your layout dynamic?

No,the table is pre-defined

I used Dash object, all transformations loaded

from dash_extensions.enrich import Dash
app = Dash(name,prevent_initial_callbacks=True)

Ah, okay. I think I just resolved a bug that might have been affecting you. Could you try upgrading to version 0.0.51? It’s brand new, so you might need to run the pip install command twice,

pip install dash-extensions==0.0.51

it fixed,but back to the previous bug, although prevent_initial_callbacks=True,the callbacks are being triggered

Yes, but I think that is due to Dash itself. I would expect the same to happen if you don’t use the multiplexer