Source code for bokeh.server.callbacks

#-----------------------------------------------------------------------------
# Copyright (c) 2012 - 2019, Anaconda, Inc., and Bokeh Contributors.
# All rights reserved.
#
# The full license is in the file LICENSE.txt, distributed with this software.
#-----------------------------------------------------------------------------
''' Provide classes to represent callback code that can be associate with
Bokeh Documents and Sessions.

'''

#-----------------------------------------------------------------------------
# Boilerplate
#-----------------------------------------------------------------------------
from __future__ import absolute_import, division, print_function, unicode_literals

import logging
log = logging.getLogger(__name__)

#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------

# Standard library imports

# External imports

# Bokeh imports
from ..util.serialization import make_id
from ..util.tornado import _CallbackGroup

#-----------------------------------------------------------------------------
# Globals and constants
#-----------------------------------------------------------------------------

__all__ = (
    'NextTickCallback',
    'PeriodicCallback',
    'SessionCallback',
    'TimeoutCallback',
)

#-----------------------------------------------------------------------------
# Dev API
#-----------------------------------------------------------------------------

[docs]class SessionCallback(object): ''' A base class for callback objects associated with Bokeh Documents and Sessions. '''
[docs] def __init__(self, document, callback, id=None): ''' Args: document (Document) : callback (callable) : id (str, optional) : ''' self._id = make_id() if id is None else id self._document = document self._callback = callback
@property def id(self): ''' A unique ID for this callback ''' return self._id @property def callback(self): ''' The callable that this callback wraps. ''' return self._callback def _copy_with_changed_callback(self, new_callback): ''' Dev API used to wrap the callback with decorators. ''' raise NotImplementedError("_copy_with_changed_callback")
#----------------------------------------------------------------------------- # General API #-----------------------------------------------------------------------------
[docs]class NextTickCallback(SessionCallback): ''' Represent a callback to execute on the next ``IOLoop`` "tick". '''
[docs] def __init__(self, document, callback, id=None): ''' Args: document (Document) : callback (callable) : id (str, optional) : ''' super(NextTickCallback, self).__init__(document, callback, id)
def _copy_with_changed_callback(self, new_callback): ''' Dev API used to wrap the callback with decorators. ''' return NextTickCallback(self._document, new_callback, self._id)
[docs]class PeriodicCallback(SessionCallback): ''' Represent a callback to execute periodically on the ``IOLoop`` at a specified periodic time interval. '''
[docs] def __init__(self, document, callback, period, id=None): ''' Args: document (Document) : callback (callable) : period (int) : id (str, optional) : ''' super(PeriodicCallback, self).__init__(document, callback, id) self._period = period
@property def period(self): ''' The period time (in milliseconds) that this callback should repeat execution at. ''' return self._period def _copy_with_changed_callback(self, new_callback): ''' Dev API used to wrap the callback with decorators. ''' return PeriodicCallback(self._document, new_callback, self._period, self._id)
[docs]class TimeoutCallback(SessionCallback): ''' Represent a callback to execute once on the ``IOLoop`` after a specified time interval passes. '''
[docs] def __init__(self, document, callback, timeout, id=None): ''' Args: document (Document) : callback (callable) : timeout (int) : id (str, optional) : ''' super(TimeoutCallback, self).__init__(document, callback, id) self._timeout = timeout
@property def timeout(self): ''' The timeout (in milliseconds) that the callback should run after. ''' return self._timeout def _copy_with_changed_callback(self, new_callback): ''' Dev API used to wrap the callback with decorators. ''' return TimeoutCallback(self._document, new_callback, self._timeout, self._id)
#----------------------------------------------------------------------------- # Private API #----------------------------------------------------------------------------- class _DocumentCallbackGroup(object): ''' ''' def __init__(self, io_loop=None): ''' ''' self._group = _CallbackGroup(io_loop) def remove_all_callbacks(self): ''' ''' self._group.remove_all_callbacks() def add_session_callbacks(self, callbacks): ''' ''' for cb in callbacks: self.add_session_callback(cb) def add_session_callback(self, callback_obj): ''' ''' if isinstance(callback_obj, PeriodicCallback): self._group.add_periodic_callback(callback_obj.callback, callback_obj.period, callback_obj.id) elif isinstance(callback_obj, TimeoutCallback): self._group.add_timeout_callback(callback_obj.callback, callback_obj.timeout, callback_obj.id) elif isinstance(callback_obj, NextTickCallback): self._group.add_next_tick_callback(callback_obj.callback, callback_obj.id) else: raise ValueError("Expected callback of type PeriodicCallback, TimeoutCallback, NextTickCallback, got: %s" % callback_obj.callback) def remove_session_callback(self, callback_obj): ''' ''' # we may be called multiple times because of multiple # views on a document - the document has to notify that # the callback was removed even if only one view invoked # it. So we need to silently no-op if we're already # removed. try: if isinstance(callback_obj, PeriodicCallback): self._group.remove_periodic_callback(callback_obj.id) elif isinstance(callback_obj, TimeoutCallback): self._group.remove_timeout_callback(callback_obj.id) elif isinstance(callback_obj, NextTickCallback): self._group.remove_next_tick_callback(callback_obj.id) except ValueError: pass #----------------------------------------------------------------------------- # Code #-----------------------------------------------------------------------------