Regression with pyodide and hold
ALL software version info
Software Version Info
bleach 6.2.0
bokeh 3.7.3
certifi 2025.8.3
charset-normalizer 3.4.3
colorama 0.4.6
contourpy 1.3.2
idna 3.10
Jinja2 3.1.6
linkify-it-py 2.0.3
Markdown 3.9
markdown-it-py 4.0.0
MarkupSafe 3.0.3
mdit-py-plugins 0.5.0
mdurl 0.1.2
narwhals 2.6.0
numpy 2.2.6
packaging 25.0
pandas 2.3.3
panel 1.7.2
param 2.2.1
pillow 11.3.0
pip 25.2
python-dateutil 2.9.0.post0
pytz 2025.2
pyviz_comms 3.0.6
PyYAML 6.0.3
requests 2.32.5
setuptools 65.5.0
six 1.17.0
tornado 6.5.2
tqdm 4.67.1
typing_extensions 4.15.0
tzdata 2025.2
uc-micro-py 1.0.3
urllib3 2.5.0
webencodings 0.5.1
xyzservices 2025.4.0
Description of expected behavior and the observed behavior
When the example is run with panel serve the behavior is as expected.
However, when the example is run in pyodide (with panel 1.7.2 and above), the change of "Number of circuits" as no effect while with panel 1.7.1, the other parameter is displayed depending on the value of "Number of circuits".
Complete, minimal, self-contained example code that reproduces the issue
When the following code is run with panel serve there is no issue with version 1.7.1 or 1.7.2. However when it is converted with pyodide with panel convert index.py and served with python -m http.server, the change of the "Number of circuits" has no effect.
import panel as pn
import param
from panel.viewable import Viewable
class InstallationMethod(pn.viewable.Viewer):
k15_number_of_circuit: int = param.Selector(
label="Number of circuits", objects=list(range(1, 13))
) # type:ignore
k15_spacing: float = param.Number(
label="Spacing", default=0, bounds=(0, 1.0)
) # type:ignore
@pn.io.hold()
@param.depends(
"k15_number_of_circuit",
watch=True,
on_init=True,
)
def _on_method_change(self):
self.param.k15_spacing.constant = self.k15_number_of_circuit == 1
def __panel__(self) -> Viewable:
return pn.Param(
self.param,
widgets={
"k15_spacing": {
"type": pn.widgets.Select,
"options": {f"{v} m": v for v in [0, 0.25, 0.5, 1.0]},
},
},
show_name=False,
hide_constant=True,
)
a = InstallationMethod()
pn.Column(a).servable()
After some search it seems that the issue is due to the commit #701e698bbcc01ecee1e837d578ead89975e9b54d
By reading the commit, I noticed that it is related with @pn.io.hold() which is used in my code. When I remove it, the code is working as expected. However, I my complete code, I update more than one parameter.
- [ ] I may be interested in making a pull request to address this
Unfortunately, the fix is not working when the component is within a template. With panel 1.8.2, I get the same issue with the following code
import panel as pn
import param
from panel.viewable import Viewable
class InstallationMethod(pn.viewable.Viewer):
k15_number_of_circuit: int = param.Selector(
label="Number of circuits", objects=list(range(1, 13))
) # type:ignore
k15_spacing: float = param.Number(
label="Spacing", default=0, bounds=(0, 1.0)
) # type:ignore
@pn.io.hold()
@param.depends(
"k15_number_of_circuit",
watch=True,
on_init=True,
)
def _on_method_change(self):
self.param.k15_spacing.constant = self.k15_number_of_circuit == 1
def __panel__(self) -> Viewable:
return pn.Param(
self.param,
widgets={
"k15_spacing": {
"type": pn.widgets.Select,
"options": {f"{v} m": v for v in [0, 0.25, 0.5, 1.0]},
},
},
show_name=False,
hide_constant=True,
)
a = InstallationMethod()
template = pn.template.VanillaTemplate(title="Bootstrap Template")
template.main.append(a)
template.servable()
On the console from Edge, I get:
bokeh-3.8.0.min.js:186 [bokeh 3.8.0] setting log level to: 'info'
bokeh-3.8.0.min.js:166 [bokeh 3.8.0] document idle at 93 ms
pyodide.asm.js:8 Loading micropip
pyodide.asm.js:8 Loaded micropip
pyodide.asm.js:8 Loading Jinja2, MarkupSafe, Pillow, bleach, certifi, charset-normalizer, contourpy, idna, narwhals, numpy, packaging, pandas, pyodide-http, python-dateutil, pytz, pyyaml, requests, six, tqdm, typing-extensions, urllib3, webencodings, xyzservices
pyodide.asm.js:8 Loaded Jinja2, MarkupSafe, Pillow, bleach, certifi, charset-normalizer, contourpy, idna, narwhals, numpy, packaging, pandas, pyodide-http, python-dateutil, pytz, pyyaml, requests, six, tqdm, typing-extensions, urllib3, webencodings, xyzservices
bokeh-3.8.0.min.js:166 [bokeh 3.8.0] document idle at 72 ms
pyodide.asm.js:8 Error raised while processing Document events: Error: reference 81a114f1-d2f7-4980-aa3b-86d7a39b09e5 isn't known
And the error is trigger by https://github.com/holoviz/panel/blob/fb16b07b6c8797a883d3072c57e4dbdce1a14e5b/panel/io/pyodide.py#L393
And if I change the order of https://github.com/holoviz/panel/blob/fb16b07b6c8797a883d3072c57e4dbdce1a14e5b/panel/io/pyodide.py#L393-L396 to
pydoc.on_event('document_ready', functools.partial(state._schedule_on_load, pydoc))
state._loaded[pydoc] = state._connected[pydoc] = True
pydoc.unhold()
pydoc.callbacks.trigger_event(DocumentReady())
It is working but I still have some error and the last line is not called
After digging, a little more. I noticed the following behavior:
- when the line https://github.com/holoviz/panel/blob/fb16b07b6c8797a883d3072c57e4dbdce1a14e5b/panel/io/pyodide.py#L393, is called, an event an nonexistent reference trigger an error which prevent the other part of the try block to run. Thus the fix in #8235 is not called and cannot work.
- The list of event that will be triggered during
pydoc.unhold()are stored inpydoc.callbacks._held_eventsWhen using the the template the content ofpydoc.callbacks._held_eventsis:
[<bokeh.document.events.RootAddedEvent object at 0x495e770>,
<bokeh.document.events.TitleChangedEvent object at 0x48adbc0>,
<bokeh.document.events.RootAddedEvent object at 0x4914658>,
<bokeh.document.events.RootAddedEvent object at 0x4baed50>,
<bokeh.document.events.RootAddedEvent object at 0x4a3bff8>,
<bokeh.document.events.RootAddedEvent object at 0x4851980>,
<bokeh.document.events.RootAddedEvent object at 0x48518b0>,
<bokeh.document.events.ModelChangedEvent object at 0x4b10020>,
<bokeh.document.events.ModelChangedEvent object at 0x4ad7ac8>,
<bokeh.document.events.ModelChangedEvent object at 0x480ff78>,
<bokeh.document.events.ModelChangedEvent object at 0x48ff170>,
<bokeh.document.events.ModelChangedEvent object at 0x4839390>,
<bokeh.document.events.ModelChangedEvent object at 0x4ad82b0>,
<bokeh.document.events.ModelChangedEvent object at 0x43f8548>]
If before the unhold call, I removed only the 1st ModelChangedEvent event with
pydoc.callbacks._held_events = pydoc.callbacks._held_events[:7] + pydoc.callbacks._held_events[8:]
I do not have any error and the code is working.
In my code, this ModelChangedEvent is due to a stylesheets and refer to a Column however, I did not have time to find where this event is created.
Thanks for the detailed investigation @etihwo.