dump an python object as yaml file
This is an old post but to complement Jayanth Koushik's answer:
Not sure that having __repr__
not implemented is the culprit here. I tried implementing it and it still raises an error, so the solution is probably not correct:
import yaml
class CameraBrand():
def __init__(self, name, url):
self.rank = ""
self.name = name
self.url = url
self.models = []
def __str__(self):
return repr(self)
def __repr__(self):
return self.name + ": " + self.url
brand_object = CameraBrand('foo', 'http://foo.com')
print(yaml.safe_dump(brand_object))
still raises yaml.representer.RepresenterError: cannot represent an object: foo: http://foo.com
Actually the answer can be found in PyYaml documentation: "safe_dump
produces only standard YAML tags and cannot represent an arbitrary Python object.". Therefore you simply need to use dump
instead of safe_dump
:
>>> print(yaml.dump(brand_object))
!!python/object:__main__.CameraBrand
models: []
name: foo
rank: ''
url: http://foo.com
However, once you do that, you will see that loading your object back
- is ok with the unsafe and not recommended
yaml.load
, - but becomes a bit tricky with the highly recommended
safe_load
. For this you would have to dig a little bit intoYAMLObject
and other PyYaml classes, and that is a bit tricky.
Alternatively, you can use the yamlable library, which automates a few of these PyYaml tricks for you. I wrote it to solve similar cases in our production code, in order to make it easier to control the object-to-yaml binding process. In your case, the following would do the trick:
import yaml
from yamlable import YamlAble, yaml_info
@yaml_info(yaml_tag_ns='myyaml')
class CameraBrand(YamlAble):
def __init__(self, name, url):
self.rank = ""
self.name = name
self.url = url
self.models = []
def __str__(self):
return self.name + ": " + self.url
def to_yaml_dict(self):
return {'name': self.name, 'url': self.url}
# @classmethod
# def from_yaml_dict(cls, dct, yaml_tag):
# return CameraBrand(**dct)
brand_object = CameraBrand('foo', 'http://foo.com')
print(yaml.safe_dump(brand_object))
yields
!yamlable/myyaml.CameraBrand {name: foo, url: 'http://foo.com'}
and
print(yaml.safe_load("""!yamlable/myyaml.CameraBrand
name: foo
url: http://bar.com
"""))
loads the object correctly back and displays its string representation:
foo: http://bar.com
Note that you can also uncomment the from_yaml_dict
method if you wish to perform some custom action when loading the object. See yamlable documentation for details.
You need to implement __repr__ for the class. You could just use your __str__ as __repr__.
What if you still want to safe_dump
?
No dump
and bothering to write a representer for every object, nor another dependency?
Well, you could write a method which will catch-all, like the "default" argument to the JSON dumpers and overwrite the one raising an exception in the YAML SafeRepresenter
.
Proof of concept using PyAML 3.12.
import yaml
yaml.SafeDumper.yaml_representers[None] = lambda self, data: \
yaml.representer.SafeRepresenter.represent_str(
self,
str(data),
)
Example:
>>> import collections
>>> yaml.safe_dump([collections.namedtuple('Foo', 'bar')(1)])
'[Foo(bar=1)]\n'
>>> yaml.safe_dump(CameraBrand('Canon', '/cameras/canon/'))
"'Canon: /cameras/canon/'\n"
Note: for the namedtuple
specific topic, see : Serializing namedtuples via PyYAML