Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

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

未关闭
#3,912 1 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

评估

难度
4/5
预计耗时
3-5 天
新手友好度
55/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
冷清
技术栈
fastapi, python
领域
backend, devtools

调研方向

从 dash/_utils.py 中的 alias_main_module 开始,使用提供的显式名称 app 和 uvicorn reload 配置复现该问题。跟踪 reload worker 如何导入 server 并注册 callbacks;完成标准是:对于显式名称和 app-factory 模式,dependency output 中每个 callback 都只出现一次,同时不破坏现有的 main 行为。

由索引模型根据 Issue 内容生成。

描述

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.

主要语言
Python
星标
24.4k
派生
2.3k
平均合并
1 天 21 小时
30 天内合并 PR
19

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

plotly/dash 的其他 Issue

查看 plotly/dash 的全部 Issue

相似的 Issue

更多 Python Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。