缓存已解析的文档

时间:2019-06-05 15:29:36

标签: python-3.x ruamel.yaml

我有一组YAML文件。我想缓存这些文件,以便尽可能多地重复使用工作。

每个文件都包含两个文档。第一个文档包含始终将以相同方式解释的“静态”信息。第二个文档包含“动态”信息,每次使用该文件时都必须重新解释。具体来说,它使用基于标签的宏系统,并且每次使用文件时都必须重新构建文档。但是,文件本身不会更改,因此可以解析整个文件的结果进行缓存(节省大量资源)。

在ruamel.yaml中,是否有一种简单的方法可以将整个文件解析为多个已解析的文档,然后分别在每个文档上运行构建?这将允许我缓存构造第一个“静态”文档的结果,并缓存第二个“动态”文档的解析,以供以后构造。

示例文件:

---
default_argument: name
...
%YAML 1.2
%TAG ! tag:yaml-macros:yamlmacros.lib.extend,yamlmacros.lib.arguments:
---
!merge
name: !argument name

第一个文档包含在第二个文档的构造中使用的元数据(以及其他位置的其他数据)。

2 个答案:

答案 0 :(得分:1)

如果您不想完全处理流中的所有YAML文档,则必须手动拆分流,以一般方式进行操作并不容易。

您需要了解的是what a YAML stream can consist of

  

零个或多个文档。后续文档需要某种分隔标记行。如果文档不是由文档结束标记行终止,则以下文档必须以指令结束标记行开头。

文档结束标记行是以...开头的行,后跟空格/换行符,指令结束标记行是---,后跟空格/换行符。

实际的生产规则稍微复杂些,“开始于”应忽略以下事实:您需要跳过任何中间流字节顺序标记。

如果您没有任何指令,字节顺序标记和文档结尾标记(而且我所见过的大多数多文档YAML流都没有这些),那么您可以{{1 }}多文档YAML作为字符串,使用data = Path().read()进行拆分,并使用l = data.split('\n---')仅处理结果列表中的适当元素。


我不确定以下内容是否可以正确处理所有情况,但确实可以处理多文档流:

YAML().load(l[N])

给出:

import sys
from pathlib import Path
import ruamel.yaml


docs = []
current = ""

state = "EOD"
for line in Path("example.yaml").open():
    if state in ["EOD", "DIR"]:
        if line.startswith("%"):
            state = "DIR"
        else:
            state = "BODY"
        current += line
        continue
    if line.startswith('...') and line[3].isspace():
        state = "EOD"
        docs.append(current)
        current = ""
        continue
    if state == "BODY" and current and line.startswith('---') and line[3].isspace():
        docs.append(current)
        current = ""
        continue
    current += line
if current:
   docs.append(current)

yaml = ruamel.yaml.YAML()
data = yaml.load(docs[1])
print(data['name'])

答案 1 :(得分:0)

看起来您确实可以直接操作ruamel.yaml的解析器内部,只是没有记录。以下函数会将YAML字符串解析为文档节点:

from ruamel.yaml import SafeLoader

def parse_documents(text):
    loader = SafeLoader(text)
    composer = loader.composer
    while composer.check_node():
        yield composer.get_node()

从此处可以单独构造文档。为了解决我的问题,应执行以下操作:

def process_yaml(text):
    my_constructor = get_my_custom_constructor()

    parsed_documents = list(parse_documents(path.read_text()))
    metadata = my_constructor.construct_document(parsed_documents[0])
    return (metadata, document[1])

cache = {}
def do_the_thing(file_path):
    if file_path not in cache:
        cache[file_path] = process_yaml(Path(file_path).read_text())

    metadata, document = cache[file_path]

    my_constructor = get_my_custom_constructor(metadata)
    return my_constructor.construct_document(document)

这样,所有文件IO和解析都将被缓存,并且每次只需要执行最后的构造步骤。