Package: yaml-json-config-paco
Module yaml_json_configThis is a module to facilitate reading a config file or text string in json or yaml to a python object, vice versa.Not only flat key value style is supported, nested objects and list of objects can be supported.Demo of UsageLet's say there is a sample config file in yaml format to represent a companyaddress:"#368GuangZhouDistrict,GZ,GD"telephone:"+86206666666666"boss:name:alphaid:001age:40linkman:name:omegamembers:-name:alphaid:001age:40-name:betaid:002age:33-name:sigmaid:003age:38There are 4 shallow attributes, 'address', 'telephone' are simple strings, 'members' is a list of common nested structure, 'boss' is a nested structure with a nested structure 'linkman'.Shallow Load to Objectfromyaml_json_config_paco.GenericItemimportGenericItemclassCompany0(GenericItem):"""shallow load demo"""passif__name__=='__main__':company0=Company0.from_file("sample.yml")print(f"company0.address:{company0.address}")print(f"company0.telephone:{company0.telephone}")print(f"company0.boss:{company0.boss}")print(f"company0.members:{company0.members}")print(f"company0.dump_to_dict():{company0.dump_to_dict()}")print(f"company0.jsonify():\n{company0.jsonify()}")print(f"company0.yamlize():\n{company0.yamlize()}")run above code, you will get below, the 4 shallow attribute can be access directly by attribute name as object attributes, the nested structures are kept as OrderedDict and list of OrderedDict, the nested structure is just kept as OrderedDict in side OrderedDictcompany0.address:#368 GuangZhou District, GZ, GDcompany0.telephone:+86206666666666company0.boss:OrderedDict([('name','alpha'),('id',1),('age',40),('linkman',OrderedDict([('name','omega')]))])company0.members:[OrderedDict([('name','alpha'),('id',1),('age',40)]),OrderedDict([('name','beta'),('id',2),('age',33)]),OrderedDict([('name','sigma'),('id',3),('age',38)])]company0.dump_to_dict():OrderedDict([('address','#368 GuangZhou District, GZ, GD'),('telephone','+86206666666666'),('boss',OrderedDict([('name','alpha'),('id',1),('age',40),('linkman',OrderedDict([('name','omega')]))])),('members',[OrderedDict([('name','alpha'),('id',1),('age',40)]),OrderedDict([('name','beta'),('id',2),('age',33)]),OrderedDict([('name','sigma'),('id',3),('age',38)])])])# This is the way to dump to jsoncompany0.jsonify():{"address":"#368 GuangZhou District, GZ, GD","telephone":"+86206666666666","boss":{"name":"alpha","id":1,"age":40,"linkman":{"name":"omega"}},"members":[{"name":"alpha","id":1,"age":40},{"name":"beta","id":2,"age":33},{"name":"sigma","id":3,"age":38}]}# this is the way to dump to yamlcompany0.yamlize():address:'#368 GuangZhou District, GZ, GD'telephone:'+86206666666666'boss:name:alphaid:1age:40linkman:name:omegamembers:-name:alphaid:1age:40-name:betaid:2age:33-name:sigmaid:3age:38Deep Load to Objectfromyaml_json_config_paco.GenericItemimportGenericItemclassPerson(GenericItem):passclassCompany(GenericItem):def__nested__(self):# return a tuple for GenericItem to build nested objectsreturn(('boss',Person),# tell GenericItem to load boss as Person Class object('members',Person,list),# tell GenericItem to load members as list of Person Class objects)if__name__=='__main__':company=Company.from_file("sample.yml")print(f"company.address:{company.address}")print(f"company.boss:{company.boss}")print(f"company.boss.name:{company.boss.name}")print(f"company.boss.linkman:{company.boss.linkman}")print(f"company.members:{company.members}")print(f"company.members[1]:{company.members[1]}")print(f"company.members[1].name:{company.members[1].name}")print(f"company.jsonify():\n{company.jsonify()}")print(f"company.yamlize():\n{company.yamlize()}")run above will get below.
The 'attr_to_convert_to_list' and 'attr_to_convert' will be called by GenericItem so that it will get information how to deal with some nested attributes.
The nested can be recursive.company.address:#368 GuangZhou District, GZ, GDcompany.boss:{"name":"alpha","id":1,"age":40,"linkman":{"name":"omega"}}# this time, boss' name can be access by attribute namecompany.boss.name:alpha# linkman is not specified with a python class, so it is kept as a OrderDictcompany.boss.linkman:OrderedDict([('name','omega')])# members is a list of Person, so it can be access by index to the Person object, and access by Person attributescompany.members:[{"name":"alpha","id":1,"age":40},{"name":"beta","id":2,"age":33},{"name":"sigma","id":3,"age":38}]company.members[1]:{"name":"beta","id":2,"age":33}company.members[1].name:betacompany.jsonify():{"address":"#368 GuangZhou District, GZ, GD","telephone":"+86206666666666","boss":{"name":"alpha","id":1,"age":40,"linkman":{"name":"omega"}},"members":[{"name":"alpha","id":1,"age":40},{"name":"beta","id":2,"age":33},{"name":"sigma","id":3,"age":38}]}company.yamlize():address:'#368 GuangZhou District, GZ, GD'telephone:'+86206666666666'boss:name:alphaid:1age:40linkman:name:omegamembers:-name:alphaid:1age:40-name:betaid:2age:33-name:sigmaid:3age:38Default Value and Optiontal Attributefromyaml_json_config_paco.GenericItemimportGenericItemclassPerson(GenericItem):def__init__(self):self.name=None# set None as mandatory attributeself.id="na"# set value as default valueself.age="na"# set value as default valueself.linkman=""# set to "" as optional, as this attribute 'linkman' is mentioned in self.__nested__ method,# and some of Person object do not have linkman, this line is needed.def__nested__(self):return(('linkman',Person),)# tell GenericItem to load 'linkman' attribute as a Person objectclassCompany(GenericItem):def__nested__(self):return(('boss',Person),# tell GenericItem to load 'boss' as Person Class object('members',Person,list))# tell GenericItem to load 'members' as list of Person Class objectsif__name__=='__main__':company=Company.from_file("sample.yml")print(f"company.address:{company.address}")print(f"company.boss:{company.boss}")print(f"company.boss.name:{company.boss.name}")print(f"company.boss.linkman.name:{company.boss.linkman.name}")print(f"company.members:{company.members}")print(f"company.members[1]:{company.members[1]}")print(f"company.members[1].name:{company.members[1].name}")print(f"company.members[1].linkman:{company.members[1].linkman}")run above to get below,company.address:#368 GuangZhou District, GZ, GDcompany.boss:{"name":"alpha","id":1,"age":40,"linkman":{"name":"omega","id":"na",# with default value as the config file not set"age":"na"# with default value}}company.boss.name:alphacompany.boss.linkman.name:omegacompany.members:[{# the optional linkman is not present here"name":"alpha","id":1,"age":40},{"name":"beta","id":2,"age":33},{"name":"sigma","id":3,"age":38}]company.members[1]:{"name":"beta","id":2,"age":33}company.members[1].name:betacompany.members[1].linkman:# pack to yamlcompany.yamlize():address:'#368 GuangZhou District, GZ, GD'telephone:'+86206666666666'boss:name:alphaid:1age:40linkman:name:omegaid:naage:namembers:-name:alphaid:1age:40-name:betaid:2age:33-name:sigmaid:3age:38Dump and Load, Json or YamlA GenericItem classmethod 'from_file' is provided to load from file.
A GenericItem method 'dump_to_file' is provide to dump to file.
The format is automatically recognized by the suffix of the filename you feed, 'json', 'yml', 'yaml' are supported.Example:company.dump_to_file("sample.json")withopen("sample.json","r",encoding="utf-8")asf:print(f"read sample.json file:\n{f.read()}\n")company2=Company.from_file("sample.json")print(f"company2.yamlize():\n{company2.yamlize()}")Run above to get belowreadsample.jsonfile:{"address":"#368 GuangZhou District, GZ, GD","telephone":"+86206666666666","boss":{"name":"alpha","id":1,"age":40,"linkman":{"name":"omega"}},"members":[{"name":"alpha","id":1,"age":40},{"name":"beta","id":2,"age":33},{"name":"sigma","id":3,"age":38}]}company2.yamlize():address:'#368 GuangZhou District, GZ, GD'telephone:'+86206666666666'boss:name:alphaid:1age:40linkman:name:omegamembers:-name:alphaid:1age:40-name:betaid:2age:33-name:sigmaid:3age:38