对于我的Python应用程序,我需要创建一个配置文件解析器,该解析器将能够解析该配置文件的多个版本。
目标是创建一个配置解析器,无论配置文件的版本如何,该解析器都将是安全的。
以以下示例为例:今天,我将软件和特定的配置文件发送给客户端。明天,我将发布一个新的软件版本。如何确保它与今天发送的配置文件兼容?还有下一个软件版本?
这里是一个示例:说我有config_1.yaml
配置文件:
version: 1
digits:
- one
- two
- three
我希望我的Python阅读:
{'digits': ['one', 'two', 'three'], 'version': 1}
然后一段时间,我将配置文件格式更新为config_2.yaml
:
version: 2
digits:
- one
- two
- three
colors:
red: #FF0000
green: #00FF00
blue: #0000FF
我希望我的软件将此配置读取为:
{'colors': {'blue': '#0000FF', 'green': '#00FF00', 'red': '#FF0000'},
'digits': ['one', 'two', 'three'],
'version': 2}
但是,我还需要此软件版本才能将config_1.yaml
读为:
{'colors': [], 'digits': ['one', 'two', 'three'], 'version': 1}
以此类推:发布第三个软件版本时,我希望它能够读取config_3.yaml
:
version: 3
digits:
- one
- two
- three
colors:
red: '#FF0000'
green: '#00FF00'
blue: '#0000FF'
constants:
pi: 3.1415
e: 2.71828
为:
{'colors': {'blue': '#0000FF', 'green': '#00FF00', 'red': '#FF0000'},
'constants': {'e': 2.71828, 'pi': 3.1415},
'digits': ['one', 'two', 'three'],
'version': 3}
分别为config_1.yaml
和config_2.yaml
:
{'colors': [], 'constants': {}, 'digits': ['one', 'two', 'three'], 'version': 1}
{'colors': {'blue': '#0000FF', 'green': '#00FF00', 'red': '#FF0000'},
'constants': {},
'digits': ['one', 'two', 'three'],
'version': 2}
我编写了以下代码以获得这些结果:
import yaml
from pprint import pprint
def read_yaml(f_path):
with open(f_path, 'r', encoding='utf-8') as fid:
config = yaml.load(fid, Loader=yaml.FullLoader)
return config
def read_config_1(config):
pass
def read_config_2(config):
if config['version'] < 2:
read_config_1(config)
# the "colors" part of the config was added between versions 1 and 2
config['colors'] = []
def read_config_3(config):
if config['version'] < 3:
read_config_2(config)
# the "constants" part of the config was added between versions 2 and 3
config['constants'] = {}
def read_config_file(f_path):
config = read_yaml(f_path)
read_config_3(config)
return config
if __name__ == '__main__':
for f_name in [f'config_{i}.yaml' for i in range(1, 4)]:
print('-'*20 + ' ' + f_name + ' ' + '-'*20)
config = read_config_file(f_name)
pprint(config)
print()
有人对此代码有任何评论吗,或是否有效率的建议?
答案 0 :(得分:1)
这似乎有点麻烦。默认值 分布在多个值上,没有封装。
您应该制作一个
从dict
派生的配置类(假设您要
它可以与dict
一起使用(订阅)。其__init__
应该
将最新版本的所有值初始化为合理的值(空
列表,字典等)。之后,您读取YAML文件并覆盖值
你在那里找到。
from pprint import pprint
from pathlib import Path
import ruamel.yaml
CONFIG_VERSION = 3 # latest version number used in config files
yaml = ruamel.yaml.YAML(typ='safe')
class Config(dict):
def __init__(self, file_name):
self['constants'] = {} # added version 3
self['colors'] = [] # added version 2
if not hasattr(file_name, 'open'):
file_name = Path(file_name)
d = yaml.load(file_name)
if d['version'] > CONFIG_VERSION:
print("don't know how to handle newer config version", d['version'])
# optionally do something special for some versions
# if d['version'] < NR:
# self.update_from_NR(d)
# else:
self.update(d)
d['version'] = CONFIG_VERSION # in case you dump the config
if __name__ == '__main__':
for f_name in [f'config_{i}.yaml' for i in range(1, 4)]:
print('-'*20 + ' ' + f_name + ' ' + '-'*20)
config = Config(f_name)
pprint(config)
print()
给出:
-------------------- config_1.yaml --------------------
{'colors': [], 'constants': {}, 'digits': ['one', 'two', 'three'], 'version': 1}
-------------------- config_2.yaml --------------------
{'colors': {'blue': None, 'green': None, 'red': None},
'constants': {},
'digits': ['one', 'two', 'three'],
'version': 2}
-------------------- config_3.yaml --------------------
{'colors': {'blue': '#0000FF', 'green': '#00FF00', 'red': '#FF0000'},
'constants': {'e': 2.71828, 'pi': 3.1415},
'digits': ['one', 'two', 'three'],
'version': 3}
我认为使用PyYAML的FullConstructor不会帮助您做得更好 配置文件,安全构建和对任何对象使用显式标签 您想要标记的结构更好,更用户友好。
您当然可以在PyYAML中执行上述操作,前提是您真的只 想要支持YAML 1.1(在2009年被YAML 1.2取代,将近10 年前),并且只需要PyYAML的1.1子集 可以加载。 (免责声明:我是ruamel.yaml的作者)