fix(minian): keep plot titles rendering when the dask dashboard is open - #15
Merged
Merged
Conversation
Partway through the pipeline, every cell that draws a titled HoloViews plot starts failing with `'str' object has no attribute 'update'`, and the kernel never recovers on its own — `visualize_seeds(max_proj, seeds, "mask_pnr")` and everything after it stays broken until a restart. HoloViews builds each figure with a plain-string title (`bokeh.plotting.figure(title=...)`), and Bokeh turns that into a `Title` model only through the coercion registered on `Plot.title` (`.accepts(String, lambda text: Title(text=text))`). That coercion lives inside the validation branch of `Property.prepare_value`, so when validation is off the figure keeps a raw `str` and the later `plot.title.update(**props)` in `ElementPlot._update_title` raises. Validation is gated by `Property._should_validate`, a process-global that Bokeh's `validate()` saves and restores with no lock and no thread-locality. This notebook runs two independent togglers in one process: HoloViews itself, which wraps every datasource update in `validate(False)`, and the in-process dask scheduler's dashboard, whose callbacks are decorated with `@without_property_validation` and run on its own IO loop thread. When the two scopes overlap, the second captures the first's temporary `False` as its "old" value and restores that on exit, latching validation off for the rest of the kernel. That latch is why the failure is permanent rather than intermittent. Re-do the coercion in `_update_title` so rendering no longer depends on the flag. Verified against a thread toggling the flag the way the dashboard does: 135/150 renders raised before, 0/150 after, titles intact. Re-running the cell is a no-op rather than stacking another wrapper, and a kernel that has already latched the flag off renders correctly once the cell runs. `notebooks/pipeline/pipeline.ipynb` and `notebooks/pipeline_groundtruth/pipeline_groundtruth.ipynb` share the exposure (in-process `LocalCluster` plus titled plots); this change is deliberately scoped to the workshop notebook. The root cause belongs upstream in Bokeh. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
Partway through
tutorials/minian/pipeline_no_deconv.ipynb, every cell that draws a titled HoloViews plot starts failing, and the kernel never recovers on its own. Reported from a live run atvisualize_seeds(max_proj, seeds, "mask_pnr"):Everything after that point stays broken until the kernel is restarted, which mid-workshop means re-running the whole pipeline.
Root cause
HoloViews builds each figure with a plain-string title (
bokeh.plotting.figure(title=...),element.py:1218). Bokeh converts that into aTitlemodel only through the coercion registered onPlot.title:That coercion lives inside the validation branch of
Property.prepare_value, so when validation is off the figure keeps a rawstr, and the laterplot.title.update(**props)raises.Validation is gated by
Property._should_validate— a process-global that Bokeh'svalidate()saves and restores with no lock and no thread-locality. This notebook runs two independent togglers in one process:validate(False)(holoviews/plotting/bokeh/plot.py:223), which theVArrayViewerwidgets drive continuously.@without_property_validationand runs them on its own IO loop thread.When the two scopes overlap, the second captures the first's temporary
Falseas itsoldvalue and restores that on exit:That latch is why the failure is permanent rather than intermittent, and why it appears "in the middle" of a run rather than at the first plot.
The fix
One guard cell after the HoloViews setup cell, re-doing the coercion so rendering no longer depends on the flag:
Verification
Executed the cell's source verbatim out of the committed notebook, against a background thread toggling the flag the way the dashboard does:
AttributeErrorAlso confirmed: titles render as real
Titleobjects with the correct text (not just non-crashing); re-running the cell is a no-op rather than stacking another wrapper; and a kernel that has already latched the flag off renders correctly once the cell runs.scripts/validate_notebooks.pypasses on all 18 notebooks.Scope and follow-ups
Deliberately scoped to the workshop notebook.
notebooks/pipeline/pipeline.ipynbandnotebooks/pipeline_groundtruth/pipeline_groundtruth.ipynbshare the exposure (in-processLocalClusterplus titled plots) and are untouched here.For anyone hitting this in a running kernel, it can be unstuck in place without a restart:
The real defect is upstream — Bokeh's
validate()mutating a process-global from multiple threads — and is worth reporting there.🤖 Generated with Claude Code