简体   繁体   English

在 ruamel.yaml 中将别名读取为字符串

[英]Reading aliases as strings in ruamel.yaml

I have a YAML file that contains strings with wildcards, for example:我有一个 YAML 文件,其中包含带通配符的字符串,例如:

hello : world
foo : *
bar : ruamel.*

This fails when passed on to ruamel.yaml.load because the * asterisk, if the first character of a keyword (string), indicates the beginning of an alias.这在传递给ruamel.yaml.load时失败,因为*星号,如果是关键字(字符串)的第一个字符,则表示别名的开始。 If it's somewhere in between as for the value of bar in this example, it works.如果在此示例中bar的值介于两者之间,则它有效。

Since it's not so nice to write and read if all the * -led strings have to be protected with quotation marks and I don't need the anchor/alias support in my file anyway, I thought I'd disable it in the Loader somehow.由于如果所有* -led 字符串都必须用引号保护并且我不需要在我的文件中提供锚点/别名支持,那么写入和读取并不是那么好,我想我会以某种方式在加载器中禁用它. I didn't find an option in the ruamel.yaml.Loader directly, so I looked around the code a bit and came up with the following:我没有直接在ruamel.yaml.Loader找到选项,所以我ruamel.yaml.Loader环顾了一下代码,得出了以下内容:

from ruamel import yaml

class NoAliasLoader(yaml.Loader):
    def fetch_alias(self):                        
        return self.fetch_plain()

yaml.load(yml_doc, Loader=NoAliasLoader)

This works and the value is interpreted as a string as intended, but only if another character follows the * , like in foo : ** .这是有效的,并且该值按预期解释为字符串,但前提是* foo : **有另一个字符,例如foo : ** If it's only the asterisk, there's an error saying如果只是星号,则会出现错误提示

ConstructorError: could not determine a constructor for the tag 'tag:yaml.org,2002:yaml'
  in "<unicode string>", line 3, column 7:
    foo : *
          ^ (line: 3)

I didn't find an easy solution to that just from going through the code and had to give up.我没有通过代码找到一个简单的解决方案,不得不放弃。

So how can I achieve what I want?那么我怎样才能实现我想要的呢? Or is there an option in the Loader somewhere that I missed?或者我错过了Loader某个地方的选项?

The steps involved in parsing YAML using ruamel.yaml are in the order of applying the result from the one to the other:使用ruamel.yaml解析 YAML 所涉及的步骤按照将结果从一个应用到另一个的顺序:

YAML document → scanning → parsing → composing → constructing → Python data structure

When you pass your document to YAML().load() , you get a ScannerError , so trying to "fix" that in the construction phase is way to late.当您将文档传递给YAML().load() ,您会收到ScannerError ,因此在构建阶段尝试“修复”该问题ScannerError已晚。


The actual check on '*' at the start of a token is done in the method fetch_more_tokens in scanner.py and you could of course change that method (either by subclassing or monkey-patching), but it is over one hunderd lines, most of which you would have to copy verbatim.在令牌开始对“*”实际检查的方法进行fetch_more_tokensscanner.py ,你当然可以改变该方法(可以通过继承或猴子修补),但它是在一个近百线,最其中你必须逐字复制。

The relevant part is:相关部分是:

    # Is it an alias?
    if ch == '*':
        return self.fetch_alias()

And it is much simpler to just replace .fetch_alias() with the routine to fetch a "normal" plain scalar ( .fetch_plain() ):.fetch_alias()替换为获取“正常”普通标量( .fetch_plain() )的例程要简单得多:

import sys
import ruamel.yaml

yaml_str = """\
hello : world
foo : *
bar : ruamel.*
"""

ruamel.yaml.scanner.Scanner.fetch_alias = ruamel.yaml.scanner.Scanner.fetch_plain
ruamel.yaml.resolver.implicit_resolvers = ruamel.yaml.resolver.implicit_resolvers[:-1]

yaml = ruamel.yaml.YAML(typ='safe', pure=True)

data = yaml.load(yaml_str)
for k in data:
    print('{:6s} -> {:10s} [{}]'.format(k, data[k], type(data[k])))

which gives:这使:

hello  -> world      [<class 'str'>]
foo    -> *          [<class 'str'>]
bar    -> ruamel.*   [<class 'str'>]

I finallly also figured out how to achieve that with PyYAML directly.我终于也想出了如何直接使用 PyYAML 来实现这一点。 Additionally to monkey patching fetch_alias into fetch_plain , it's required to remove the * key from the yaml_implicit_resolvers dict.除了猴子修补fetch_aliasfetch_plain ,还需要从yaml_implicit_resolvers dict 中删除*键。 That's what caused the mentioned ConstructorError .这就是导致提到的ConstructorError

import yaml
yaml.Loader.fetch_alias = yaml.Loader.fetch_plain
yaml.Loader.yaml_implicit_resolvers.pop("*", None)

As a result:因此:

yaml.load("""
hello : world
foo : *
bar : 10
""")
>>> {'bar': 10, 'foo': '*', 'hello': 'world'}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM