标准标签的 PyYAML 替代构造函数
PyYAML alternative constructors for standard tags
在尝试创建一个能够扩展变量的加载器时,我注意到当我使用块标量时 PyYAML 没有扩展变量(例如 >-
)。所以,举个例子:
# ademo.yml
key1: foo
key2: bar $LOL bar
key3: >-
baz $ASD baz
为了扩展变量,我做了以下操作:
# yaamer.py
import re
import sys
import yaml
# I create a loder
class MyLoader(yaml.SafeLoader): pass
# Add to it an implicit resolver that matches variables
matcher = re.compile(r".*$[A-Z].*")
MyLoader.add_implicit_resolver("!path", matcher, None)
# Add a constructor that would replaces the variable
def cons(loader, node):
print("Constructing for", node)
return "FOO"
MyLoader.add_constructor("!path", cons)
with open(sys.argv[1]) as inf:
print(yaml.load(inf, MyLoader))
当我调用它时,python yaamer.py ademo.yml
,我得到这个输出:
Constructing for ScalarNode(tag='!path', value='bar $LOL bar')
{'key1': 'foo', 'key2': 'FOO', 'key3': 'baz $ASD baz'}
所以,替换只是在key2,而不是key3。
为了了解发生了什么,我为正则表达式实现了一个包装器,让我了解发生了什么:
class Matcher:
def __init__(self):
self.matcher = re.compile(r".*$[A-Z].*")
def match(self, *args, **kwargs):
print("Matching", args, kwargs)
return self.matcher.match(*args, **kwargs)
并使用它会导致此输出:
Matching ('key1',) {}
Matching ('foo',) {}
Matching ('key2',) {}
Matching ('bar $LOL bar',) {}
Matching ('key3',) {}
Constructing for ScalarNode(tag='!path', value='bar $LOL bar')
{'key1': 'foo', 'key2': 'FOO', 'key3': 'baz $ASD baz'}
匹配中没有块标量的踪迹!因此,显然,这个匹配器从未用于解析 >-
块。
于是我调查了一下,发现有一些pre-defined tags,比如!!str
或者!!map
。我虽然如此,因为 >-
是一种特殊语法,它会自动匹配到其中一个标签。
所以我继续为所有标签安装自定义构造函数,以查看是否调用了其中的任何一个:
def make_cons(name):
def _cons(loader, node):
print("Constructor for", name)
return node.value
return _cons
MyLoader.add_constructor("!!str", make_cons('!!str'))
MyLoader.add_constructor("!!int", make_cons('!!int'))
但是他们从未被调用过!到底是怎么回事??如果我必须为预先存在的 YAML 标签安装自定义构造函数,我该怎么做?
隐式解析器只能匹配普通标量,而不是引用。带引号的意思是单引号或双引号,或者文字或折叠块标量。所以你的 key3
是一个折叠块标量,因此不考虑匹配。
您可以在 YAML 源代码中显式添加 !path
标签,然后您的自定义构造函数将被调用。
查看 PyYAML Documentation 并查找单词 'implicit' 和 'plain'。
要为标准标签添加构造函数,您需要使用全名。 !!str
只是 tag:yaml.org,2002:str
的 shorthand:
yaml.add_constructor('tag:yaml.org,2002:str', my_constructor)
在尝试创建一个能够扩展变量的加载器时,我注意到当我使用块标量时 PyYAML 没有扩展变量(例如 >-
)。所以,举个例子:
# ademo.yml
key1: foo
key2: bar $LOL bar
key3: >-
baz $ASD baz
为了扩展变量,我做了以下操作:
# yaamer.py
import re
import sys
import yaml
# I create a loder
class MyLoader(yaml.SafeLoader): pass
# Add to it an implicit resolver that matches variables
matcher = re.compile(r".*$[A-Z].*")
MyLoader.add_implicit_resolver("!path", matcher, None)
# Add a constructor that would replaces the variable
def cons(loader, node):
print("Constructing for", node)
return "FOO"
MyLoader.add_constructor("!path", cons)
with open(sys.argv[1]) as inf:
print(yaml.load(inf, MyLoader))
当我调用它时,python yaamer.py ademo.yml
,我得到这个输出:
Constructing for ScalarNode(tag='!path', value='bar $LOL bar')
{'key1': 'foo', 'key2': 'FOO', 'key3': 'baz $ASD baz'}
所以,替换只是在key2,而不是key3。
为了了解发生了什么,我为正则表达式实现了一个包装器,让我了解发生了什么:
class Matcher:
def __init__(self):
self.matcher = re.compile(r".*$[A-Z].*")
def match(self, *args, **kwargs):
print("Matching", args, kwargs)
return self.matcher.match(*args, **kwargs)
并使用它会导致此输出:
Matching ('key1',) {}
Matching ('foo',) {}
Matching ('key2',) {}
Matching ('bar $LOL bar',) {}
Matching ('key3',) {}
Constructing for ScalarNode(tag='!path', value='bar $LOL bar')
{'key1': 'foo', 'key2': 'FOO', 'key3': 'baz $ASD baz'}
匹配中没有块标量的踪迹!因此,显然,这个匹配器从未用于解析 >-
块。
于是我调查了一下,发现有一些pre-defined tags,比如!!str
或者!!map
。我虽然如此,因为 >-
是一种特殊语法,它会自动匹配到其中一个标签。
所以我继续为所有标签安装自定义构造函数,以查看是否调用了其中的任何一个:
def make_cons(name):
def _cons(loader, node):
print("Constructor for", name)
return node.value
return _cons
MyLoader.add_constructor("!!str", make_cons('!!str'))
MyLoader.add_constructor("!!int", make_cons('!!int'))
但是他们从未被调用过!到底是怎么回事??如果我必须为预先存在的 YAML 标签安装自定义构造函数,我该怎么做?
隐式解析器只能匹配普通标量,而不是引用。带引号的意思是单引号或双引号,或者文字或折叠块标量。所以你的 key3
是一个折叠块标量,因此不考虑匹配。
您可以在 YAML 源代码中显式添加 !path
标签,然后您的自定义构造函数将被调用。
查看 PyYAML Documentation 并查找单词 'implicit' 和 'plain'。
要为标准标签添加构造函数,您需要使用全名。 !!str
只是 tag:yaml.org,2002:str
的 shorthand:
yaml.add_constructor('tag:yaml.org,2002:str', my_constructor)