I have a .yaml
file I want to update with Python code. Let's say it looks something like that:
state: 'present'
I'd like to have a code that changes the state and saves the file. I'm trying with something like this and fail:
def set_state(state):
with open("file_to_edit.yaml", 'rw') as f:
doc = yaml.load(f)
doc['state'] = state
yaml.dump(f)
I am using the 'yaml' package for Python.
PyYAML is a YAML parser and emitter for Python. PyYAML features a complete YAML 1.1 parser, Unicode support, pickle support, capable extension API, and sensible error messages. PyYAML supports standard YAML tags and provides Python-specific tags that allow to represent an arbitrary Python object.
The problem is that yaml.dump(doc)
doesn't actually write to a file. Instead, it returns the modified YAML as a string unless you pass the file descriptor as argument as well, which allows you to write directly to the file.
The following should work:
def set_state(state):
with open('file_to_edit.yaml') as f:
doc = yaml.load(f)
doc['state'] = state
with open('file_to_edit.yaml', 'w') as f:
yaml.dump(doc, f)
Before anything else: never use yaml.load()
if you don't have to, as it is in principle unsafe to do so. For this kind of simple structure (without tags) you should use yaml.safe_load()
(and the corresponding safe_dump()
that will complain if your data cannot be safe_loaded after dumping).
yaml.dump()
has the following signature:
def dump(documents, stream=None, Dumper=Dumper,
default_style=None, default_flow_style=None,
canonical=None, indent=None, width=None,
allow_unicode=None, line_break=None,
encoding='utf-8', explicit_start=None, explicit_end=None,
version=None, tags=None)
Of this only the first one needs to be given, that should be your doc
variable. If you don't specify a stream, then dump()
writes data structure to an in memory file-object (like StringIO) and after writing returns the value as string.
So although you could do:
with open("file_to_edit.yaml", 'w') as f:
f.write(yaml.safe_dump(doc))
this is inefficient and shows little understanding of how yaml.safe_dump()
works.
If you want to open the file for reading and writing you have to make sure you both reset the index in the file and truncate its content. This is usually not worth the effort so it is safer to re-open the file for writing:
def set_state(state):
file_name = "file_to_edit.yaml"
with open(file_name) as f:
doc = yaml.safe_load(f)
doc['state'] = state
with open(file_name, 'w') as f:
yaml.safe_dump(doc, f, default_flow_style=False)
(of course you make the filename a variable when you want to make sure you overwrite the original, so you cannot mistype it).
If you don't specify default_flow_style=False
, your output will look like:
{state: deleted}
The output will not include the superfluous quotes around present
in your input. You can specify default_style="'"
as well, but this will also put quotes around state
.
If losing the quotes is a problem and you really want the output to look like the input, you should be using ruamel.yaml
(disclaimer I am the author of that package), which can preserve the quotes on individual strings, handles YAML 1.2 (instead of YAML 1.1) and also preserves comments in your file.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With