Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

[BUG] Callbacks still double-register with uvicorn `--reload` when an explicit `name` is passed to `Dash()` (incomplete fix for #3818 / #3883)

Aperta
#3,912 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
4/5
Tempo stimato
3-5 giorni
Idoneità per principianti
55/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Tranquilla
Stack tecnologico
fastapi, python
Ambito
backend, devtools

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

enhancement P3 size: 10+

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

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di plotly/dash

Tutte le issue di plotly/dash

Issue simili

Altre issue su Python

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.