#-----------------------------------------------------------------------------# Copyright (c) Anaconda, Inc., and Bokeh Contributors.# All rights reserved.## The full license is in the file LICENSE.txt, distributed with this software.#-----------------------------------------------------------------------------""" Provide the Instance property.The Instance property is used to construct object graphs of Bokeh models,where one Bokeh model refers to another."""#-----------------------------------------------------------------------------# Boilerplate#-----------------------------------------------------------------------------from__future__importannotationsimportlogging# isort:skiplog=logging.getLogger(__name__)#-----------------------------------------------------------------------------# Imports#-----------------------------------------------------------------------------# Standard library importsimporttypesfromimportlibimportimport_modulefromtypingimport(Any,Callable,Generic,TypeVar,)# Bokeh importsfrom..has_propsimportHasPropsfrom..serializationimportSerializablefrom._sphinximportmodel_link,property_link,register_type_linkfrom.basesimportInit,Propertyfrom.singletonsimportUndefined#-----------------------------------------------------------------------------# Globals and constants#-----------------------------------------------------------------------------__all__=('Instance','InstanceDefault','Object',)T=TypeVar("T",bound=object)S=TypeVar("S",bound=Serializable)#-----------------------------------------------------------------------------# General API#-----------------------------------------------------------------------------
[docs]classObject(Property[T]):""" Accept values that are instances of any class. .. note:: This is primarily useful for validation purpose. Non-serializable objects will fail regardless during the serialization process. """_instance_type:type[T]|Callable[[],type[T]]|strdef__init__(self,instance_type:type[T]|Callable[[],type[T]]|str,default:Init[T]=Undefined,help:str|None=None):ifnot(isinstance(instance_type,(type,str))orcallable(instance_type)):raiseValueError(f"expected a type, fn() -> type, or string, got {instance_type}")ifisinstance(instance_type,type):self._assert_type(instance_type)self._instance_type=instance_typesuper().__init__(default=default,help=help)@staticmethoddef_assert_type(instance_type:type[Any])->None:passdef__str__(self)->str:class_name=self.__class__.__name__instance_type=self.instance_type.__name__returnf"{class_name}({instance_type})"@propertydefhas_ref(self)->bool:returnTrue@propertydefinstance_type(self)->type[T]:instance_type:type[Serializable]ifisinstance(self._instance_type,type):instance_type=self._instance_typeelifisinstance(self._instance_type,str):module,name=self._instance_type.rsplit(".",1)instance_type=getattr(import_module(module,"bokeh"),name)self._assert_type(instance_type)self._instance_type=instance_typeelse:instance_type=self._instance_type()self._assert_type(instance_type)self._instance_type=instance_typereturninstance_typedefvalidate(self,value:Any,detail:bool=True)->None:super().validate(value,detail)ifisinstance(value,self.instance_type):returninstance_type=self.instance_type.__name__value_type=type(value).__name__msg=""ifnotdetailelsef"expected an instance of type {instance_type}, got {value} of type {value_type}"raiseValueError(msg)def_may_have_unstable_default(self):# because the instance value is mutablereturnself._defaultisnotUndefined
[docs]classInstance(Object[S]):""" Accept values that are instances of serializable types (e.g. |HasProps|). """@staticmethoddef_assert_type(instance_type:type[Any])->None:ifnot(isinstance(instance_type,type)andissubclass(instance_type,Serializable)):raiseValueError(f"expected a subclass of Serializable (e.g. HasProps), got {instance_type}")
I=TypeVar("I",bound=HasProps)
[docs]classInstanceDefault(Generic[I]):""" Provide a deferred initializer for Instance defaults. This is useful for Bokeh models with Instance properties that should have unique default values for every model instance. Using an InstanceDefault will afford better user-facing documentation than a lambda initializer. """def__init__(self,model:type[I],**kwargs:Any)->None:self._model=modelself._kwargs=kwargsdef__call__(self)->I:returnself._model(**self._kwargs)def__repr__(self)->str:kwargs=", ".join(f"{key}={val}"forkey,valinself._kwargs.items())returnf"<Instance: {self._model.__qualified_model__}({kwargs})>"
#-----------------------------------------------------------------------------# Dev API#-----------------------------------------------------------------------------#-----------------------------------------------------------------------------# Private API#-----------------------------------------------------------------------------#-----------------------------------------------------------------------------# Code#-----------------------------------------------------------------------------@register_type_link(Instance)def_sphinx_type_link(obj:Instance[Any])->str:# Sphinx links may be generated during docstring processing which means# we can't necessarily evaluate pure function (e.g. lambda) Instance# initializers, since they may contain circular references to the (not# yet fully defined at this point) Modelifisinstance(obj._instance_type,types.FunctionType):returnf"{property_link(obj)}"ifisinstance(obj._instance_type,str):returnf"{property_link(obj)}({obj._instance_type!r})"model=obj.instance_typemodel_name=f"{model.__module__}.{model.__name__}"returnf"{property_link(obj)}({model_link(model_name)})"