classNULL(metaclass=Singleton):r""" NULL class. `get` method in CHANfiG may accept `None` or `Ellipse`(`...`) as value of `default`. Therefore, it is mandatory to have a different default value for `default`. `Null` is an instance of `NULL` and is recommended to be used as `obj is Null`. """def__repr__(self):return"Null"def__nonzero__(self):returnFalsedef__len__(self):return0def__call__(self,*args:Any,**kwargs:Any):returnselfdef__contains__(self,name):returnFalsedef__iter__(self):returnselfdef__next__(self):raiseStopIterationdef__getattr__(self,name):returnselfdef__getitem__(self,index):returnself
classYamlLoader(SafeLoader):r""" YAML Loader for Config. """def__init__(self,stream):super().__init__(stream)self._root=os.path.abspath(os.path.dirname(stream.name))ifhasattr(stream,"name")elseos.getcwd()self.add_constructor("!include",self._include)self.add_constructor("!includes",self._includes)self.add_constructor("!env",self._env)@staticmethoddef_include(loader:YamlLoader,node):relative_path=loader.construct_scalar(node)include_path=os.path.join(loader._root,relative_path)ifnotos.path.exists(include_path):raiseFileNotFoundError(f"Included file not found: {include_path}")from.functionalimportloadreturnload(include_path)@staticmethoddef_includes(loader:YamlLoader,node):ifnotisinstance(node,SequenceNode):raiseConstructorError(None,None,f"!includes tag expects a sequence, got {node.id}",node.start_mark)files=loader.construct_sequence(node)return[YamlLoader._include(loader,ScalarNode("tag:yaml.org,2002:str",file))forfileinfiles]@staticmethoddef_env(loader:YamlLoader,node):env_var=loader.construct_scalar(node)value=os.getenv(env_var)ifvalueisNone:raiseValueError(f"Environment variable '{env_var}' not set.")returnvalue