Repository: holoviz/panel

Issue:
RangeXY raising exception with panel serve but not in Notebook
Hi

I'm trying to make a reactive dashboard. I now have my dashboard working great in a Jupyter Notebook. But when I run it via 'panel serve' it raises the below 'exception'.

Maybe it's me that misunderstands something. I've tried many different ways of implementing this as cannot find a reference example.

```bash
$ python -m panel serve 'src\pages\gallery\kickstarter_dashboard\main.py' --dev --show
C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\command\util.py:127: UserWarning:
It looks like you might be running the main.py of a directory app directly.
If this is the case, to enable the features of directory style apps, you must
call "bokeh serve" on the directory instead. For example:

    bokeh serve my_app_dir/

If this is not the case, renaming main.py will suppress this warning.

  warnings.warn(DIRSTYLE_MAIN_WARNING)
2019-12-18 12:20:25,354 Starting Bokeh server version 1.4.0 (running on Tornado 5.1.1)
2019-12-18 12:20:25,356 User authentication hooks NOT provided (default user enabled)
2019-12-18 12:20:25,361 Bokeh app running at: http://localhost:5006/main
2019-12-18 12:20:25,362 Starting Bokeh server with process id: 8752
2019-12-18 12:20:31,342 WebSocket connection opened
2019-12-18 12:20:31,344 ServerConnection created
C:\repos\private\awesome-panel\src\pages\gallery\kickstarter_dashboard\main.py:208: UserWarning: Boolean Series key will be reindexed to match DataFrame index.
  sub_df = sub_df[x_filter]
WARNING:param.ParamMethod01391: HoloViews pane model Figure(id='1371', ...) could not be replaced with new model Figure(id='1819', ...), ensure that the parent is not modified at the same time the panel is being updated.
2019-12-18 12:20:36,987 HoloViews pane model Figure(id='1371', ...) could not be replaced with new model Figure(id='1819', ...), ensure that the parent is not modified at the same time the panel is being updated.
2019-12-18 12:20:37,347 Exception in callback functools.partial(<function wrap.<locals>.null_wrapper at 0x0000005386226C18>,
<Future finished exception=TypeError("Constant parameter 'x_range' cannot be modified")>)
Traceback (most recent call last):
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\tornado\ioloop.py", line 758, in _run_callback
    ret = callback()
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\tornado\stack_context.py", line 300, in null_wrapper
    return fn(*args, **kwargs)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\tornado\ioloop.py", line 779, in _discard_future_result
    future.result()
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\tornado\gen.py", line 1147, in run
    yielded = self.gen.send(value)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\server\session.py", line 70, in _needs_document_lock_wrapper
    result = yield yield_for_all_futures(func(self, *args, **kwargs))
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\server\session.py", line 191, in with_document_locked
    return func(*args, **kwargs)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\document\document.py", line 1127, in wrapper
    return doc._with_self_as_curdoc(invoke)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\document\document.py", line 1113, in _with_self_as_curdoc
    return f()
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\document\document.py", line 1126, in invoke
    return f(*args, **kwargs)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\document\document.py", line 916, in remove_then_invoke
    return callback(*args, **kwargs)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\panel\viewable.py", line 714, in _change_event
    self._process_events(events)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\panel\viewable.py", line 704, in _process_events
    self.set_param(**self._process_property_change(events))
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\param\parameterized.py", line 1219, in inner
    return fn(*args, **kwargs)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\param\parameterized.py", line 2573, in set_param
    return self_or_cls.param.set_param(*args,**kwargs)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\param\parameterized.py", line 1365, in set_param
    self_._batch_call_watchers()
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\param\parameterized.py", line 1480, in _batch_call_watchers
    watcher.fn(*events)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\panel\param.py", line 345, in link_widget
    self.object.set_param(**{p_name: change.new})
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\param\parameterized.py", line 1219, in inner
    return fn(*args, **kwargs)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\param\parameterized.py", line 2573, in set_param
    return self_or_cls.param.set_param(*args,**kwargs)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\param\parameterized.py", line 1358, in set_param
    setattr(self_or_cls, k, v)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\param\parameterized.py", line 294, in _f
    instance_param.__set__(obj, val)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\param\parameterized.py", line 296, in _f
    return f(self, obj, val)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\param\parameterized.py", line 838, in __set__
    raise TypeError("Constant parameter '%s' cannot be modified"%self.name)
TypeError: Constant parameter 'x_range' cannot be modified
C:\repos\private\awesome-panel\src\pages\gallery\kickstarter_dashboard\main.py:208: UserWarning: Boolean Series key will be reindexed to match DataFrame index.
  sub_df = sub_df[x_filter]
WARNING:param.ParamMethod01391: HoloViews pane model Figure(id='1819', ...) could not be replaced with new model Figure(id='1976', ...), ensure that the parent is not modified at the same time the panel is being updated.
2019-12-18 12:20:37,858 HoloViews pane model Figure(id='1819', ...) could not be replaced with new model Figure(id='1976', ...), ensure that the parent is not modified at the same time the panel is being updated.
WARNING:param.ParamMethod01391: HoloViews pane model Figure(id='1819', ...) could not be replaced with new model Figure(id='2072', ...), ensure that the parent is not modified at the same time the panel is being updated.
2019-12-18 12:20:37,940 HoloViews pane model Figure(id='1819', ...) could not be replaced with new model Figure(id='2072', ...), ensure that the parent is not modified at the same time the panel is being updated.
2019-12-18 12:20:38,271 Exception in callback functools.partial(<function wrap.<locals>.null_wrapper at 0x0000005386226EE8>,
<Future finished exception=TypeError("Constant parameter 'x_range' cannot be modified")>)
Traceback (most recent call last):
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\tornado\ioloop.py", line 758, in _run_callback
    ret = callback()
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\tornado\stack_context.py", line 300, in null_wrapper
    return fn(*args, **kwargs)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\tornado\ioloop.py", line 779, in _discard_future_result
    future.result()
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\tornado\gen.py", line 1147, in run
    yielded = self.gen.send(value)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\server\session.py", line 70, in _needs_document_lock_wrapper
    result = yield yield_for_all_futures(func(self, *args, **kwargs))
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\server\session.py", line 191, in with_document_locked
    return func(*args, **kwargs)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\document\document.py", line 1127, in wrapper
    return doc._with_self_as_curdoc(invoke)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\document\document.py", line 1113, in _with_self_as_curdoc
    return f()
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\document\document.py", line 1126, in invoke
    return f(*args, **kwargs)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\document\document.py", line 916, in remove_then_invoke
    return callback(*args, **kwargs)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\panel\viewable.py", line 714, in _change_event
    self._process_events(events)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\panel\viewable.py", line 704, in _process_events
    self.set_param(**self._process_property_change(events))
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\param\parameterized.py", line 1219, in inner
    return fn(*args, **kwargs)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\param\parameterized.py", line 2573, in set_param
    return self_or_cls.param.set_param(*args,**kwargs)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\param\parameterized.py", line 1365, in set_param
    self_._batch_call_watchers()
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\param\parameterized.py", line 1480, in _batch_call_watchers
    watcher.fn(*events)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\panel\param.py", line 345, in link_widget
    self.object.set_param(**{p_name: change.new})
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\param\parameterized.py", line 1219, in inner
    return fn(*args, **kwargs)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\param\parameterized.py", line 2573, in set_param
    return self_or_cls.param.set_param(*args,**kwargs)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\param\parameterized.py", line 1358, in set_param
    setattr(self_or_cls, k, v)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\param\parameterized.py", line 294, in _f
    instance_param.__set__(obj, val)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\param\parameterized.py", line 296, in _f
    return f(self, obj, val)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\param\parameterized.py", line 838, in __set__
    raise TypeError("Constant parameter '%s' cannot be modified"%self.name)
TypeError: Constant parameter 'x_range' cannot be modified

Interrupted, shutting down
(.venv)

````

I've attached the source code, notebook and data.

[issue_rangexy.zip](https://github.com/holoviz/panel/files/3978167/issue_rangexy.zip)

and a small video that shows it works fine in the notebook. But when you start interacting with panel serve it raises the exception.

![note_book_works](https://user-images.githubusercontent.com/42288570/71083265-e76f6300-2192-11ea-9b0f-f66488cca07d.gif)



Discussion:
Moved to https://github.com/holoviz/panel/issues/882
I'd moved it to HoloViews on purpose.
Ahh. I just saw that. Sorry.
I thought it was me being tired after all those efforts getting rangeXY working. So I thought I posted in wrong Repo.
A bit more detail on what your example looks like would be very helpful.
Ah sorry, I see you posted the file, sorry about that.
I see the issue, you are trying to create a widget from the RangeXY stream, is that intentional? Streams cannot currently be used that way, but I suppose we could change that.
Hi @philippjfr . The problem is I cannot find a reference example of how develop a reactive dashboard in Panel with

- Parent/ child plots
- Select widget that affects both parent and child
- rangeXY stream of Parent affecting child.

based on a parametrized class. All the examples only have the individual parts. So I don't know how they can work together. But I'm trying to make it work. But it's guess work.

How would I make rangeXY work? And why does it work in a notebook and not with `panel serve`? If it works in the notebook it's natural to me think think i have a correct implementation.

**Could you give me some kind of hint on how to solve this. I've spent maybe 8 hours now trying to get it working.**

I think the next step for me would be to systematic go though the different tutorials of HoloViews, hvplot, streams, param and panel. Because I need to know them all in order to work efficiently with Panel. I just started out skimming the Panel docs, developing a bit, reading some more Panel docs and what I needed of the rest.

But there is actually a lot of concepts to understand and master before you can get things to work together. And how the different things work together is not yet that obvious to me. For example I have a feeling that I could implement a dashboard in HoloViews alone with a rangeXY stream. I could also develop it in Panel. But the two ways are different. So when I read the documentation of both I'm not yet cabable of understanding how things should be done the *Panel way*.

And I have a feeling that when I have this working I have the reference example of how all my dashboards would be implemented. They would just be variations of this with maybe more widgets, streams and plots/ tables in some cases.

Thanks
Okay this turns out not be your fault at all. It's a bug in Panel afterall. Just a quick tip though, you are declaring rangexy as a class attribute:

```
rangexy = hv.streams.RangeXY()
```

This isn't ideal because if you serve the app to multiple people they'll all share the same instance of RangeXY. I'd replace that with:

```python
rangexy = param.ClassSelector(class_=hv.streams.RangeXY, default=hv.streams.RangeXY())
```

I'll push a fix for the bug in a second though.

Actually there also is a second issue, you are returning the x_range as part of the layout:

```
return pn.Column(
            __doc__,
            self.param.categories,
            self.scatter_plot_view,
            self.bar_chart_view,
            self.rangexy,
            sizing_mode="stretch_width",
        )
