My objective is to insert a key value pair in a YAML file which might be empty.
For example, my hiera.yaml
(used in puppet) file contains only three hyphens.
Here is my code:
#!/usr/bin/python
import ruamel.yaml
import sys
def read_file(f):
with open(f, 'r') as yaml:
return ruamel.yaml.round_trip_load(yaml)
dict = {}
dict['first_name'] = sys.argv[1]
dict['last_name'] = sys.argv[2]
dict['role'] = sys.argv[3]
data = read_file('hiera.yaml')
pos = len(data)
data.insert(pos, sys.argv[1], dict, None)
ruamel.yaml.round_trip_dump(data, open('hiera.yaml', 'w'), block_seq_indent=1)
I am running it like:
./alice.py Alice Doe Developer
I get an output like:
Traceback (most recent call last):
File "./alice.py", line 16, in <module>
pos = len(data)
TypeError: object of type 'NoneType' has no len()
But when my hiera.yaml file is not empty, for example:
$ cat hiera.yaml
john:
$./alice.py Alice Doe Developer
$ cat hiera.yaml
john:
alice:
first_name: Alice
last_name: Doe
role: Developer
Then it works properly.
Please tell me how to insert a key value pair(in my case a dict) to an empty YAML file. The examples of ruamel.yaml official page use doc string as a sample YAML content and then insert key-value pairs.
An empty scalar in a YAML document gives you the null
YAML object that loads into Python as None
:
a: 1
b:
The value for key b
in data loaded from this will be None
.
An empty file, or an empty string, from which your data is loaded using ruamel.yaml is considered the same as a file/string that contains the scalar null
:
null
If you load that, you get None
back, and you cannot add new keys to that.
To be sure check if the data you loaded is a dict
or a subclass of a dict (in case you load with round_trip_load, you will get a ruamel.yaml.comment.CommentedMap), or check if it is None:
data = ruamel.yaml.round_trip_load(open('myfile.yaml'))
if data is None:
data = ruamel.yaml.comments.CommentedMap()
data.insert(len(data), key, value)
You have to use a CommentedMap()
as a normal Python dict doesn't have a method .insert()
Beware thought that a toplevel item in YAML file might also be a scalar (string, integer, datetime, etc) or a sequence (which gets loaded as a list). The former probably cannot be .inserted()
upon, and the later (list) takes only a single parameter for .insert()