Skip to content

Fix callbacks registered twice when app/app.py runs as a script - #4023

Open
jamalkamaladdin wants to merge 1 commit into
plotly:devfrom
jamalkamaladdin:fix/4011-alias-same-name-script
Open

jamalkamaladdin wants to merge 1 commit into
plotly:devfrom
jamalkamaladdin:fix/4011-alias-same-name-script

Conversation

@jamalkamaladdin

Copy link
Copy Markdown

Fixes #4011.

dash/_utils.py: alias_main_module returns early when the first part of a dotted import name is not a package.
tests/unit/test_main_module_alias.py: new case for a script inside a folder with its own name.
CHANGELOG.md: new Fixed entry for #4011.

Contributor Checklist

  • I have broken down my PR scope into the following TODO tasks
    • Skip the alias in alias_main_module for a dotted name whose first part is not a package
    • Add a regression test for python app/app.py
  • I have run the tests locally and they passed. (refer to testing section in contributing)
  • I have added tests, or extended existing tests, to cover any new features or bugs fixed in this PR

optionals

  • I have added entry in the CHANGELOG.md
  • If this PR needs a follow-up in dash docs, community thread, I have mentioned the relevant URLS as follows
    • this GitHub #PR number updates the dash docs
    • here is the show and tell thread in Plotly Dash community

Running `python app/app.py` from the parent directory gives the import
name `app.app`, but only `app/` is on sys.path, so find_spec imported
the parent `app` from the running script and executed it a second
time. Every page callback was then registered twice.

Check the top-level name first and skip the alias when it does not
resolve to a package. Fixes plotly#4011.
@sonarqubecloud

Copy link
Copy Markdown

@T4rk1n T4rk1n left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix is good. Checking only the top-level name with find_spec doesn't import anything, so the running script can no longer be pulled in as its own parent. I confirmed python app/app.py now executes once, and pkg/app.py under uvicorn reload still aliases with and without an __init__.py. It also fixes a case the PR doesn't mention: python app/main.py with a separate app/app.py next to it used to execute that app.py as the parent package. One small ask on the test below; not blocking.

SAME_NAME = "dash_test_alias_same"


def test_no_reexecution_when_script_dir_shares_script_name(tmp_path, monkeypatch):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a case for python app/main.py where app/app.py sits next to it? Before this change, find_spec("app.main") imported that other app.py as the parent package, so any Dash app or callbacks in it got registered too. The fix handles it, but nothing guards it. It's worth a line in the CHANGELOG entry too, since that entry only describes the same-name case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dash 4.4.1: alias_main_module() causes double callback registration when app entrypoint is run as python <subdir>/<script>.py from a parent directory

2 participants