[BUG] @callbacks not being registered when @app.callback and @callback with duplicate outputs are used
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 45/100
Direzione di ricerca
Inizia eseguendo la riproduzione fornita di Dash 3.2.0 e confronta la registrazione dei callback quando si usano @callback e @app.callback con output duplicati e prevent_initial_call=True. Traccia il percorso di registrazione che produce il comportamento segnalato, quindi verifica che il caso con output duplicati sollevi DuplicateCallback mentre i callback non correlati continuano a funzionare.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
I'm aware that this is an edge case, but it's something we believe should be documented (and eventually fixed) in case someone runs into a similar issue.
The general recommendation would be to choose between @app.callback and @callback to use consistently across the app, even though combining them sometimes works, like in the following case:
@callback(
Output("div1", "children", allow_duplicate=True),
Input("b1", "n_clicks"),
prevent_initial_call=True
)
...
@app.callback(
Output("div1", "children"),
Input("b2", "n_clicks"),
prevent_initial_call=True
)
Description of the issue
If we have something like this in the code:
@app.callback(
Output("div1", "children"),
Input("b1", "n_clicks"),
prevent_initial_call=True
)
...
@callback(
Output("div1", "children"),
Input("b2", "n_clicks"),
prevent_initial_call=True
)
any callback defined with @callback will not work. Every callback defined with @app.callback will still work.
The code for the video app is at the end of this message.
https://github.com/user-attachments/assets/39899de0-8fdd-472e-8f7a-20789105774c
Defining duplicate callbacks like this is wrong according to the documentation. However, instead of this "all @callbacks not working" behaviour, we should see a "duplicate output warning" (see next section for more details).
Variations of the issue:
- The order in which the
@app.callbackand@callbackcallbacks are defined for the duplicate output doesn't affect the occurrence of the issue (it happens either way). - If
prevent_initial_call=Trueis not specified for (at least)@app.callback(the one with the duplicate output), the expected error will be raised:
dash.exceptions.DuplicateCallback: The callback `div1.children` provided with `dash.callback` was already assigned with `app.callback`.
- However, if
prevent_initial_call=Trueis specified for@app.callbackand not for@callback(the ones for the duplicate outputs), the issue will still occur, i.e. no error will be raised.
The last two points mean that:
- This will raise the expected error/exception:
@app.callback(
Output("div1", "children"),
Input("b1", "n_clicks"),
)
...
@callback(
Output("div1", "children"),
Input("b2", "n_clicks"),
prevent_initial_call=True
)
- This will cause the issue - no error will be raised and other callbacks defined with
@callbackwill not work.
@app.callback(
Output("div1", "children"),
Input("b1", "n_clicks"),
prevent_initial_call=True
)
...
@callback(
Output("div1", "children"),
Input("b2", "n_clicks"),
)
If both callbacks are defined with @callback or @app.callback, the (expected) error appears in the debug menu. The app is loaded but no callback (not even the ones defined with @app.callback work:
https://github.com/user-attachments/assets/7d98a477-ae3a-4fd6-aa28-593341f5d450
If the callbacks are defined as the docs specify, with prevent_initial_call=True and allow_duplicate=True, like this, every callback will work (no matter if they are defined with @callback or @app.callback`):
@app.callback(
Output("div1", "children", allow_duplicate=True),
Input("b1", "n_clicks"),
prevent_initial_call=True
)
...
@callback(
Output("div1", "children"),
Input("b2", "n_clicks"),
prevent_initial_call=True
)
Expected behavior
Any case where callbacks with duplicate outputs are defined incorrectly should raise the below exception or an error in the debug menu:
dash.exceptions.DuplicateCallback: The callback `div1.children` provided with `dash.callback` was already assigned with `app.callback`.
So these two cases (and potentially more) should raise the exception:
# CASE 1: prevent_initial_call in app.callback and callback
@app.callback(
Output("div1", "children"),
Input("b1", "n_clicks"),
prevent_initial_call=True
)
...
@callback(
Output("div1", "children"),
Input("b2", "n_clicks"),
prevent_initial_call=True
)
# CASE 2: only prevent_initial_call in app.callback
@app.callback(
Output("div1", "children"),
Input("b1", "n_clicks"),
prevent_initial_call=True
)
...
@callback(
Output("div1", "children"),
Input("b2", "n_clicks"),
)
Code to reproduce the issue
dash==3.2.0
import dash
from dash import dcc, html, Input, Output, State, callback, ctx
app = dash.Dash(__name__)
app.layout = html.Div([
html.H3("callback 1: @callback, output=div1 - without duplicate_output=True"),
html.Button(id="b1", children="Trigger callback 1"),
html.Div(id="div1", children="callbacks 1 or 2 not triggered yet"),
####
html.H3("callback 2: @app.callback, output=div1"),
html.Button(id="b2", children="Trigger callback 2"),
###
html.H3("callback 3: @app.callback, output=div3"),
html.Button(id="b3", children="Trigger callback 3"),
html.Div(id="div3", children="callback 3 not triggered yet"),
####
html.H3("callback 4: @callback, output=div4"),
html.Button(id="b4", children="Trigger callback 4"),
html.Div(id="div4", children="callback 4 not triggered yet"),
])
# this one won't work
@callback(
Output("div1", "children"),
Input("b1", "n_clicks"),
prevent_initial_call=True
)
def update_div1_with_b1(b1):
return f"Callback 1 triggered by {ctx.triggered_id} and {b1} clicks"
# this one will work
@app.callback(
Output("div1", "children"),
Input("b2", "n_clicks"),
prevent_initial_call=True
)
def update_div1_with_b2(b2):
return f"Callback 2 triggered by {ctx.triggered_id} and {b2} clicks"
# this one will work
@app.callback(
Output("div3", "children"),
Input("b3", "n_clicks"),
prevent_initial_call=True
)
def update_div3(b3):
return f"Callback 3 triggered by {ctx.triggered_id} and {b3} clicks"
# this one won't work
@callback(
Output("div4", "children"),
Input("b4", "n_clicks"),
prevent_initial_call=True
)
def update_div4(b4):
return f"Callback 4 triggered by {ctx.triggered_id} and {b4} clicks"
if __name__ == '__main__':
app.run(debug=True)
- Lingua principale
- Python
- Stelle
- 24.4k
- Fork
- 2.3k
- Merge medio
- 1g 21h
- PR unite (30g)
- 19
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di plotly/dash
-
good first issue P3 size: 1 task
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
-
enhancement P2 size: 5
Difficoltà 5/5 Più di una settimana Idoneità per principianti 45/100
-
enhancement P3 size: 10+
-
P2 size: 1 task
-
enhancement P3 size: 1
Difficoltà 3/5 1-2 giorni Idoneità per principianti 72/100
Issue simili
-
essnmx good first issue
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 95/100
-
[Feature] 奇物选择添加优先级 Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
syfoud/Simulated_Scepter#174 ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
Giskard-AI/giskard-oss#2840 · 1 commento ·
-
A claim comment carrying the issue number is silently declined while the workflow reports success Apertaarea: repo bug perceived difficulty: 2
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
yeti-platform/yeti#1380 ·