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
原因
平たく言うとバグ。若しくは仕様のようだ。
トップページに事例付きで解説が書いてある。
同様の問題を抱えている人は少なくなく
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'