```

This is problematic on the server because it ends up syncing the widget state back to Python and tries to edit the parameter. We need to make sure that it never tries to set stream parameter values.

Resolving patch:
diff --git a/panel/pane/base.py b/panel/pane/base.py
--- a/panel/pane/base.py
+++ b/panel/pane/base.py
@@ -105,7 +105,8 @@ def _init_properties(self):
         return {k: v for k, v in self.param.get_param_values()
                 if v is not None and k not in ['default_layout', 'object']}
 
-    def _update_object(self, old_model, doc, root, parent, comm):
+    def _update_object(self, ref, doc, root, parent, comm):
+        old_model = self._models[ref][0]
         if self._updates:
             self._update(old_model)
         else:
@@ -136,15 +137,15 @@ def _update_object(self, old_model, doc, root, parent, comm):
             state._views[ref][0]._preprocess(root)
 
     def _update_pane(self, event):
-        for ref, (model, parent) in self._models.items():
+        for ref, (_, parent) in self._models.items():
             viewable, root, doc, comm = state._views[ref]
             if comm or state._unblocked(doc):
                 with unlocked():
-                    self._update_object(model, doc, root, parent, comm)
+                    self._update_object(ref, doc, root, parent, comm)
                 if comm and 'embedded' not in root.tags:
                     push(doc, comm)
             else:
-                cb = partial(self._update_object, model, doc, root, parent, comm)
+                cb = partial(self._update_object, ref, doc, root, parent, comm)
                 if doc.session_context:
                     doc.add_next_tick_callback(cb)
                 else:
diff --git a/panel/pane/vtk/vtk.py b/panel/pane/vtk/vtk.py
--- a/panel/pane/vtk/vtk.py
+++ b/panel/pane/vtk/vtk.py
@@ -77,9 +77,9 @@ def _get_model(self, doc, root=None, parent=None, comm=None):
         self._models[root.ref['id']] = (model, parent)
         return model
 
-    def _update_object(self, old_model, doc, root, parent, comm):
+    def _update_object(self, ref, doc, root, parent, comm):
         self._legend = None
-        super()._update_object(old_model, doc, root, parent, comm)
+        super()._update_object(ref, doc, root, parent, comm)
 
     def _init_properties(self):
         return {k: v for k, v in self.param.get_param_values()
@@ -236,9 +236,9 @@ def _get_model(self, doc, root=None, parent=None, comm=None):
         self._models[root.ref['id']] = (model, parent)
         return model
 
-    def _update_object(self, old_model, doc, root, parent, comm):
+    def _update_object(self, ref, doc, root, parent, comm):
         self._legend = None
-        super()._update_object(old_model, doc, root, parent, comm)
+        super()._update_object(ref, doc, root, parent, comm)
 
     def construct_colorbars(self, orientation='horizontal'):
         if self._legend is None:


Test patch: