panel icon indicating copy to clipboard operation
panel copied to clipboard

AttributeError when attempting to add PyComponent to column in FastListTemplate main/modal

Open 14lclark opened this issue 10 months ago • 5 comments

ALL software version info

Software Version Info
Python == 3.11.11 (but it also happens on lesser versions, such as 3.10 -- not sure for higher versions))

panel==1.6.0 (but it also happens on lesser versions)
bokeh==3.6.3
param==2.2.0

Description of expected behavior and the observed behavior

In my full project, I am attempting to add a custom PyComponent to a column which I have in the modal of a FastListTemplate. When I append my component and then open the modal, I get the error shown below, and the modal doesn't open (as expected since an error occurred). If I open the modal first and then append, I get the error, and my component shows correctly with some style errors (not sure if that is reproduced in the example below).

I was able to semi-work around this issue by directly inheriting from pn.reactive.Reactive, which implements _process_param_change. Then, I started getting other errors which may be seen by adding in Reactive to the FeatureInput inheritance below. (I already imported it at the top of the example.)

The example I provide below is the minimal example I could get to reproduce the issue.

The expected behavior is to be able to append a PyComponent to these columns and have it work with no errors.

Complete, minimal, self-contained example code that reproduces the issue

This is the FeatureInput example from the PyComponent example page, except I changed it to display in a FastListTemplate main or modal.

Remember to test adding Reactive to the inheritance of FeatureInput as well.

import panel as pn
import param

from panel.widgets.base import WidgetBase
from panel.custom import PyComponent
from panel.reactive import Reactive

class FeatureInput(WidgetBase, PyComponent):
    """
    The `FeatureInput` enables a user to select from a list of features and set their values.
    """

    value = param.Dict(
        doc="The names of the features selected and their set values", allow_None=False
    )

    features = param.Dict(
        doc="The names of the available features and their default values"
    )

    selected_features = param.ListSelector(
        doc="The list of selected features"
    )

    _selected_widgets = param.ClassSelector(
        class_=pn.Column, doc="The widgets used to edit the selected features"
    )

    def __init__(self, **params):
        params["value"] = params.get("value", {})
        params["features"] = params.get("features", {})
        params["selected_features"] = params.get("selected_features", [])

        params["_selected_widgets"] = self.param._selected_widgets.class_()

        super().__init__(**params)

        self._selected_features_widget = pn.widgets.MultiChoice.from_param(
            self.param.selected_features, sizing_mode="stretch_width"
        )

    def __panel__(self):
        return pn.Column(self._selected_features_widget, self._selected_widgets)

    @param.depends("features", watch=True, on_init=True)
    def _reset_selected_features(self):
        selected_features = []
        for feature in self.selected_features.copy():
            if feature in self.features.copy():
                selected_features.append(feature)

        self.param.selected_features.objects = list(self.features)
        self.selected_features = selected_features

    @param.depends("selected_features", watch=True, on_init=True)
    def _handle_selected_features_change(self):
        org_value = self.value

        self._update_selected_widgets(org_value)
        self._update_value()

    def _update_value(self, *args):  # pylint: disable=unused-argument
        new_value = {}

        for widget in self._selected_widgets:
            new_value[widget.name] = widget.value

        self.value = new_value

    def _update_selected_widgets(self, org_value):
        new_widgets = {}

        for feature in self.selected_features:
            value = org_value.get(feature, self.features[feature])
            widget = self._new_widget(feature, value)
            new_widgets[feature] = widget

        self._selected_widgets[:] = list(new_widgets.values())

    def _new_widget(self, feature, value):
        widget = pn.widgets.FloatInput(
            name=feature, value=value, sizing_mode="stretch_width"
        )
        pn.bind(self._update_value, widget, watch=True)
        return widget


