Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

dump an python object as yaml file

Tags:

python

yaml

I am having problems dumping the Python object shown below to a yaml file. I was wondering whether there is any specific object type/format.

class CameraBrand():

  def __init__(self, name, url):
    self.rank = ""
    self.name = name
    self.url = url
    self.models = []

  def __str__(self):
      return self.name + ": " + self.url

This object contains a list of objects, and I tried to use yaml.safe_dump

  yaml.safe_dump(brandObject, file)

But I got this error:

yaml.representer.RepresenterError: cannot represent an object: Canon: /cameras/canon/

Is there a problem with this object?

like image 513
mr ng Avatar asked Feb 11 '14 07:02

mr ng


1 Answers

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.

like image 186
smarie Avatar answered Sep 21 '22 04:09

smarie