#coding: utf-8fromcollectionsimportOrderedDictfromcopyimportdeepcopyfromastimportliteral_evalimportre__all__=['parse_ini']default='__default__'# [ production ]REGEX_SEC=re.compile('\[\s?([\w]+)\s?\]',re.IGNORECASE)# [ dev : production ]REGEX_I_SEC=re.compile('\[\s?([\w]+)\s?:\s?([\w]+)\s?\]',re.IGNORECASE)defparse_ini(ini_path,env=None):ini=_Obj({default:_Obj()})current_section=defaultwithopen(ini_path)asf:forlineinf:line=line.strip()# empty line or a comment lineifnotlineorline.isspace()orline[0]==';'orline[0]=='#':continue# sectionifline[0]=='[':# a common section result=REGEX_SEC.search(line)ifresultisnotNone:section=result.group(1)ini[section]=deepcopy(ini[default])else:# a inherited section result=REGEX_I_SEC.search(line)ifresultinNone:raiseSyntaxError("Invalid section declaration")section=result.group(1)parent=result.group(2)# wrong parent sectionifparentnotinini:raiseMissingSectionError("'%s' inherits from '%s' which hasn't been declared."%(section,parent))ini[section]=deepcopy(ini[parent])current_section=section# optionelse:pieces=line.split("=")# master.foo.barvals=pieces[0].strip().split('.')# ['bar', 'foo', 'master']vals.reverse()data=_cast(pieces[1].strip())working_obj=ini[current_section]whilevals:iflen(vals)==1:working_obj[vals.pop()]=dataelse:val=vals.pop()ifvalnotinworking_obj:working_obj[val]=_Obj()working_obj=working_obj[val]ifenvisnotNone:ifenvnotinini:raiseMissingSectionError("The section being loaded does not exist.")returnini[env]returninidef_cast(val):try:val=literal_eval(val)except:passreturnvalclass_Obj(OrderedDict):""" A dict that allows for object-like property access syntax. """def__copy__(self):data=self.__dict__.copy()return_Obj(data)def__getattr__(self,name):try:returnself[name]exceptKeyError:try:returnself[default][name]exceptKeyError:raiseAttributeError(name)def__contains__(self,name):try:self.__getattr__(name)exceptAttributeError:returnFalsereturnTrueclassMissingSectionError(Exception):""" Thrown when a section header inherits from a section that has yet been undeclared. """pass