$begingroup$
I have written a simple yaml based config parser in Python. The idea is that it will get passed around in other scripts after being initialized with a config file within a main function.
I am interested if this code is OK with regards to the following
Mar 2, 2018 - This is a valid YAML file that represents a list of strings. Which means that the result of this parsing is a Python list containing the appropriate. The official recommended filename extension for YAML files has been.yaml. There are two modules in Python for YAML: PyYAML and ruamel.yaml. In this tutorial, we use the former. PyYAML is a YAML parser and emitter for Python. $ pip install pyyaml The module is installed with pip. In this tutorial, we use the following YAML files.
- general best coding practices
- interface design
- test design
- should check for more corner cases, e.g. in the section below
# allowed values
, should I check first ifself.config[key]
exists? Or are the python exceptions that will be raised in such a case sufficient?
The config parser:
The test code:
200_success134k2121 gold badges169169 silver badges440440 bronze badges
IsaacIsaac
$endgroup$Browse other questions tagged pythonyamlconfiguration or ask your own question.
I am trying to output and then to parse back from YAML the following
The YAML file looks like
To write, I have added to the class two methods
and to read, the method
and following YAML Documentation, I added the following snippet in the same Python file
myClass.py
:Now, the writing seems to work ok, but reading the YAML, the code
seems to return the dictionary with empty data:
How should I fix the reader to be able to do this properly? Or perhaps I am not writing something out right? I spent a long time looking at PyYAML documentation and debugging through how the package is implemented but cannot figure out a way to parse out a complicated structure, and the only example I seemed to find has a 1-line class which parses out easily.
Related: YAML parsing and Python
UPDATE
Manually parsing the node as follows worked:
But the question still stands, is there a non-manual way to do this?
Anthon34.8k1717 gold badges101101 silver badges159159 bronze badges
gt6989bgt6989b1,70977 gold badges2626 silver badges4949 bronze badges
2 Answers
Instead of
try this:
Also, you have a little mistake in your YAML file:
The colon at the end does not belong there.
tinitatinita2,41311 gold badge1313 silver badges1616 bronze badges
There are multiple problems with your approach, even not taking into account that you should read PEP 8, the style guide for Python code, in particular the part on Method Names and Instance Variables
- As you indicate you have looked long at the Python documentation, you cannot have failed to notice that
yaml.load()
is unsafe. It is also is almost never necessary to use it, certainly not if you write your own representers and constructors. - You use
dumper.represent_dict({data.YAMLTag : data.toDict()})
which dumps an object as a key-value pair. What you want to do, at least if you want to have a tag in your output YAML is:dumper.represent_mapping(data.YAMLTag, data.toDict())
. This will get you output of the form:i.e. a tagged mapping instead of your key-value pair, where the value is a mapping. (And I would have expected the first line to be'!MyClass':
to make sure the scalar that starts with an exclamation mark is not interpreted as a tag). - Constructing a complex object, that are potentially self-referential (directly or indirectly) has to be done in two steps using a generator (the PyYAML code calls this in the correct way for you). In your code you assume that you have all the parameters to create an instance of
MyClass
. But if there is self-reference, these parameters have to include that instance itself and it is not created yet. The proper example code in the YAML code base for this isconstruct_yaml_object()
inconstructor.py
:You don't have to use.__new__()
, but you should takedeep=True
into account as explained here
In general it also is useful to have a
__repr__()
that allows you to check the object that you load, with something more expressive than <__main__.MyClass object at 0x12345>
The imports:
To check the correct workings of self-referential versions I added the
self._ref
attribute to the class:The representer and constructor 'methods':
And how to use it:
the above combined gives:
As you can see the
id
s of data
and data._ref
are the same after loading. The above throws an error if you use the simplistic approach in your constructor, by just using
AnthonAnthonloader.construct_mapping(node, deep=True)
34.8k1717 gold badges101101 silver badges159159 bronze badges