Skip to content

Stop ES modules from waiting forever on modules that never load - #83

Merged
maartenbreddels merged 3 commits into
masterfrom
fix/module-dependencies
Oct 5, 2026
Merged

maartenbreddels merged 3 commits into
masterfrom
fix/module-dependencies

Conversation

@maartenbreddels

@maartenbreddels maartenbreddels commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Stops ES modules and widgets from waiting forever for modules that never load, and adds dependencies= to define_module, as in ipyvue.

Problem

define_module makes each module wait for every module name defined earlier in the Python process, and each widget waits for all of them too.
That list never forgets a name, so three cases hang with no error:

  • A module's widget is closed. After a page reload, every later module and widget waits for a module that no widget provides.
  • A cell that defines modules a and b runs again. The second a waits for b and the second b waits for a; after a page reload both wait forever.
  • A module's code changes while a widget already waits for it. The pending waiter is dropped, so that widget never renders. Two loads of one name can also finish in the wrong order, and the old code wins.

App authors also cannot say what a module really needs. The same fixes were reviewed in ipyvue (#106, #108).

Change

  • define_module(name, module=None, *, code=None, url=None, dependencies=None): an explicit list is used as given; without it the default is unchanged except that it skips modules whose widget is closed.
  • Defining a name again updates its live widget instead of creating a second one, and a name keeps the dependency list it was first defined with (as Solara's server version does).
  • A widget's default _dependencies also skips closed modules.
  • JS: a reload keeps pending waiters, so they get the new module; a per-name counter drops a load that a newer load replaced.
  • JS: a widget whose _module is a Module widget's name takes the module from the registry instead of importShim(name). ipyvue 3.0.0 (pulled in by solara 1.64) sets esmsInitOptions without mapOverrides, so the shared es-module-shims rejects a remap and a recreated module kept serving the old code. This made test_module_recreated_widget_hot_reload fail on CI for master too, not only for this PR.

Validation

  • tests/unit/define_module_test.py: new tests for explicit dependencies, closed modules skipped (modules and widgets), and redefinition. 9 passed; the new ones fail on the old code.
  • tests/ui/module_hot_reload_test.py::test_module_code_change_while_pending: a widget waits for a module whose code changes before its dependency arrives, and must show the new code. It passes with the fix and times out on master.
  • npm run build:prod passes.

Gaps

  • Imports between modules still go through the import map, so with ipyvue 3.0.0 on the page a module that imports a recreated module can still get the old version. ipyvue #106 sets mapOverrides and fixes that once released.
  • Only Python 3.11 was run locally; CI covers the rest.
  • On the Solara server, Solara replaces define_module; widgetti/solara#1244 adds dependencies= there.

Align results

Caution

/align was not run on this change: the interface (ipyreact parity with ipyvue, explicit dependencies= overrides the default) was settled with the maintainer in the ipyvue review session.

Crossreview results

  • Rounds: 1. Reviewers: astra and sol, on 30a25b4. Both APPROVE with no findings.
  • No CRITICAL or HIGH finding.
  • Neither reviewer could run the tests in its read-only sandbox; the unit and UI tests above ran locally.
  • Small fixes: 30a25b4 (widgets' default _dependencies skip closed modules) was added by the driver before review and is covered by a unit test. 2ec94ea (consumers take a Module widget's name from the registry, 6 lines) was added after review to fix the CI failure above; the driver confirmed it, and tests/ui/module_hot_reload_test.py passes in CI.

🤖 Generated with Claude Code

maartenbreddels and others added 3 commits October 5, 2026 16:02
A module waited for every earlier name, also for names whose widget was closed, and a redefinition picked up the names defined after it, so re-running a cell made a dependency cycle. In the browser, a code change during a pending load dropped the resolver that consumers were waiting on, and a slow stale load could overwrite newer code.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A widget's default _dependencies listed every module name, so after a page
reload it waited forever for a module whose widget was closed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…rides

ipyvue 3 (pulled in by solara 1.64) sets esmsInitOptions without mapOverrides, so the shared es-module-shims keeps the first url for a module name and consumers of a recreated or updated Module kept importing the old code. Consumers of a Module widget's name now take the module from the registry, which every load updates.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@maartenbreddels
maartenbreddels marked this pull request as ready for review October 5, 2026 15:19
@maartenbreddels
maartenbreddels merged commit e1c3fbe into master Oct 5, 2026
12 checks passed
@maartenbreddels
maartenbreddels deleted the fix/module-dependencies branch October 5, 2026 15:19
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.

1 participant