#-----------------------------------------------------------------------------# Copyright (c) 2012 - 2023, Anaconda, Inc., and Bokeh Contributors.# All rights reserved.## The full license is in the file LICENSE.txt, distributed with this software.#-----------------------------------------------------------------------------''''''#-----------------------------------------------------------------------------# Boilerplate#-----------------------------------------------------------------------------from__future__importannotationsimportlogging# isort:skiplog=logging.getLogger(__name__)#-----------------------------------------------------------------------------# Imports#-----------------------------------------------------------------------------# Standard library importsimportasynciofromfunctoolsimportwrapsfromtypingimport(TYPE_CHECKING,Any,Callable,Literal,Protocol,TypeVar,cast,)## Bokeh importsifTYPE_CHECKING:from..server.callbacksimportNextTickCallbackfrom.documentimportCallback,Document#-----------------------------------------------------------------------------# Globals and constants#-----------------------------------------------------------------------------__all__=('UnlockedDocumentProxy','without_document_lock',)#-----------------------------------------------------------------------------# General API#-----------------------------------------------------------------------------F=TypeVar("F",bound=Callable[...,Any])classNoLockCallback(Protocol[F]):__call__:Fnolock:Literal[True]
[docs]defwithout_document_lock(func:F)->NoLockCallback[F]:''' Wrap a callback function to execute without first obtaining the document lock. Args: func (callable) : The function to wrap Returns: callable : a function wrapped to execute without a |Document| lock. While inside an unlocked callback, it is completely *unsafe* to modify ``curdoc()``. The value of ``curdoc()`` inside the callback will be a specially wrapped version of |Document| that only allows safe operations, which are: * :func:`~bokeh.document.Document.add_next_tick_callback` * :func:`~bokeh.document.Document.remove_next_tick_callback` Only these may be used safely without taking the document lock. To make other changes to the document, you must add a next tick callback and make your changes to ``curdoc()`` from that second callback. Attempts to otherwise access or change the Document will result in an exception being raised. ``func`` can be a synchronous function, an async function, or a function decorated with ``asyncio.coroutine``. The returned function will be an async function if ``func`` is any of the latter two. '''ifasyncio.iscoroutinefunction(func):@wraps(func)asyncdef_wrapper(*args:Any,**kw:Any)->None:awaitfunc(*args,**kw)else:@wraps(func)def_wrapper(*args:Any,**kw:Any)->None:func(*args,**kw)wrapper=cast(NoLockCallback[F],_wrapper)wrapper.nolock=Truereturnwrapper
UNSAFE_DOC_ATTR_USAGE_MSG=("Only 'add_next_tick_callback' may be used safely without taking the document lock; ""to make other changes to the document, add a next tick callback and make your changes ""from that callback.")
[docs]classUnlockedDocumentProxy:# TODO(mypy): this needs to implement Document interface''' Wrap a Document object so that only methods that can safely be used from unlocked callbacks or threads are exposed. Attempts to otherwise access or change the Document results in an exception. '''