我有一组YAML文件。我想缓存这些文件,以便尽可能多地重复使用工作。
每个文件都包含两个文档。第一个文档包含始终将以相同方式解释的“静态”信息。第二个文档包含“动态”信息,每次使用该文件时都必须重新解释。具体来说,它使用基于标签的宏系统,并且每次使用文件时都必须重新构建文档。但是,文件本身不会更改,因此可以解析整个文件的结果进行缓存(节省大量资源)。
在ruamel.yaml中,是否有一种简单的方法可以将整个文件解析为多个已解析的文档,然后分别在每个文档上运行构建?这将允许我缓存构造第一个“静态”文档的结果,并缓存第二个“动态”文档的解析,以供以后构造。
示例文件:
---
default_argument: name
...
%YAML 1.2
%TAG ! tag:yaml-macros:yamlmacros.lib.extend,yamlmacros.lib.arguments:
---
!merge
name: !argument name
第一个文档包含在第二个文档的构造中使用的元数据(以及其他位置的其他数据)。
答案 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和解析都将被缓存,并且每次只需要执行最后的构造步骤。