"""Implementation of the JSON configurable."""__author__='Paul Landes'fromtypingimportUnion,Dict,AnyfromdataclassesimportdataclassimportloggingfrompathlibimportPathfromioimportTextIOBaseimportjsonfromzensols.persistimportpersisted,PersistedWorkfrom.import(ConfigurableError,ConfigurableFileNotFoundError,DictionaryConfig)logger=logging.getLogger(__name__)
[docs]@dataclassclassJsonConfig(DictionaryConfig):"""A configurator that reads JSON as a two level dictionary. The top level keys are the section and the values are a single depth dictionary with string keys and values. A caveat is if all the values are terminal, in which case the top level singleton section is ``default_section`` given in the initializer and the section content is the single dictionary. """
[docs]def__init__(self,config_file:Union[Path,TextIOBase],default_section:str=None,deep:bool=False):"""Initialize. :param config_file: the configuration file path to read from; if the type is an instance of :class:`io.TextIOBase`, then read it as a file object :param config: configures this instance (see class docs) :param default_section: used as the default section when non given on the get methds such as :meth:`get_option` """ifisinstance(config_file,str):self.config_file=Path(config_file).expanduser()else:self.config_file=config_fileself._parsed_config=PersistedWork('_parsed_config',self)super().__init__(config=None,default_section=default_section,deep=deep)
def_narrow_root(self,conf:Dict[str,Any])->Dict[str,str]:ifnotisinstance(conf,dict):raiseConfigurableError(f'Expecting a root level dict: {self.config_file}')returnconf@persisted('_parsed_config')def_get_config(self)->Dict[str,Dict[str,Any]]:ifhasattr(self,'_ext_config'):returnself._ext_configiflogger.isEnabledFor(logging.INFO):logger.info(f'loading config: {self.config_file}')ifisinstance(self.config_file,TextIOBase):conf=json.load(self.config_file)self.config_file.seek(0)else:ifnotself.config_file.is_file():raiseConfigurableFileNotFoundError(self.config_file)withopen(self.config_file)asf:conf=json.load(f)conf=self._narrow_root(conf)iflogger.isEnabledFor(logging.DEBUG):logger.debug(f'raw json: {conf}')has_terminals=Truefork,vinconf.items():ifisinstance(v,dict):has_terminals=Falsebreakifhas_terminals:conf={self.default_section:conf}returnconfdef_set_config(self,source:Dict[str,Any]):self._ext_config=sourceself._parsed_config.clear()self.invalidate()