Edited at

Using yaml.dumper without Alias by PyYAML - PyYAML の yaml.dumpでaliasを無効にする


Overview

PyYAMLを使ってyaml dumpをしていると

&id001*id001 と言った出力を見かけたことがないだろうか。

非常に簡単な例を挙げると以下のようなものだ。


サンプルコード

import yaml

sample_dict = {
"username": "User",
"password": "Password",
}
yaml_data = [sample_dict, sample_dict]

print(yaml.dump(yaml_data))


出力結果

$ python sample.py 

- &id001 {password: Password, username: User}
- *id001


期待した動作

$ python sample.py 

- {password: Password, username: User}
- {password: Password, username: User}


サンプルコード2

YAMLファイルを生成するならば

より読みやすい出力にすることが多いと思われるので

default_flow_style = False としたサンプルも貼っておく

import yaml

sample_dict = {
"username": "User",
"password": "Password",
}
yaml_data = [sample_dict, sample_dict]

print(yaml.dump(yaml_data, default_flow_style=False,))


出力結果2

$ python sample.py 

- &id001
password: Password
username: User
- *id001


原因

平たく言うとバグ。若しくは仕様のようだ。

トップページに事例付きで解説が書いてある。

https://pypi.org/project/pyaml/

同様の問題を抱えている人は少なくなく

Qiitaでもこちらで対処法が記載されていたり、

Issue103、及びIssue104でチケットが上がっている。

残念ながら2018年11月現在においてもこの問題は解消されておらず

WorkArround対応となっている。

Issue104に記載があるが

yaml.dump(data, ignore_aliases=True) を実装すると、逆にAliasの有るYAMLを読み込んだ際に

無限ループに陥ってしまうらしく、双方をええ具合によろしくやってくれる方法はまだ無いようだ。

従ってWork Arroundで対処する必要がある。


対処方法

- &id001

password: Password
username: User
- *id001

import yaml

from pathlib import Path

"""Load yaml file"""
yaml_data = Path().parent.joinpath('sample.yml').open('r')
data = yaml.load(yaml_data)

"""Solution1"""
class NoAliasDumper(yaml.Dumper):
def ignore_aliases(self, data):
return True

out1 = yaml.dump(data, Dumper=NoAliasDumper)
print(out1)

"""Solution2"""
noalias_dumper = yaml.dumper.Dumper
noalias_dumper.ignore_aliases = lambda self, data: True
out2 = yaml.dump(data, Dumper=noalias_dumper)
print(out2)

python sample_read.py 

- {password: Password, username: User}
- {password: Password, username: User}

- {password: Password, username: User}
- {password: Password, username: User}

どちらで対処するかはお好みで。

なお、Dumper を SafeDumperにすることはできるが

yaml.dump を yaml.safe_dumpにはできない。

yaml.safe_dumpにすると以下のようなエラーが出てしまう

TypeError: dump_all() got multiple values for keyword argument 'Dumper'


参考リンク