#-----------------------------------------------------------------------------# Copyright (c) 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 importsfromcollections.abcimport(Container,Iterable,Mapping,Sequence,Sized,)fromtypingimportTYPE_CHECKING,Any,TypeVar# Bokeh importsfrom._sphinximportproperty_link,register_type_link,type_linkfrom.basesimport(ContainerProperty,Init,Property,SingleParameterizedProperty,TypeOrInst,)from.descriptorsimportColumnDataPropertyDescriptorfrom.enumimportEnumfrom.numericimportIntfrom.singletonsimportIntrinsic,Undefinedfrom.wrappersimport(PropertyValueColumnData,PropertyValueDict,PropertyValueList,PropertyValueSet,)ifTYPE_CHECKING:from...document.eventsimportDocumentPatchedEvent#-----------------------------------------------------------------------------# Globals and constants#-----------------------------------------------------------------------------__all__=('Array','ColumnData','Dict','Len','List','NonEmpty','RelativeDelta','RestrictedDict','Seq','Set','Tuple',)T=TypeVar("T")#-----------------------------------------------------------------------------# General API#-----------------------------------------------------------------------------
[docs]classSeq(ContainerProperty[T]):""" Accept non-string ordered sequences of values, e.g. list, tuple, array. """def__init__(self,item_type:TypeOrInst[Property[T]],*,default:Init[T]=Undefined,help:str|None=None)->None:super().__init__(item_type,default=default,help=help)@propertydefitem_type(self):returnself.type_params[0]defvalidate(self,value:Any,detail:bool=True)->None:super().validate(value,True)ifself._is_seq(value)andall(self.item_type.is_valid(item)foriteminvalue):returnifself._is_seq(value):invalid=[]foriteminvalue:ifnotself.item_type.is_valid(item):invalid.append(item)msg=""ifnotdetailelsef"expected an element of {self}, got seq with invalid items {invalid!r}"raiseValueError(msg)msg=""ifnotdetailelsef"expected an element of {self}, got {value!r}"raiseValueError(msg)@classmethoddef_is_seq(cls,value:Any)->bool:return((isinstance(value,Sequence)orcls._is_seq_like(value))andnotisinstance(value,str))@classmethoddef_is_seq_like(cls,value:Any)->bool:return(isinstance(value,Container|Sized|Iterable)andhasattr(value,"__getitem__")# NOTE: this is what makes it disallow set typeandnotisinstance(value,Mapping))
[docs]classList(Seq[T]):""" Accept Python list values. """def__init__(self,item_type:TypeOrInst[Property[T]],*,default:Init[T]=[],help:str|None=None)->None:# TODO: refactor to not use mutable objects as default values.# Left in place for now because we want to allow None to express# optional values. Also in Dict.super().__init__(item_type,default=default,help=help)defwrap(self,value:list[T])->PropertyValueList[T]:""" Some property types need to wrap their values in special containers, etc. """ifisinstance(value,list):ifisinstance(value,PropertyValueList):returnvalueelse:returnPropertyValueList(value)else:returnvalue@classmethoddef_is_seq(cls,value:Any):returnisinstance(value,list)
[docs]classSet(Seq[T]):""" Accept Python ``set()`` values. """def__init__(self,item_type:TypeOrInst[Property[T]],*,default:Init[T]=set(),help:str|None=None)->None:# TODO: refactor to not use mutable objects as default values.# Left in place for now because we want to allow None to express# optional values. Also in Dict.super().__init__(item_type,default=default,help=help)defwrap(self,value:set[T])->PropertyValueSet[T]:""" Some property types need to wrap their values in special containers, etc. """ifisinstance(value,set):ifisinstance(value,PropertyValueSet):returnvalueelse:returnPropertyValueSet(value)else:returnvalue@classmethoddef_is_seq(cls,value:Any)->bool:returnisinstance(value,set)
[docs]classDict(ContainerProperty[Any]):""" Accept Python dict values. If a default value is passed in, then a shallow copy of it will be used for each new use of this property. """def__init__(self,keys_type:TypeOrInst[Property[Any]],values_type:TypeOrInst[Property[Any]],*,default:Init[T]={},help:str|None=None)->None:super().__init__(keys_type,values_type,default=default,help=help)@propertydefkeys_type(self):returnself.type_params[0]@propertydefvalues_type(self):returnself.type_params[1]defvalidate(self,value:Any,detail:bool=True)->None:super().validate(value,detail)key_is_valid=self.keys_type.is_validvalue_is_valid=self.values_type.is_validexpected=f"expected a dict of type {self}"ifnotisinstance(value,dict):raiseValueError(f"{expected}, got a value of type {type(value)}"ifdetailelse"")bad_keys=[str(k)forkinvalueifnotkey_is_valid(k)]bad_value_keys=[str(k)for(k,v)invalue.items()ifnotvalue_is_valid(v)]exception_header=f"{expected}, got a dict with"bad_keys_str=f"invalid keys: {', '.join(bad_keys)}"bad_value_keys_str=f"invalid values for keys: {', '.join(bad_value_keys)}"err=Noneif(has_bad_keys:=any(bad_keys))&(has_bad_key_values:=any(bad_value_keys)):err=ValueError(f"{exception_header}{bad_keys_str} and {bad_value_keys_str}")elifhas_bad_keys:err=ValueError(f"{exception_header}{bad_keys_str}")elifhas_bad_key_values:err=ValueError(f"{exception_header}{bad_value_keys_str}")iferr:raiseerrifdetailelseValueError("")defwrap(self,value):""" Some property types need to wrap their values in special containers, etc. """ifisinstance(value,dict):ifisinstance(value,PropertyValueDict):returnvalueelse:returnPropertyValueDict(value)else:returnvalue
[docs]classColumnData(Dict):""" Accept a Python dictionary suitable as the ``data`` attribute of a :class:`~bokeh.models.sources.ColumnDataSource`. This class is a specialization of ``Dict`` that handles efficiently encoding columns that are NumPy arrays. """defmake_descriptors(self,base_name):""" Return a list of ``ColumnDataPropertyDescriptor`` instances to install on a class, in order to delegate attribute access to this property. Args: base_name (str) : the name of the property these descriptors are for Returns: list[ColumnDataPropertyDescriptor] The descriptors returned are collected by the ``MetaHasProps`` metaclass and added to ``HasProps`` subclasses during class creation. """return[ColumnDataPropertyDescriptor(base_name,self)]def_hinted_value(self,value:Any,hint:DocumentPatchedEvent|None)->Any:from...document.eventsimportColumnDataChangedEvent,ColumnsStreamedEventifisinstance(hint,ColumnDataChangedEvent):return{col:hint.model.data[col]forcolinhint.cols}ifisinstance(hint,ColumnsStreamedEvent):returnhint.datareturnvaluedefwrap(self,value):""" Some property types need to wrap their values in special containers, etc. """ifisinstance(value,dict):ifisinstance(value,PropertyValueColumnData):returnvalueelse:returnPropertyValueColumnData(value)else:returnvalue
[docs]classTuple(ContainerProperty):""" Accept Python tuple values. """def__init__(self,*type_params:TypeOrInst[Property[Any]],default:Init[T]=Undefined,help:str|None=None)->None:super().__init__(*type_params,default=default,help=help)defvalidate(self,value:Any,detail:bool=True)->None:super().validate(value,detail)ifisinstance(value,tuple|list)andlen(self.type_params)==len(value):ifall(type_param.is_valid(item)fortype_param,iteminzip(self.type_params,value)):returnmsg=""ifnotdetailelsef"expected an element of {self}, got {value!r}"raiseValueError(msg)deftransform(self,value):""" Change the value into a JSON serializable format. """returntuple(typ.transform(x)for(typ,x)inzip(self.type_params,value))
[docs]classRelativeDelta(Dict):""" Accept RelativeDelta dicts for time delta values. """def__init__(self,default={},*,help:str|None=None)->None:keys=Enum("years","months","days","hours","minutes","seconds","microseconds")values=Intsuper().__init__(keys,values,default=default,help=help)def__str__(self)->str:returnself.__class__.__name__
[docs]classRestrictedDict(Dict):""" Check for disallowed key(s). """def__init__(self,keys_type,values_type,disallow,default={},*,help:str|None=None)->None:self._disallow=set(disallow)super().__init__(keys_type=keys_type,values_type=values_type,default=default,help=help)defvalidate(self,value:Any,detail:bool=True)->None:super().validate(value,detail)error_keys=self._disallow&value.keys()iferror_keys:msg=""ifnotdetailelsef"Disallowed keys: {error_keys!r}"raiseValueError(msg)
TSeq=TypeVar("TSeq",bound=Seq[Any])classNonEmpty(SingleParameterizedProperty[TSeq]):""" Allows only non-empty containers. """def__init__(self,type_param:TypeOrInst[TSeq],*,default:Init[TSeq]=Intrinsic,help:str|None=None)->None:super().__init__(type_param,default=default,help=help)defvalidate(self,value:Any,detail:bool=True)->None:super().validate(value,detail)ifnotvalue:msg=""ifnotdetailelse"Expected a non-empty container"raiseValueError(msg)classLen(SingleParameterizedProperty[TSeq]):""" Allows only containers of the given length. """def__init__(self,type_param:TypeOrInst[TSeq],length:int,*,default:Init[TSeq]=Intrinsic,help:str|None=None)->None:super().__init__(type_param,default=default,help=help)self.length=lengthdefvalidate(self,value:Any,detail:bool=True)->None:super().validate(value,detail)iflen(value)!=self.length:msg=""ifnotdetailelsef"Expected a container of length #{self.length}, got #{len(value)}"raiseValueError(msg)#-----------------------------------------------------------------------------# Dev API#-----------------------------------------------------------------------------#-----------------------------------------------------------------------------# Private API#-----------------------------------------------------------------------------#-----------------------------------------------------------------------------# Code#-----------------------------------------------------------------------------@register_type_link(Dict)def_sphinx_type_dict(obj:Dict):returnf"{property_link(obj)}({type_link(obj.keys_type)}, {type_link(obj.values_type)})"@register_type_link(Seq)def_sphinx_type_seq(obj:Seq[Any]):returnf"{property_link(obj)}({type_link(obj.item_type)})"@register_type_link(Tuple)def_sphinx_type_tuple(obj:Tuple):item_types=", ".join(type_link(x)forxinobj.type_params)returnf"{property_link(obj)}({item_types})"