features = {
    "Blade Length (m)": 73.5,
    "Cut-in Wind Speed (m/s)": 3.5,
    "Cut-out Wind Speed (m/s)": 25,
    "Grid Connection Capacity (MW)": 5,
    "Hub Height (m)": 100,
    "Rated Wind Speed (m/s)": 12,
    "Rotor Diameter (m)": 150,
    "Turbine Efficiency (%)": 45,
    "Water Depth (m)": 30,
    "Wind Speed (m/s)": 10,
}
_selected_features = ["Wind Speed (m/s)", "Rotor Diameter (m)"]
_widget = FeatureInput(
    features=features,
    selected_features=_selected_features,
    width=500,
)

##### My code starts here #####

main_column = pn.Column()
modal_column = pn.Column()

flt = pn.template.FastListTemplate(
    main=[main_column],
    modal=[modal_column]
)


def add_to_main(_):
    main_column.append(pn.FlexBox(
        pn.Column(
            "## Widget",
            _widget,
        ),
        pn.Column(
            "## Value",
            pn.pane.JSON(_widget.param.value, width=500, height=200),
        ),
    ))


def add_to_modal(_):
    modal_column.append(
        pn.FlexBox(
            pn.Column(
                "## Widget",
                _widget,
            ),
            pn.Column(
                "## Value",
                pn.pane.JSON(_widget.param.value, width=500, height=200),
            ),
        )
    )


def add_then_open(_):
    modal_column.append(
        pn.FlexBox(
            pn.Column(
                "## Widget",
                _widget,
            ),
            pn.Column(
                "## Value",
                pn.pane.JSON(_widget.param.value, width=500, height=200),
            ),
        )
    )
    flt.open_modal()


def open_then_add(_):
    flt.open_modal()
    modal_column.append(
        pn.FlexBox(
            pn.Column(
                "## Widget",
                _widget,
            ),
            pn.Column(
                "## Value",
                pn.pane.JSON(_widget.param.value, width=500, height=200),
            ),
        )
    )


main_column.append(pn.widgets.Button(
    name='add to main', on_click=add_to_main))
main_column.append(pn.widgets.Button(
    name='add to modal', on_click=add_to_modal))
main_column.append(pn.widgets.Button(
    name="add to modal, then open modal", on_click=add_then_open))
main_column.append(pn.widgets.Button(
    name="open modal, then add to modal", on_click=open_then_add))


flt.servable()


Stack traceback and/or browser JavaScript console output

Errors without inheriting from Reactive:

AttributeError: 'FeatureInput' object has no attribute '_process_param_change'
2025-02-06 19:21:37,267 WebSocket connection closed: code=1001, reason=None
2025-02-06 19:21:37,826 WebSocket connection opened
2025-02-06 19:21:37,826 ServerConnection created
2025-02-06 19:21:39,019 error handling message
 message: Message 'PATCH-DOC' content: {'events': [{'kind': 'MessageSent', 'msg_type': 'bokeh_event', 'msg_data': {'type': 'event', 'name': 'button_click', 'values': {'type': 'map', 'entries': [['model', {'id': 'p1289'}]]}}}]} 
 error: AttributeError("'FeatureInput' object has no attribute '_process_param_change'")
