Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow ParamMethod and ParamFunction to be lazy #1966

Merged
merged 2 commits into from
Feb 25, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 27 additions & 18 deletions panel/param.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from param.parameterized import classlist, discard_events

from .io import init_doc, state
from .layout import Row, Panel, Tabs, Column
from .layout import Column, Panel, Row, Spacer, Tabs
from .pane.base import PaneBase, ReplacementPane
from .util import (
abbreviated_repr, full_groupby, get_method_owner, is_parameterized,
Expand Down Expand Up @@ -671,15 +671,20 @@ class ParamMethod(ReplacementPane):
return any object which itself can be rendered as a Pane.
"""

lazy = param.Boolean(default=False, doc="""
Whether to lazily evaluate the contents of the object
only when it is required for rendering.""")

loading_indicator = param.Boolean(default=False, doc="""
Whether to show loading indicator while pane is updating.""")

def __init__(self, object=None, **params):
super().__init__(object, **params)
self._evaled = not self.lazy
self._link_object_params()
if object is not None:
self._validate_object()
self._update_inner(self.eval(object))
self._replace_pane(not self.lazy)

@param.depends('object', watch=True)
def _validate_object(self):
Expand Down Expand Up @@ -714,6 +719,19 @@ def eval(self, function):
kwargs = {n: getattr(dep.owner, dep.name) for n, dep in kw_deps.items()}
return function(*args, **kwargs)

def _replace_pane(self, *args, force=False):
self._evaled = bool(self._models) or force or not self.lazy
if self._evaled:
self._inner_layout.loading = self.loading_indicator
try:
if self.object is None:
new_object = Spacer()
else:
new_object = self.eval(self.object)
self._update_inner(new_object)
finally:
self._inner_layout.loading = False

def _update_pane(self, *events):
callbacks = []
for watcher in self._callbacks:
Expand All @@ -724,8 +742,7 @@ def _update_pane(self, *events):
obj.param.unwatch(watcher)
self._callbacks = callbacks
self._link_object_params()
if object is not None:
self._update_inner(self.eval(self.object))
self._replace_pane()

def _link_object_params(self):
parameterized = get_method_owner(self.object)
Expand Down Expand Up @@ -756,12 +773,7 @@ def update_pane(*events):
self._callbacks.append(watcher)
for p in params:
deps.append(p)
self._inner_layout.loading = self.loading_indicator
try:
new_object = self.eval(self.object)
self._update_inner(new_object)
finally:
self._inner_layout.loading = False
self._replace_pane()

for _, params in full_groupby(params, lambda x: (x.inst or x.cls, x.what)):
p = params[0]
Expand All @@ -774,6 +786,11 @@ def update_pane(*events):
watcher = pobj.param.watch(update_pane, ps, p.what)
self._callbacks.append(watcher)

def _get_model(self, doc, root=None, parent=None, comm=None):
if not self._evaled:
self._replace_pane(force=True)
return super()._get_model(doc, root, parent, comm)

#----------------------------------------------------------------
# Public API
#----------------------------------------------------------------
Expand All @@ -795,14 +812,6 @@ class ParamFunction(ParamMethod):

priority = 0.6

def _replace_pane(self, *args):
self._inner_layout.loading = self.loading_indicator
try:
new_object = self.eval(self.object)
self._update_inner(new_object)
finally:
self._inner_layout.loading = False

def _link_object_params(self):
deps = self.object._dinfo
dep_params = list(deps['dependencies']) + list(deps.get('kw', {}).values())
Expand Down