簡體   English   中英

在 ruamel.yaml 中將別名讀取為字符串

[英]Reading aliases as strings in ruamel.yaml

我有一個 YAML 文件,其中包含帶通配符的字符串,例如:

hello : world
foo : *
bar : ruamel.*

這在傳遞給ruamel.yaml.load時失敗,因為*星號,如果是關鍵字(字符串)的第一個字符,則表示別名的開始。 如果在此示例中bar的值介於兩者之間,則它有效。

由於如果所有* -led 字符串都必須用引號保護並且我不需要在我的文件中提供錨點/別名支持,那么寫入和讀取並不是那么好,我想我會以某種方式在加載器中禁用它. 我沒有直接在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)

這是有效的,並且該值按預期解釋為字符串,但前提是* foo : **有另一個字符,例如foo : ** 如果只是星號,則會出現錯誤提示

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

我沒有通過代碼找到一個簡單的解決方案,不得不放棄。

那么我怎樣才能實現我想要的呢? 或者我錯過了Loader某個地方的選項?

使用ruamel.yaml解析 YAML 所涉及的步驟按照將結果從一個應用到另一個的順序:

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

當您將文檔傳遞給YAML().load() ,您會收到ScannerError ,因此在構建階段嘗試“修復”該問題ScannerError已晚。


在令牌開始對“*”實際檢查的方法進行fetch_more_tokensscanner.py ,你當然可以改變該方法(可以通過繼承或猴子修補),但它是在一個近百線,最其中你必須逐字復制。

相關部分是:

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

.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])))

這使:

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

我終於也想出了如何直接使用 PyYAML 來實現這一點。 除了猴子修補fetch_aliasfetch_plain ,還需要從yaml_implicit_resolvers dict 中刪除*鍵。 這就是導致提到的ConstructorError

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

因此:

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