#-----------------------------------------------------------------------------# Copyright (c) Anaconda, Inc. All rights reserved.## Powered by the Bokeh Development Team.## The full license is in the file LICENSE.txt, distributed with this software.#-----------------------------------------------------------------------------""" Provide the Struct property."""#-----------------------------------------------------------------------------# Boilerplate#-----------------------------------------------------------------------------from__future__importannotationsimportlogging# isort:skiplog=logging.getLogger(__name__)#-----------------------------------------------------------------------------# Imports#-----------------------------------------------------------------------------# Standard library importsfromtypesimportSimpleNamespacefromtypingimportAny,Generic,TypeVar# Bokeh importsfrom.basesimportParameterizedProperty,Property#-----------------------------------------------------------------------------# Globals and constants#-----------------------------------------------------------------------------__all__=('Struct',)T=TypeVar("T")#-----------------------------------------------------------------------------# General API#-----------------------------------------------------------------------------classOptional(Generic[T]):def__init__(self,type_param:Property[T]):self.type_param=type_param
[docs]classStruct(ParameterizedProperty[T]):""" Accept values that are structures. """_fields:dict[str,Property[Any]]_optional:set[str]def__init__(self,**fields:Any)->None:default=fields.pop("default",None)help=fields.pop("help",None)ifnotfields:raiseValueError("expected specification of fields, got nothing")self._fields={}self._optional=set()forname,typeinfields.items():ifisinstance(type,Optional):self._optional.add(name)type=type.type_paramself._fields[name]=self._validate_type_param(type,help_allowed=True)# XXXsuper().__init__(default=default,help=help)def__eq__(self,other:object)->bool:ifisinstance(other,self.__class__):returnsuper().__eq__(other)andself._fields==other._fieldsandself._optional==other._optionalelse:returnFalse@propertydeftype_params(self):returnlist(self._fields.values())defvalidate(self,value:Any,detail:bool=True):super().validate(value,detail)ifisinstance(value,SimpleNamespace):value=value.__dict__ifisinstance(value,dict)andlen(value)<=len(self._fields):forname,typeinself._fields.items():ifnamenotinvalue:ifnamenotinself._optional:breakelifnottype.is_valid(value[name]):breakelse:fornameinvalue.keys():ifnamenotinself._fields:breakelse:returnmsg=""ifnotdetailelsef"expected an element of {self}, got {value!r}"raiseValueError(msg)def__str__(self)->str:class_name=self.__class__.__name__fields=", ".join(f"{name}={typ}"forname,typinself._fields.items())returnf"{class_name}({fields})"
#-----------------------------------------------------------------------------# Dev API#-----------------------------------------------------------------------------classstruct(SimpleNamespace):""" Allow access unnamed struct with attributes and keys. .. note:: This feature is experimental and may change in the short term. """def__getitem__(self,key:str)->Any:returngetattr(self,key)def__setitem__(self,key:str,val:Any)->None:setattr(self,key,val)def__delitem__(self,key:str)->None:delattr(self,key)#-----------------------------------------------------------------------------# Private API#-----------------------------------------------------------------------------#-----------------------------------------------------------------------------# Code#-----------------------------------------------------------------------------