I have an YAML config that looks like:
config:
- id: foo
- name: bar
content:
- run: xxx
- remove: yyy
I am using Python YAML module to load it but I want to access it in better ways like:
stream = open(filename)
config = load(stream, Loader=Loader)
print(config['content'])
What I want is to be able to do: print(config.content)
.
We can read the YAML file using the PyYAML module's yaml. load() function. This function parse and converts a YAML object to a Python dictionary ( dict object). This process is known as Deserializing YAML into a Python.
YAML is a data serialization format designed for human readability and interaction with scripting languages. PyYAML is a YAML parser and emitter for the Python programming language.
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.
You can use the pyyaml package to read and write YAML using Python. You can read the file in with the pyyaml shorthand functions load or safe_load . The safe_load function prevents the running arbitrary of Python code as the YAML is loaded, so it is usually preferred.
You can use object notation with dictionaries using the following class, as discussed in this answer:
class DictAsMember(dict):
def __getattr__(self, name):
value = self[name]
if isinstance(value, dict):
value = DictAsMember(value)
return value
This class in action:
>>> my_dict = DictAsMember(one=1, two=2)
>>> my_dict
{'two': 2, 'one': 1}
>>> my_dict.two
2
Edit This works recursively with sub-dictionaries, for example:
>>> my_dict = DictAsMember(one=1, two=2, subdict=dict(three=3, four=4))
>>> my_dict.one
1
>>> my_dict.subdict
{'four': 4, 'three': 3}
>>> my_dict.subdict.four
4
The easiest way to do this is probably to overwrite the YAML constructor for tag:yaml.org,2002:map
so it returns a custom dictionary class instead of an ordinary dictionary.
import yaml
class AttrDict(object):
def __init__(self, attr):
self._attr = attr
def __getattr__(self, attr):
try:
return self._attr[attr]
except KeyError:
raise AttributeError
def construct_map(self, node):
# WARNING: This is copy/pasted without understanding!
d = {}
yield AttrDict(d)
d.update(self.construct_mapping(node))
# WARNING: We are monkey patching PyYAML, and this will affect other clients!
yaml.add_constructor('tag:yaml.org,2002:map', construct_map)
YAML = """
config:
- id: foo
- name: bar
content:
- run: xxx
- remove: yyy
"""
obj = yaml.load(YAML)
print(obj.config[0].id) # prints foo
Note that this will break everything else in the process that uses YAML, if it expects everything to work the normal Python way. You can use a custom loader, but I personally find the PyYAML documentation a bit labyrinthine, and it seems that side effects are global and contagious as a rule rather than an exception.
You have been warned.
As an alternative, if your schema is relatively static you could write your own classes and deserialize to those (e.g., class Config
with id
and name
properties). It probably wouldn't be worth the cost of the extra code, however.
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