#-----------------------------------------------------------------------------# Copyright (c) 2012 - 2022, Anaconda, Inc., and Bokeh Contributors.# All rights reserved.## The full license is in the file LICENSE.txt, distributed with this software.#-----------------------------------------------------------------------------""" Provide the Either property.The Either property is used to construct properties that an accept any ofmultiple possible types."""#-----------------------------------------------------------------------------# Boilerplate#-----------------------------------------------------------------------------from__future__importannotationsimportlogging# isort:skiplog=logging.getLogger(__name__)#-----------------------------------------------------------------------------# Imports#-----------------------------------------------------------------------------# Bokeh importsfrom...util.stringimportnice_joinfrom._sphinximportproperty_link,register_type_link,type_linkfrom.basesimportDeserializationError,ParameterizedPropertyfrom.singletonsimportIntrinsic#-----------------------------------------------------------------------------# Globals and constants#-----------------------------------------------------------------------------__all__=('Either',)#-----------------------------------------------------------------------------# General API#-----------------------------------------------------------------------------
[docs]classEither(ParameterizedProperty):""" Accept values according to a sequence of other property types. Example: .. code-block:: python >>> class EitherModel(HasProps): ... prop = Either(Bool, Int, Auto) ... >>> m = EitherModel() >>> m.prop = True >>> m.prop = 10 >>> m.prop = "auto" >>> m.prop = 10.3 # ValueError !! >>> m.prop = "foo" # ValueError !! """def__init__(self,tp1,tp2,*type_params,default=Intrinsic,help=None,serialized=None,readonly=False)->None:type_params=list(map(self._validate_type_param,(tp1,tp2)+type_params))default=defaultifdefaultisnotIntrinsicelsetype_params[0]._raw_default()super().__init__(default=default,help=help,serialized=serialized,readonly=readonly)self._type_params=type_paramsfortpinself._type_params:self.alternatives.extend(tp.alternatives)def__str__(self)->str:class_name=self.__class__.__name__item_types=", ".join(str(x)forxinself.type_params)returnf"{class_name}({item_types})"@propertydeftype_params(self):returnself._type_paramsdeffrom_json(self,json,*,models=None):fortpinself.type_params:try:returntp.from_json(json,models=models)exceptDeserializationError:passraiseDeserializationError(f"{self} couldn't deserialize {json}")deftransform(self,value):forparaminself.type_params:try:returnparam.transform(value)exceptValueError:passraiseValueError(f"Could not transform {value!r}")defvalidate(self,value,detail=True):super().validate(value,detail)ifany(param.is_valid(value)forparaminself.type_params):returnmsg=""ifnotdetailelsef"expected an element of either {nice_join(self.type_params)}, got {value!r}"raiseValueError(msg)defwrap(self,value):fortpinself.type_params:value=tp.wrap(value)returnvalue
# TODO (bev) implement this# def _may_have_unstable_default(self):# return any(tp._may_have_unstable_default() for tp in self.type_params)#-----------------------------------------------------------------------------# Dev API#-----------------------------------------------------------------------------#-----------------------------------------------------------------------------# Private API#-----------------------------------------------------------------------------#-----------------------------------------------------------------------------# Code#-----------------------------------------------------------------------------@register_type_link(Either)def_sphinx_type_link(obj):subtypes=", ".join(type_link(x)forxinobj.type_params)returnf"{property_link(obj)}({subtypes})"