解析和验证 YAML 配置文件的最佳方式
the best way to parse and validate YAML configuration file
我们有一个在 YAML 中存储设置的项目(设置文件由 ansible 脚本生成)。现在我们使用 pyyaml 来解析 YAML 格式,使用 marshmallow 来验证设置。我很高兴在 YAML 中存储设置,但我不认为 marshmellow 是我需要的工具(模式很难阅读,我不需要设置序列化,想要 xsd 之类的东西)。那么在项目中验证设置的最佳实践是什么,也许有独立于语言的方式? (我们正在使用 python 2.7)
YAML 设置:
successive:
worker:
cds_process_number: 0 # positive integer or zero
spider_interval: 10 # positive integer
run_worker_sh: /home/lmakeev/CDS/releases/master/scripts/run_worker.sh # OS path
allow:
- "*" # regular expression
deny:
- "^[A-Z]{3}_.+$" # regular expression
模式描述是一种自己的语言,具有您必须学习的自己的语法和特性。如果您的要求发生变化,您必须维护其 "programs" 以验证您的 YAML。
如果您已经在使用 YAML 并且熟悉 Python,您可以使用 YAML 的标记工具在解析时检查对象。
假设你有一个文件 input.yaml
:
successive:
worker:
cds_process_number: !nonneg 0
spider_interval: !pos 10
run_worker_sh: !path /home/lmakeev/CDS/releases/master/scripts/run_worker.sh
allow:
- !regex "*"
deny:
- !regex "^[A-Z]{3}_.+$"
(您的示例文件删除了注释并插入了标签),您可以创建并注册四个 类 来使用以下程序检查值¹:
import sys
import os
import re
import ruamel.yaml
import pathlib
class NonNeg:
yaml_tag = u"!nonneg"
@classmethod
def from_yaml(cls, constructor, node):
val = int(node.value) # this creates/returns an int
assert val >= 0
return val
class Pos(int):
yaml_tag = u"!pos"
@classmethod
def from_yaml(cls, constructor, node):
val = cls(node.value) # this creates/return a Pos()
assert val > 0
return val
class Path:
yaml_tag = u"!path"
@classmethod
def from_yaml(cls, constructor, node):
val = pathlib.Path(node.value)
assert os.path.exists(val)
return val
class Regex:
yaml_tag = u"!regex"
def __init__(self, val, comp):
# store original string and compile() of that string
self._val = val
self._compiled = comp
@classmethod
def from_yaml(cls, constructor, node):
val = str(node.value)
try:
comp = re.compile(val)
except Exception as e:
comp = None
print("Incorrect regex", node.start_mark)
print(" ", node.tag, node.value)
return cls(val, comp)
yaml = ruamel.yaml.YAML(typ="safe")
yaml.register_class(NonNeg)
yaml.register_class(Pos)
yaml.register_class(Path)
yaml.register_class(Regex)
data = yaml.load(pathlib.Path('input.yaml'))
单个 from_yaml
类方法中的实际检查应根据您的需要进行调整(我不得不删除路径的断言,因为我没有该文件)。
如果你 运行 以上你会注意到它打印:
Incorrect regex in "input.yaml", line 7, column 9
!regex *
因为 "*"
不是有效的正则表达式。您的意思是:".*"
?
¹ 这是使用 ruamel.yaml 完成的,这是一个 YAML 1.2 解析器,我是它的作者。您可以使用 PyYAML 实现相同的结果,例如通过子类化 ObjectDict
(默认情况下这是不安全的,因此请确保在您的代码中更正它)
我们有一个在 YAML 中存储设置的项目(设置文件由 ansible 脚本生成)。现在我们使用 pyyaml 来解析 YAML 格式,使用 marshmallow 来验证设置。我很高兴在 YAML 中存储设置,但我不认为 marshmellow 是我需要的工具(模式很难阅读,我不需要设置序列化,想要 xsd 之类的东西)。那么在项目中验证设置的最佳实践是什么,也许有独立于语言的方式? (我们正在使用 python 2.7)
YAML 设置:
successive:
worker:
cds_process_number: 0 # positive integer or zero
spider_interval: 10 # positive integer
run_worker_sh: /home/lmakeev/CDS/releases/master/scripts/run_worker.sh # OS path
allow:
- "*" # regular expression
deny:
- "^[A-Z]{3}_.+$" # regular expression
模式描述是一种自己的语言,具有您必须学习的自己的语法和特性。如果您的要求发生变化,您必须维护其 "programs" 以验证您的 YAML。
如果您已经在使用 YAML 并且熟悉 Python,您可以使用 YAML 的标记工具在解析时检查对象。
假设你有一个文件 input.yaml
:
successive:
worker:
cds_process_number: !nonneg 0
spider_interval: !pos 10
run_worker_sh: !path /home/lmakeev/CDS/releases/master/scripts/run_worker.sh
allow:
- !regex "*"
deny:
- !regex "^[A-Z]{3}_.+$"
(您的示例文件删除了注释并插入了标签),您可以创建并注册四个 类 来使用以下程序检查值¹:
import sys
import os
import re
import ruamel.yaml
import pathlib
class NonNeg:
yaml_tag = u"!nonneg"
@classmethod
def from_yaml(cls, constructor, node):
val = int(node.value) # this creates/returns an int
assert val >= 0
return val
class Pos(int):
yaml_tag = u"!pos"
@classmethod
def from_yaml(cls, constructor, node):
val = cls(node.value) # this creates/return a Pos()
assert val > 0
return val
class Path:
yaml_tag = u"!path"
@classmethod
def from_yaml(cls, constructor, node):
val = pathlib.Path(node.value)
assert os.path.exists(val)
return val
class Regex:
yaml_tag = u"!regex"
def __init__(self, val, comp):
# store original string and compile() of that string
self._val = val
self._compiled = comp
@classmethod
def from_yaml(cls, constructor, node):
val = str(node.value)
try:
comp = re.compile(val)
except Exception as e:
comp = None
print("Incorrect regex", node.start_mark)
print(" ", node.tag, node.value)
return cls(val, comp)
yaml = ruamel.yaml.YAML(typ="safe")
yaml.register_class(NonNeg)
yaml.register_class(Pos)
yaml.register_class(Path)
yaml.register_class(Regex)
data = yaml.load(pathlib.Path('input.yaml'))
单个 from_yaml
类方法中的实际检查应根据您的需要进行调整(我不得不删除路径的断言,因为我没有该文件)。
如果你 运行 以上你会注意到它打印:
Incorrect regex in "input.yaml", line 7, column 9
!regex *
因为 "*"
不是有效的正则表达式。您的意思是:".*"
?
¹ 这是使用 ruamel.yaml 完成的,这是一个 YAML 1.2 解析器,我是它的作者。您可以使用 PyYAML 实现相同的结果,例如通过子类化 ObjectDict
(默认情况下这是不安全的,因此请确保在您的代码中更正它)