Traceback (most recent call last):
  File "/code/venv/lib/python3.11/site-packages/bokeh/server/protocol_handler.py", line 94, in handle
    work = await handler(message, connection)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/bokeh/server/session.py", line 94, in _needs_document_lock_wrapper
    result = func(self, *args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/bokeh/server/session.py", line 286, in _handle_patch
    message.apply_to_document(self.document, self)
  File "/code/venv/lib/python3.11/site-packages/bokeh/protocol/messages/patch_doc.py", line 104, in apply_to_document
    invoke_with_curdoc(doc, lambda: doc.apply_json_patch(self.payload, setter=setter))
  File "/code/venv/lib/python3.11/site-packages/bokeh/document/callbacks.py", line 453, in invoke_with_curdoc
    return f()
           ^^^
  File "/code/venv/lib/python3.11/site-packages/bokeh/protocol/messages/patch_doc.py", line 104, in <lambda>
    invoke_with_curdoc(doc, lambda: doc.apply_json_patch(self.payload, setter=setter))
                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/bokeh/document/document.py", line 391, in apply_json_patch
    DocumentPatchedEvent.handle_event(self, event, setter)
  File "/code/venv/lib/python3.11/site-packages/bokeh/document/events.py", line 244, in handle_event
    event_cls._handle_event(doc, event)
  File "/code/venv/lib/python3.11/site-packages/bokeh/document/events.py", line 279, in _handle_event
    cb(event.msg_data)
  File "/code/venv/lib/python3.11/site-packages/bokeh/document/callbacks.py", line 400, in trigger_event
    model._trigger_event(event)
  File "/code/venv/lib/python3.11/site-packages/bokeh/util/callback_manager.py", line 111, in _trigger_event
    self.document.callbacks.notify_event(cast(Model, self), event, invoke)
  File "/code/venv/lib/python3.11/site-packages/bokeh/document/callbacks.py", line 262, in notify_event
    invoke_with_curdoc(doc, callback_invoker)
  File "/code/venv/lib/python3.11/site-packages/bokeh/document/callbacks.py", line 453, in invoke_with_curdoc
    return f()
           ^^^
  File "/code/venv/lib/python3.11/site-packages/bokeh/util/callback_manager.py", line 107, in invoke
    cast(EventCallbackWithEvent, callback)(event)
  File "/code/venv/lib/python3.11/site-packages/panel/reactive.py", line 580, in _server_event
    self._comm_event(doc, event)
  File "/code/venv/lib/python3.11/site-packages/panel/reactive.py", line 567, in _comm_event
    state._handle_exception(e)
  File "/code/venv/lib/python3.11/site-packages/panel/io/state.py", line 484, in _handle_exception
    raise exception
  File "/code/venv/lib/python3.11/site-packages/panel/reactive.py", line 565, in _comm_event
    self._process_bokeh_event(doc, event)
  File "/code/venv/lib/python3.11/site-packages/panel/reactive.py", line 502, in _process_bokeh_event
    self._process_event(event)
  File "/code/venv/lib/python3.11/site-packages/panel/widgets/button.py", line 241, in _process_event
    self.clicks += 1
    ^^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 515, in _f
    instance_param.__set__(obj, val)
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 517, in _f
    return f(self, obj, val)
           ^^^^^^^^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/param/parameters.py", line 541, in __set__
    super().__set__(obj,val)
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 517, in _f
    return f(self, obj, val)
           ^^^^^^^^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 1564, in __set__
    obj.param._call_watcher(watcher, event)
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 2604, in _call_watcher
    self_._execute_watcher(watcher, (event,))
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 2586, in _execute_watcher
    watcher.fn(*args, **kwargs)
  File "/workspaces/dexter-ui/test.py", line 125, in add_to_modal
    modal_column.append(
  File "/code/venv/lib/python3.11/site-packages/panel/layout/base.py", line 474, in append
    self.objects = new_objects
    ^^^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 515, in _f
    instance_param.__set__(obj, val)
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 517, in _f
    return f(self, obj, val)
           ^^^^^^^^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/panel/viewable.py", line 1184, in __set__
    super().__set__(obj, self._transform_value(val))
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 517, in _f
    return f(self, obj, val)
           ^^^^^^^^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 1564, in __set__
    obj.param._call_watcher(watcher, event)
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 2604, in _call_watcher
    self_._execute_watcher(watcher, (event,))
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 2586, in _execute_watcher
    watcher.fn(*args, **kwargs)
  File "/code/venv/lib/python3.11/site-packages/panel/reactive.py", line 451, in _param_change
    applied &= self._apply_update(named_events, properties, model, ref)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/panel/reactive.py", line 339, in _apply_update
    self._update_model(events, msg, root, model, doc, comm)
  File "/code/venv/lib/python3.11/site-packages/panel/layout/base.py", line 135, in _update_model
    state._views[ref][0]._preprocess(root, self, old_children)
  File "/code/venv/lib/python3.11/site-packages/panel/viewable.py", line 619, in _preprocess
    hook(self, root, changed, old_models)
  File "/code/venv/lib/python3.11/site-packages/panel/theme/base.py", line 150, in _apply_hooks
    self._reapply(changed, root, old_models, isolated=False, cache=cache, document=root.document)
  File "/code/venv/lib/python3.11/site-packages/panel/theme/base.py", line 138, in _reapply
    self._apply_modifiers(o, ref, self.theme, isolated, cache, document)
  File "/code/venv/lib/python3.11/site-packages/panel/theme/base.py", line 253, in _apply_modifiers
    cls._apply_params(viewable, mref, modifiers, document)
  File "/code/venv/lib/python3.11/site-packages/panel/theme/base.py", line 273, in _apply_params
    props = viewable._process_param_change(params)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'FeatureInput' object has no attribute '_process_param_change'

Errors when inheriting from Reactive and trying to add something in the UI:

2025-02-06 19:52:36,522 WebSocket connection closed: code=1001, reason=None
2025-02-06 19:52:37,391 WebSocket connection opened
2025-02-06 19:52:37,392 ServerConnection created
2025-02-06 19:52:42,574 ERROR: panel.reactive - Callback failed for object named 'Selected features' changing property {'value': ['Rotor Diameter (m)', 'Wind Speed (m/s)', 'Blade Length (m)']} 
Traceback (most recent call last):
  File "/code/venv/lib/python3.11/site-packages/panel/reactive.py", line 470, in _process_events
    self.param.update(**self_params)
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 2406, in update
    restore = dict(self_._update(arg, **kwargs))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 2439, in _update
    self_._batch_call_watchers()
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 2624, in _batch_call_watchers
    self_._execute_watcher(watcher, events)
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 2586, in _execute_watcher
    watcher.fn(*args, **kwargs)
  File "/code/venv/lib/python3.11/site-packages/panel/param.py", line 526, in link_widget
    self.object.param.update(**{p_name: change.new})
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 2406, in update
    restore = dict(self_._update(arg, **kwargs))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 2439, in _update
    self_._batch_call_watchers()
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 2624, in _batch_call_watchers
    self_._execute_watcher(watcher, events)
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 2586, in _execute_watcher
    watcher.fn(*args, **kwargs)
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 767, in _sync_caller
    return function()
           ^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/param/depends.py", line 85, in _depends
    return func(*args, **kw)
           ^^^^^^^^^^^^^^^^^
  File "/workspaces/dexter-ui/test.py", line 61, in _handle_selected_features_change
    self._update_value()
  File "/workspaces/dexter-ui/test.py", line 69, in _update_value
    self.value = new_value
    ^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 515, in _f
    instance_param.__set__(obj, val)
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 517, in _f
    return f(self, obj, val)
           ^^^^^^^^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 1564, in __set__
    obj.param._call_watcher(watcher, event)
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 2604, in _call_watcher
    self_._execute_watcher(watcher, (event,))
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 2586, in _execute_watcher
    watcher.fn(*args, **kwargs)
  File "/code/venv/lib/python3.11/site-packages/panel/reactive.py", line 451, in _param_change
    applied &= self._apply_update(named_events, properties, model, ref)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/panel/reactive.py", line 339, in _apply_update
    self._update_model(events, msg, root, model, doc, comm)
  File "/code/venv/lib/python3.11/site-packages/panel/reactive.py", line 711, in _update_model
    super()._update_model(events, msg, root, model, doc, comm)
  File "/code/venv/lib/python3.11/site-packages/panel/reactive.py", line 371, in _update_model
    model_val = getattr(model, attr)
                ^^^^^^^^^^^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/bokeh/core/has_props.py", line 369, in __getattr__
    self._raise_attribute_error_with_matches(name, properties)
  File "/code/venv/lib/python3.11/site-packages/bokeh/core/has_props.py", line 377, in _raise_attribute_error_with_matches
    raise AttributeError(f"unexpected attribute {name!r} to {self.__class__.__name__}, {text} attributes are {nice_join(matches)}")
AttributeError: unexpected attribute 'value' to Column, possible attributes are align, aspect_ratio, auto_scroll_limit, children, context_menu, css_classes, css_variables, disabled, elements, flow_mode, height, height_policy, js_event_callbacks, js_property_callbacks, margin, max_height, max_width, min_height, min_width, name, resizable, scroll_button_threshold, scroll_index, scroll_position, sizing_mode, spacing, styles, stylesheets, subscribed_events, syncable, tags, view_latest, visible, width or width_policy
2025-02-06 19:52:42,578 Exception in callback functools.partial(<bound method IOLoop._discard_future_result of <tornado.platform.asyncio.AsyncIOMainLoop object at 0x774f97e62390>>, <Task finished name='Task-1212' coro=<ServerSession.with_document_locked() done, defined at /code/venv/lib/python3.11/site-packages/bokeh/server/session.py:77> exception=AttributeError("unexpected attribute 'value' to Column, possible attributes are align, aspect_ratio, auto_scroll_limit, children, context_menu, css_classes, css_variables, disabled, elements, flow_mode, height, height_policy, js_event_callbacks, js_property_callbacks, margin, max_height, max_width, min_height, min_width, name, resizable, scroll_button_threshold, scroll_index, scroll_position, sizing_mode, spacing, styles, stylesheets, subscribed_events, syncable, tags, view_latest, visible, width or width_policy")>)
Traceback (most recent call last):
  File "/code/venv/lib/python3.11/site-packages/tornado/ioloop.py", line 750, in _run_callback
    ret = callback()
          ^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/tornado/ioloop.py", line 774, in _discard_future_result
    future.result()
  File "/code/venv/lib/python3.11/site-packages/bokeh/server/session.py", line 98, in _needs_document_lock_wrapper
    result = await result
             ^^^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/panel/reactive.py", line 517, in _change_coroutine
    state._handle_exception(e)
  File "/code/venv/lib/python3.11/site-packages/panel/io/state.py", line 484, in _handle_exception
    raise exception
  File "/code/venv/lib/python3.11/site-packages/panel/reactive.py", line 515, in _change_coroutine
    self._change_event(doc)
  File "/code/venv/lib/python3.11/site-packages/panel/reactive.py", line 533, in _change_event
    self._process_events(events)
  File "/code/venv/lib/python3.11/site-packages/panel/reactive.py", line 470, in _process_events
    self.param.update(**self_params)
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 2406, in update
    restore = dict(self_._update(arg, **kwargs))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 2439, in _update
    self_._batch_call_watchers()
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 2624, in _batch_call_watchers
    self_._execute_watcher(watcher, events)
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 2586, in _execute_watcher
    watcher.fn(*args, **kwargs)
  File "/code/venv/lib/python3.11/site-packages/panel/param.py", line 526, in link_widget
    self.object.param.update(**{p_name: change.new})
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 2406, in update
    restore = dict(self_._update(arg, **kwargs))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 2439, in _update
    self_._batch_call_watchers()
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 2624, in _batch_call_watchers
    self_._execute_watcher(watcher, events)
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 2586, in _execute_watcher
    watcher.fn(*args, **kwargs)
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 767, in _sync_caller
    return function()
           ^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/param/depends.py", line 85, in _depends
    return func(*args, **kw)
           ^^^^^^^^^^^^^^^^^
  File "/workspaces/dexter-ui/test.py", line 61, in _handle_selected_features_change
    self._update_value()
  File "/workspaces/dexter-ui/test.py", line 69, in _update_value
    self.value = new_value
    ^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 515, in _f
    instance_param.__set__(obj, val)
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 517, in _f
    return f(self, obj, val)
           ^^^^^^^^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 1564, in __set__
    obj.param._call_watcher(watcher, event)
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 2604, in _call_watcher
    self_._execute_watcher(watcher, (event,))
  File "/code/venv/lib/python3.11/site-packages/param/parameterized.py", line 2586, in _execute_watcher
    watcher.fn(*args, **kwargs)
  File "/code/venv/lib/python3.11/site-packages/panel/reactive.py", line 451, in _param_change
    applied &= self._apply_update(named_events, properties, model, ref)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/panel/reactive.py", line 339, in _apply_update
    self._update_model(events, msg, root, model, doc, comm)
  File "/code/venv/lib/python3.11/site-packages/panel/reactive.py", line 711, in _update_model
    super()._update_model(events, msg, root, model, doc, comm)
  File "/code/venv/lib/python3.11/site-packages/panel/reactive.py", line 371, in _update_model
    model_val = getattr(model, attr)
                ^^^^^^^^^^^^^^^^^^^^
  File "/code/venv/lib/python3.11/site-packages/bokeh/core/has_props.py", line 369, in __getattr__
    self._raise_attribute_error_with_matches(name, properties)
  File "/code/venv/lib/python3.11/site-packages/bokeh/core/has_props.py", line 377, in _raise_attribute_error_with_matches
    raise AttributeError(f"unexpected attribute {name!r} to {self.__class__.__name__}, {text} attributes are {nice_join(matches)}")
AttributeError: unexpected attribute 'value' to Column, possible attributes are align, aspect_ratio, auto_scroll_limit, children, context_menu, css_classes, css_variables, disabled, elements, flow_mode, height, height_policy, js_event_callbacks, js_property_callbacks, margin, max_height, max_width, min_height, min_width, name, resizable, scroll_button_threshold, scroll_index, scroll_position, sizing_mode, spacing, styles, stylesheets, subscribed_events, syncable, tags, view_latest, visible, width or width_policy
2025-02-06 19:52:42,620 Dropping a patch because it contains a previously known reference (id='p1637'). Most of the time this is harmless and usually a result of updating a model on one side of a communications channel while it was being removed on the other end.
2025-02-06 19:52:42,621 Dropping a patch because it contains a previously known reference (id='p1638'). Most of the time this is harmless and usually a result of updating a model on one side of a communications channel while it was being removed on the other end.

14lclark avatar Feb 06 '25 20:02 14lclark

I'm also running into this. I've made a basic extension to date picker with some buttons below to input presets. It works in a very simple test app but fails as described when adding to BootstrapTemplate, which all my apps use.

Is there any work around?

ta264 avatar May 23 '25 19:05 ta264

@ta264 did you try to have your component also inherit from pn.reactive.Reactive? That worked for me as a temporary fix — you’ll still get errors, but everything works fine as far as I can tell.

I haven’t updated to 1.7.0 yet, so not sure if that will affect the fix.

14lclark avatar May 24 '25 01:05 14lclark

@ta264 did you try to have your component also inherit from pn.reactive.Reactive? That worked for me as a temporary fix — you’ll still get errors, but everything works fine as far as I can tell.

Thanks, I did try this and got errors just like the ones you posted. But unfortunately my component also didn't seem to work

ta264 avatar May 24 '25 05:05 ta264

@ta264 Sorry, I don't have any other workarounds. Could you post the errors you're getting with and without inheriting from pn.reactive.Reactive plus some of your code if possible? It might be helpful in finding a workaround or helping the devs fix the bug.

14lclark avatar May 26 '25 19:05 14lclark

I think I have got a related issue ~and can provide a workaround~ (edit: now I also get other errors with the workaround).

Software Version Info
Python == 3.13.3
panel == 1.7.1
bokeh == 3.7.3
param == 2.2.0

Description of expected behavior and the observed behavior

The following script raises the error about missing _process_param_change in MyCustomWidget whenever a tab is switched, resulting in some weird behavior:

  • The content of MyCustomWidget is shown, but when switching back to the first tab, nothing is shown.
  • When the order of the tabs is switched, or active=1 is set on the Tabs widget, no error occurs and everything renders as expected.

Complete, minimal, self-contained example code that reproduces the issue

import panel as pn
from panel.custom import PyComponent
from panel.widgets.base import WidgetBase

pn.extension()

class MyCustomWidget(WidgetBase, PyComponent):
    def __panel__(self) -> pn.Column:
        return pn.Column("I am the custom widget.")

template = pn.template.BootstrapTemplate(
    title="Panel Test App",
    main=[
        pn.Tabs(
            ("Test 1", "Hello world!"),
            ("Test 2", MyCustomWidget()),
            dynamic=True,
        )
    ],
)

template.servable()

Stack traceback

Stack traceback
2025-05-30 15:35:01,835 ERROR: panel.reactive - Callback failed for object named 'Tabs00717' changing property {'active': 1} 
Traceback (most recent call last):
  File "*****/panel-test/.venv/lib/python3.13/site-packages/panel/reactive.py", line 470, in _process_events
    self.param.update(**self_params)
    ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/param/parameterized.py", line 2406, in update
    restore = dict(self_._update(arg, **kwargs))
                   ~~~~~~~~~~~~~^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/param/parameterized.py", line 2439, in _update
    self_._batch_call_watchers()
    ~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/param/parameterized.py", line 2624, in _batch_call_watchers
    self_._execute_watcher(watcher, events)
    ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/param/parameterized.py", line 2586, in _execute_watcher
    watcher.fn(*args, **kwargs)
    ~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/panel/layout/tabs.py", line 139, in _update_active
    self.param.trigger('objects')
    ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/param/parameterized.py", line 2556, in trigger
    self_.update(dict(params, **triggers))
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/param/parameterized.py", line 2406, in update
    restore = dict(self_._update(arg, **kwargs))
                   ~~~~~~~~~~~~~^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/param/parameterized.py", line 2439, in _update
    self_._batch_call_watchers()
    ~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/param/parameterized.py", line 2624, in _batch_call_watchers
    self_._execute_watcher(watcher, events)
    ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/param/parameterized.py", line 2586, in _execute_watcher
    watcher.fn(*args, **kwargs)
    ~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/panel/reactive.py", line 456, in _param_change
    self._apply_update(named_events, properties, model, ref)
    ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/panel/reactive.py", line 345, in _apply_update
    self._update_model(events, msg, root, model, doc, comm)
    ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/panel/layout/base.py", line 264, in _update_model
    state._views[ref][0]._preprocess(root, self, old_children)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/panel/viewable.py", line 631, in _preprocess
    hook(self, root, changed, old_models)
    ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/panel/theme/base.py", line 156, in _apply_hooks
    self._reapply(changed, root, old_models, isolated=False, cache=cache, document=root.document)
    ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/panel/theme/base.py", line 142, in _reapply
    self._apply_modifiers(o, ref, self.theme, isolated, cache, document)
    ~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/panel/theme/base.py", line 260, in _apply_modifiers
    cls._apply_params(viewable, mref, modifiers, document)
    ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/panel/theme/base.py", line 280, in _apply_params
    props = viewable._process_param_change(params)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'MyCustomWidget' object has no attribute '_process_param_change'
2025-05-30 15:35:01,841 Exception in callback functools.partial(<bound method IOLoop._discard_future_result of <tornado.platform.asyncio.AsyncIOMainLoop object at 0x72a595926900>>, <Task finished name='Task-699' coro=<ServerSession.with_document_locked() done, defined at *****/panel-test/.venv/lib/python3.13/site-packages/bokeh/server/session.py:77> exception=AttributeError("'MyCustomWidget' object has no attribute '_process_param_change'")>)
Traceback (most recent call last):
  File "*****/panel-test/.venv/lib/python3.13/site-packages/tornado/ioloop.py", line 758, in _run_callback
    ret = callback()
  File "*****/panel-test/.venv/lib/python3.13/site-packages/tornado/ioloop.py", line 782, in _discard_future_result
    future.result()
    ~~~~~~~~~~~~~^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/bokeh/server/session.py", line 98, in _needs_document_lock_wrapper
    result = await result
             ^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/panel/reactive.py", line 519, in _change_coroutine
    state._handle_exception(e)
    ~~~~~~~~~~~~~~~~~~~~~~~^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/panel/io/state.py", line 488, in _handle_exception
    raise exception
  File "*****/panel-test/.venv/lib/python3.13/site-packages/panel/reactive.py", line 517, in _change_coroutine
    self._change_event(doc)
    ~~~~~~~~~~~~~~~~~~^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/panel/reactive.py", line 535, in _change_event
    self._process_events(events)
    ~~~~~~~~~~~~~~~~~~~~^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/panel/reactive.py", line 489, in _process_events
    raise e
  File "*****/panel-test/.venv/lib/python3.13/site-packages/panel/reactive.py", line 470, in _process_events
    self.param.update(**self_params)
    ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/param/parameterized.py", line 2406, in update
    restore = dict(self_._update(arg, **kwargs))
                   ~~~~~~~~~~~~~^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/param/parameterized.py", line 2439, in _update
    self_._batch_call_watchers()
    ~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/param/parameterized.py", line 2624, in _batch_call_watchers
    self_._execute_watcher(watcher, events)
    ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/param/parameterized.py", line 2586, in _execute_watcher
    watcher.fn(*args, **kwargs)
    ~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/panel/layout/tabs.py", line 139, in _update_active
    self.param.trigger('objects')
    ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/param/parameterized.py", line 2556, in trigger
    self_.update(dict(params, **triggers))
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/param/parameterized.py", line 2406, in update
    restore = dict(self_._update(arg, **kwargs))
                   ~~~~~~~~~~~~~^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/param/parameterized.py", line 2439, in _update
    self_._batch_call_watchers()
    ~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/param/parameterized.py", line 2624, in _batch_call_watchers
    self_._execute_watcher(watcher, events)
    ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/param/parameterized.py", line 2586, in _execute_watcher
    watcher.fn(*args, **kwargs)
    ~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/panel/reactive.py", line 456, in _param_change
    self._apply_update(named_events, properties, model, ref)
    ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/panel/reactive.py", line 345, in _apply_update
    self._update_model(events, msg, root, model, doc, comm)
    ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/panel/layout/base.py", line 264, in _update_model
    state._views[ref][0]._preprocess(root, self, old_children)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/panel/viewable.py", line 631, in _preprocess
    hook(self, root, changed, old_models)
    ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/panel/theme/base.py", line 156, in _apply_hooks
    self._reapply(changed, root, old_models, isolated=False, cache=cache, document=root.document)
    ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/panel/theme/base.py", line 142, in _reapply
    self._apply_modifiers(o, ref, self.theme, isolated, cache, document)
    ~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/panel/theme/base.py", line 260, in _apply_modifiers
    cls._apply_params(viewable, mref, modifiers, document)
    ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "*****/panel-test/.venv/lib/python3.13/site-packages/panel/theme/base.py", line 280, in _apply_params
    props = viewable._process_param_change(params)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'MyCustomWidget' object has no attribute '_process_param_change'

Workaround for simple example

Inherit panel.reactive.Syncable. It implements _process_param_change and I observed no other issues with this example, but in more complex cases it produces other errors and makes components unusable.

import panel as pn
from panel.custom import PyComponent
from panel.reactive import Syncable
from panel.widgets.base import WidgetBase

pn.extension()


class MyCustomWidget(WidgetBase, PyComponent, Syncable):
    def __panel__(self) -> pn.Column:
        return pn.Column("I am the custom widget.")


template = pn.template.BootstrapTemplate(
    title="Panel Test App",
    main=[
        pn.Tabs(
            ("Test 1", "Hello world!"),
            ("Test 2", MyCustomWidget()),
            dynamic=True,
        )
    ],
)


template.servable()

dejfh avatar May 30 '25 13:05 dejfh

I'm running into related issues. It does seem like the workarounds mentioned here can get it to run, but not without other issues and bugs. Without a template it seems to work as the example shows, but i really don't want to give up the templating.

Specifically with the Syncable workaround, it seems to introduce attribute errors when updating a parameter attribute of my custom widget. It seems to be trying to assign that attribute to the element returned in __panel__.

CmpCtrl avatar Jun 24 '25 13:06 CmpCtrl

Fixed by https://github.com/holoviz/panel/pull/7975. Will try to get a new release out tomorrow.

philippjfr avatar Jun 24 '25 14:06 philippjfr

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

github-actions[bot] avatar Oct 07 '25 01:10 github-actions[bot]