[BUG] Callbacks still double-register with uvicorn `--reload` when an explicit `name` is passed to `Dash()` (incomplete fix for #3818 / #3883)
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 55/100
Direzione di ricerca
Inizia in dash/_utils.py, all’interno di alias_main_module, e riproduci il problema con l’app fornita con nome esplicito e la configurazione di reload di uvicorn. Traccia il modo in cui il worker di reload importa il server e registra i callback; il lavoro è completato quando l’output delle dipendenze contiene ogni callback una sola volta per i nomi espliciti e i pattern app-factory, senza compromettere il comportamento esistente di main.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Summary
Thanks for the fix! This is a follow up issue from #3818 . PR #3883 (released in 4.4.1) fixes the double-registration reported in #3818 but only for the exact idiom app = Dash(__name__) written at the top level of the entry-point file.
The fix (alias_main_module) keys entirely on the app's resolved name being "__main__" or "__mp_main__". As soon as Dash() receives any other name — a literal string, or Dash(__name__) when the app is built inside a package / app-factory (where __name__ is the module name) — the alias no-ops, the module is executed twice in the reload worker, and every callback is registered twice. The renderer then throws:
Duplicate callback outputs
Output 0 (output.children) is already in use.
Environment
- dash 4.4.1 (
dash[fastapi]) - uvicorn (standard), reload=True
- Python 3.10, Linux
Minimal reproduction
# repro.py
from dash import Dash, html, dcc, callback, Output, Input
# BUG TRIGGER: any explicit name that is not "__main__"/"__mp_main__".
# Using Dash(__name__) here (top level) works; Dash("myapp") does not.
app = Dash("myapp", backend="fastapi")
app.layout = html.Div([dcc.Input(id="input", value="Hello"), html.Div(id="output")])
@callback(Output("output", "children"), Input("input", "value"))
def update(value):
return f"You typed: {value}"
app.enable_dev_tools(debug=True, dev_tools_hot_reload=True)
server = app.server
if __name__ == "__main__":
import uvicorn
uvicorn.run("repro:server", host="127.0.0.1", port=8050, reload=True)
python3 repro.py
curl -s http://127.0.0.1:8050/_dash-dependencies | grep -o '"output.children"' | wc -l
# -> 2 (expected 1)
Open the page in a browser and the console shows Duplicate callback outputs.
Constructing the app inside a package with Dash(__name__) also reproduces it, because __name__ is the package name, not __main__, in this case -- __name__ == "app_factory":
- app_factory.py:
from dash import Dash, html, dcc, callback, Output, Input
class AppBuilder:
def __init__(self):
self.app = Dash(__name__, backend="fastapi")
self.app.layout = html.Div([
html.H1("Reload double-register demo (factory)"),
dcc.Input(id="input", value="Hello"),
html.Div(id="output"),
])
@callback(Output("output", "children"), Input("input", "value"))
def update(value):
return f"You typed: {value}"
def setup(self):
self.app.enable_dev_tools(debug=True, dev_tools_hot_reload=True)
return self.app.server
- main.py:
if __name__ != "__main__":
from app_factory import AppBuilder
server = AppBuilder().setup()
if __name__ == "__main__":
import uvicorn
uvicorn.run("main:server", host="127.0.0.1", port=8050, reload=True)
Root Vause
dash/_utils.py:
def alias_main_module(caller_name: str) -> None:
if caller_name not in ("__main__", "__mp_main__"):
return
...
alias_main_module only aliases the running module into sys.modules when caller_name is exactly "__main__"/"__mp_main__". Passing an explicit name (or building the app anywhere other than the entry module's top level) makes caller_name something else, so the alias is skipped, the "main:server" import string re-executes the file, and the callbacks register a second time.
This means the fix effectively only covers the single-file app = Dash(__name__) tutorial layout, and misses the very common app-factory / class-based / explicitly-named app patterns.
Current Workaround
# Skip the reload worker's __mp_main__ spawn re-exec so callbacks are registered exactly once.
if __name__ not in ("__main__", "__mp_main__"):
server = build_app()
Works temporarily but idk if that will break in any future releases.
- 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 ·