1

For Python 3.7, I am getting following error:

 `ruamel.yaml.representer.RepresenterError: cannot represent an object: {'a': 3}`

For the following piece of code taken from answer for PyYAML - Saving data to .yaml files. How can I fix this error?

from ruamel.yaml import YAML

class Config(dict):
    def __init__(self, filename, auto_dump=True):
        self.filename = filename
        self.auto_dump = auto_dump
        self.changed = False
        self.yaml = YAML()
        self.yaml.preserve_quotes = True
        # uncomment and adapt to your specific indentation
        # self.yaml.indent(mapping=4, sequence=4, offset=2)
        if os.path.isfile(filename):
            with open(filename) as f:
                # use super here to avoid unnecessary write
                super(Config, self).update(self.yaml.load(f) or {})

    def dump(self, force=False):
        if not self.changed and not force:
            return
        with open(self.filename, "w") as f:
            self.yaml.dump(self, f)
        self.changed = False

    # following methods unchanged from PyYAML example
    def updated(self):
        if self.auto_dump:
            self.dump(force=True)
        else:
            self.changed = True

    def __setitem__(self, key, value):
        super(Config, self).__setitem__(key, value)
        self.updated()

    def __delitem__(self, key):
        super(Config, self).__delitem__(key)
        self.updated()

    def update(self, kwargs):
        super(Config, self).update(kwargs)
        self.updated()


cfg = Config("test.yaml")
print(cfg)
cfg['a'] = 3  # <=Error occurs
print(cfg)
cfg.update({"b": 4})
cfg.update(c=5)
del cfg['a']
print(cfg)

Related:

alper
  • 2,919
  • 9
  • 53
  • 102

1 Answers1

1

It looks like the code in "PyYAML - Saving data to .yaml files" was not tested, normally there would be some output (which is automatically included in the answers I post).

The code almost works, but needs a cast of self to dict to get the key-value pairs, otherwise ruamel.yaml tries to dump the attributes (.filename, etc).

If you want both (dict-like) arguments as well as key-value arguments for .update() you'll need to extend it argument list:

import os
from ruamel.yaml import YAML

class Config(dict):
    def __init__(self, filename, auto_dump=True):
        self.filename = filename
        self.auto_dump = auto_dump
        self.changed = False
        self.yaml = YAML()
        self.yaml.preserve_quotes = True
        # uncomment and adapt to your specific indentation
        # self.yaml.indent(mapping=4, sequence=4, offset=2)
        if os.path.isfile(filename):
            with open(filename) as f:
                # use super here to avoid unnecessary write
                super(Config, self).update(self.yaml.load(f) or {})

    def dump(self, force=False):
        if not self.changed and not force:
            return
        with open(self.filename, "w") as f:
            self.yaml.dump(dict(self), f)
        self.changed = False

    # following methods unchanged from PyYAML example
    def updated(self):
        if self.auto_dump:
            self.dump(force=True)
        else:
            self.changed = True

    def __setitem__(self, key, value):
        super(Config, self).__setitem__(key, value)
        self.updated()

    def __delitem__(self, key):
        super(Config, self).__delitem__(key)
        self.updated()

    def update(self, *args, **kw):
        for arg in args:
            super(Config, self).update(arg)
        super(Config, self).update(**kw)
        self.updated()


cfg = Config("test.yaml")
print(cfg)
cfg['a'] = 3  # <=Error occurs
print(cfg)
cfg.update({"b": 4})
cfg.update(c=5)
del cfg['a']
print(cfg)

print('------')
print(open(cfg.filename).read())

which gives:

{}
{'a': 3}
{'b': 4, 'c': 5}
------
b: 4
c: 5

This dumps the config as if it were a dict and there is no tag in the YAML to indicate what has been dumped and nothing would help identifying during reading back of the configuration. So it would IMO be better to make this a dumpable class in the normal way and dump with a tag like !Config.

Anthon
  • 69,918
  • 32
  • 186
  • 246
  • Thank you. Would it be possible to apply your solution (auto dumping) for changes in nested dict ex: `my_dict[in_key][out_key] = 100`, is locally changed but is not saved into data? Please see: https://stackoverflow.com/questions/68691490/how-to-auto-dump-modified-values-in-nested-dictionaries-using-ruamel-yaml – alper Aug 07 '21 at 10:46
  • Instead of `self.yaml.dump`, can we use `self.yaml.safe_dump`? – alper Aug 07 '21 at 11:09
  • No we cannot use `self.yaml.safe_dump` as `self.yaml` is an instance of `YAML` and that has no `safe_dump` method (or attribute). Did you read the docs? Did you find the `typ` argument to `YAML`? ( `self.yaml = YAML(typ='safe')` will get you the C based safe loader) – Anthon Aug 07 '21 at 17:20
  • Do you advice to use `pure=True` as well along with `typ='safe'`? – alper Aug 07 '21 at 19:15
  • Not necessarily, there are differences between the pure Python and the C implementation, but I don't think you'll notice for a config file. – Anthon Aug 08 '21 at 05:36