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 into YAMLObject 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

Tags:

Python

Yaml