如何使用 ruamel.yaml read/write markdown yaml frontmatter?

How do I read/write markdown yaml frontmatter with ruamel.yaml?

我想使用Python在markdown文件中读写YAML frontmatter。我遇到了 ruamel.yaml 包,但我无法理解如何将其用于此目的。

如果我有降价文件:

---
car: 
  make: Toyota
  model: Camry
---

# My Ultimate Car Review
This is a good car.

首先,有没有办法在我的 python 代码中将 yaml 数据设置为变量?

其次,有没有办法给markdown文件中的yaml设置新的值?

第一个,我试过:

from ruamel.yaml import YAML
import sys

f = open("cars.txt", "r+") # I'm really not sure if r+ is ideal here.

yaml = YAML()
code = yaml.load(f)
print(code['car']['make'])

但出现错误:

ruamel.yaml.composer.ComposerError: expected a single document in the stream
  in "cars.txt", line 2, column 1
but found another document
  in "cars.txt", line 5, column 1

第二个,我试过:

from ruamel.yaml import YAML
import sys

f = open("cars.txt", "r+") # I'm really not sure if r+ is ideal here.

yaml = YAML()
code = yaml.load(f)
code['car']['model'] = 'Sequoia'

却得到同样的错误error:

ruamel.yaml.composer.ComposerError: expected a single document in the stream
  in "cars.txt", line 2, column 1
but found another document
  in "cars.txt", line 5, column 1

当你在一个文件中有多个 YAML 文档时,这些文件用一行分隔 三个破折号,或以三个破折号开头,后跟 space。 大多数 YAML 解析器,包括 ruamel.yaml 要么期望单个文档文件(使用 YAML().load() 时) 或多文档文件(使用 YAML().load_all() 时)。

方法.load() returns单一的数据结构,如果好像不止一个就报错 文档(即当它遇到文件中的第二个 --- 时)。这 .load_all() 方法可以处理一个或多个 YAML 文档,但总是 returns 一个迭代器。

您的输入恰好是一个有效的多文档 YAML 文件,但降价部分通常使情况并非如此。它很容易可以 只需将第二个 --- 更改为 --- | 从而使 YAML 始终有效 降价部分(多行)文字标量字符串。我不知道为什么 这种 YAML frontmatter 格式的设计者没有具体说明,它可能必须 做一些解析器(如 PyYAML)无法解析这种非缩进文字标量 根级别的字符串正确,尽管这些示例在 YAML 中 规范.

在你的例子中,降价部分非常简单,它是有效的 YAML,没有 必须为文字标量字符串指定 |。所以你可以使用 .load_all() 在此输入上。但只是添加例如一条线 以破折号开头的降价部分,将导致无效的 YAML 文件,所以如果你使用 .load_all(),你必须确保你 不要迭代到解析第二个文档:

import sys
from pathlib import Path
import ruamel.yaml

path = Path('cars.txt')

yaml = ruamel.yaml.YAML()
for data in yaml.load_all(path):
    break
print(data['car']['make'])

给出:

Toyota

但是你不应该尝试更新文件(所以不要使用 r+),因为你的 YAML frontmatter 可能是 比原来的和更新的时间长,更新会覆盖你的降价。为了 更新,将文件读入内存,根据第二行分成两部分 破折号,更新数据,转储它并附加破折号和降价:

import sys
from pathlib import Path
import ruamel.yaml

path = Path('cars.txt')
opath = Path('cars_out.txt')
yaml_str, markdown = path.read_text().lstrip().split('\n---', 1)
yaml_str += '\n' # re-add the trailing newline that was split off

yaml = ruamel.yaml.YAML()
yaml.explicit_start = True
data = yaml.load(yaml_str)

data['car']['year'] = 2003

with opath.open('w') as fp:
    yaml.dump(data, fp)
    fp.write('---')
    fp.write(markdown)

sys.stdout.write(opath.read_text())

给出:

---
car:
  make: Toyota
  model: Camry
  year: 2003
---

# My Ultimate Car Review
This is a good car.