#-----------------------------------------------------------------------------# Copyright (c) Anaconda, Inc., and Bokeh Contributors.# All rights reserved.## The full license is in the file LICENSE.txt, distributed with this software.#-----------------------------------------------------------------------------''' Provide a base class for all objects (called Bokeh Models) that can go ina Bokeh |Document|.'''#-----------------------------------------------------------------------------# Boilerplate#-----------------------------------------------------------------------------from__future__importannotationsimportlogging# isort:skiplog=logging.getLogger(__name__)#-----------------------------------------------------------------------------# Imports#-----------------------------------------------------------------------------# Standard library importsfromtypingimportTYPE_CHECKING,Any,Callable# Bokeh importsfrom..core.has_propsimportHasProps,Qualifiedfrom..util.dataclassesimportentries,is_dataclassifTYPE_CHECKING:from..core.typesimportIDfrom..documentimportDocumentfrom.modelimportModel#-----------------------------------------------------------------------------# Globals and constants#-----------------------------------------------------------------------------__all__=('HasDocumentRef','Qualified',# XXX: for backwards compatibility'collect_filtered_models','collect_models','get_class','visit_immediate_value_references','visit_value_and_its_immediate_references',)#-----------------------------------------------------------------------------# General API#-----------------------------------------------------------------------------#-----------------------------------------------------------------------------# Dev API#-----------------------------------------------------------------------------classHasDocumentRef:_document:Document|None_temp_document:Document|Nonedef__init__(self,*args,**kw):super().__init__(*args,**kw)self._document=Noneself._temp_document=None@propertydefdocument(self)->Document|None:''' The |Document| this model is attached to (can be ``None``) '''ifself._temp_documentisnotNone:returnself._temp_documentreturnself._document@document.setterdefdocument(self,doc:Document)->None:self._document=docdefcollect_filtered_models(discard:Callable[[Model],bool]|None,*input_values:Any)->list[Model]:''' Collect a duplicate-free list of all other Bokeh models referred to by this model, or by any of its references, etc, unless filtered-out by the provided callable. Iterate over ``input_values`` and descend through their structure collecting all nested ``Models`` on the go. Args: *discard (Callable[[Model], bool]) a callable which accepts a *Model* instance as its single argument and returns a boolean stating whether to discard the instance. The latter means that the instance will not be added to collected models nor will its references be explored. *input_values (Model) Bokeh models to collect other models from Returns: list(Model) '''ids:set[ID]=set()collected:list[Model]=[]queued:list[Model]=[]defqueue_one(obj:Model)->None:ifobj.idnotinidsandnot(callable(discard)anddiscard(obj)):queued.append(obj)forvalueininput_values:visit_value_and_its_immediate_references(value,queue_one)whilequeued:obj=queued.pop(0)ifobj.idnotinids:ids.add(obj.id)collected.append(obj)visit_immediate_value_references(obj,queue_one)returncollected
[docs]defcollect_models(*input_values:Any)->list[Model]:''' Collect a duplicate-free list of all other Bokeh models referred to by this model, or by any of its references, etc. Iterate over ``input_values`` and descend through their structure collecting all nested ``Models`` on the go. The resulting list is duplicate-free based on objects' identifiers. Args: *input_values (Model) Bokeh models to collect other models from Returns: list[Model] : all models reachable from this one. '''returncollect_filtered_models(None,*input_values)
[docs]defget_class(view_model_name:str)->type[Model]:''' Look up a Bokeh model class, given its view model name. Args: view_model_name (str) : A view model name for a Bokeh model to look up Returns: Model: the model class corresponding to ``view_model_name`` Raises: KeyError, if the model cannot be found Example: .. code-block:: python >>> from bokeh.model import get_class >>> get_class("Range1d") <class 'bokeh.models.ranges.Range1d'> '''# In order to look up from the model catalog that Model maintains, it# has to be created first. These imports ensure that all built-in Bokeh# models are represented in the catalog.from..importmodels# noqa: F401from..importplotting# noqa: F401from.modelimportModelknown_models=Model.model_class_reverse_mapifview_model_nameinknown_models:returnknown_models[view_model_name]else:raiseKeyError(f"View model name '{view_model_name}' not found")
defvisit_immediate_value_references(value:Any,visitor:Callable[[Model],None])->None:''' Visit all references to another Model without recursing into any of the child Model; may visit the same Model more than once if it's referenced more than once. Does not visit the passed-in value. '''ifisinstance(value,HasProps):forattrinvalue.properties_with_refs():child=getattr(value,attr)visit_value_and_its_immediate_references(child,visitor)else:visit_value_and_its_immediate_references(value,visitor)defvisit_value_and_its_immediate_references(obj:Any,visitor:Callable[[Model],None])->None:''' Visit Models, HasProps, and Python containers. Recurses down HasProps references and Python containers (does not recurse down Model subclasses). The ordering in this function is to optimize performance. We check the most comomn types (int, float, str) first so that we can quickly return in the common case. We avoid isinstance and issubclass checks in a couple places with `type` checks because isinstance checks can be slow. '''from.modelimportModeltyp=type(obj)iftypin{int,float,str}:# short circuit on common scalar typesreturniftypislistorissubclass(typ,(list,tuple)):# check common containersforiteminobj:visit_value_and_its_immediate_references(item,visitor)elifissubclass(typ,dict):forkey,valueinobj.items():visit_value_and_its_immediate_references(key,visitor)visit_value_and_its_immediate_references(value,visitor)elifissubclass(typ,HasProps):ifissubclass(typ,Model):visitor(obj)else:# this isn't a Model, so recurse into itvisit_immediate_value_references(obj,visitor)elifis_dataclass(obj):for_,valueinentries(obj):visit_value_and_its_immediate_references(value,visitor)#-----------------------------------------------------------------------------# Private API#-----------------------------------------------------------------------------#-----------------------------------------------------------------------------# Code#-----------------------------------------------------------------------------