Skip to content

Make components always truthy - #4078

Open
monsterlag wants to merge 1 commit into
plotly:devfrom
monsterlag:fix/issue-2906-4000
Open

monsterlag wants to merge 1 commit into
plotly:devfrom
monsterlag:fix/issue-2906-4000

Conversation

@monsterlag

Copy link
Copy Markdown

Note

AI-generated, not reviewed by a human. This pull request was written by an AI coding agent and then checked by a separate AI review agent, which re-ran the tests described below. No human has reviewed the code. It is submitted by @monsterlag, who runs these agents. If that does not fit how you want to receive contributions, please feel free to close it.

Component defines __len__ (the number of items in its children tree) but not __bool__, so Python falls back to __len__ for truthiness. Any component without children is falsy: bool(html.Div()) is False, and bool(dash_table.DataTable(data=[...])) is always False because DataTable has no children prop. That breaks the usual component or fallback and if component: patterns.

This adds Component.__bool__ that always returns True, as suggested in the issue. len() and iteration are unchanged.

One behavior change to note: in _setup_server, if not self.layout and self.use_pages: no longer replaces an explicitly assigned empty layout (for example app.layout = html.Div()) with page_container. A layout that was never set (None) is still replaced as before.

Fixes #2906

Contributor Checklist

  • I have broken down my PR scope into the following TODO tasks (done by the AI agents, not by a human)
    • Add Component.__bool__ returning True
    • Add a unit test
  • 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 (done by the AI agents, not by a human)

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

How tested

Python 3.11 on Linux, pip install -e .[ci,dev,testing,celery,diskcache]. I used the built dcc, html, dash_table, deps and renderer build files from the 4.5.0rc1 wheel instead of running npm run build.

  • New test test_debc031_bool_is_always_true in tests/unit/development/test_base_component.py: pytest tests/unit/development/test_base_component.py -k debc031 fails before the change (assert bool(Component())) and passes after it.
  • pytest tests/unit/development/test_base_component.py: 31 passed.
  • pytest tests/unit: 723 passed, 5 failed, 2 errors with the change. Without it: 722 passed, with the same 5 failures and 2 errors. The failures are environment-only: test_browser_smoke (no browser available) and test_main_module_alias.
  • A small app, driven with the Flask test client, had a callback that returns dash_table.DataTable(...) or "no table". Before the change it returned "no table". After it, it returned the DataTable.
  • black --check and flake8 on both changed files passed, and pylint on base_component.py gave 10.00/10.

I did not run a dash_duo browser test because no Chrome is available in my environment. I left out a CHANGELOG entry because it needs the PR number. I can add one under Fixed.

The same truthiness change also affects the validation_layout checks (dash.py around lines 1030 and 1236): a validation_layout set to a childless component is now treated as set rather than overwritten. With use_pages, an explicitly empty layout now reaches Dash's existing "dash.page_container not found in the layout" error instead of being silently replaced. The 2 errors in the unit run (test_app_runners.py smoke tests) are environmental and identical on the base branch.

Component defined __len__ but not __bool__, so truthiness fell back to
the number of children and a component without children (or without a
children prop, like DataTable) was falsy. Fixes plotly#2906.
@sonarqubecloud

Copy link
Copy Markdown

@KoolADE85

Copy link
Copy Markdown
Contributor

Thanks for the PR! We'll take a closer look and follow up.

@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.

This breaks pages apps that use an empty placeholder layout, so I'd block on it as is.

dash.py:1894 does if not self.layout and self.use_pages: self.layout = page_container. Today app.layout = html.Div() with use_pages=True gets swapped for page_container. With __bool__ always True, the empty Div is kept. Then every request fails with "dash.page_container not found in the layout". The app still starts, but / and every other page return 500. I reproduced it with dash_duo: the page loads on dev and never loads with this change.

Can you keep the old behavior at that check? For example self.layout is None or (isinstance(self.layout, Component) and not len(self.layout)). And can you add an integration test for the pages case?

This also changes public behavior: user code that does if component: to mean "has children" silently flips. Please add a CHANGELOG entry that says so.

for t in self._traverse_ids():
yield t.id # type: ignore[reportAttributeAccessIssue]

def __bool__(self):

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.

This also changes the result of if not self.layout in dash.py:1894. An empty layout with use_pages=True is no longer replaced with page_container, and every request then errors. Can you update that check so an empty component still counts as unset there?

dcc.Input(children="invalid children")


def test_debc031_bool_is_always_true():

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.

This covers bool() itself but not the case that breaks. Can you add a dash_duo test under tests/integration/multi_page/: use_pages=True, app.layout = html.Div(), check that the home page renders? It fails with the current change, so it guards the fix.

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.

bool(DashTable) is always False

3 participants