Search code examples
yamlruamel.yaml

ruamel.yaml CommentedMap is not compatible with dict in update() (has a bug?)


The code below reproduces the issue:

from collections import OrderedDict
from ruamel.yaml import YAML

ordered_dict = OrderedDict(a = 1, b = 2)
replacement = dict(a = 3, b = 4)
ordered_dict.update(**replacement)
print(ordered_dict)  # this is to show that OrderedDict is compatible with dict

commented_map = YAML().load('''\
a: 1
b: 2
''')

commented_map.update(**replacement)  # should work like dict's update()
print(commented_map)

Output

The first part

OrderedDict([('a', 3), ('b', 4)])

shows that OrderedDict is compatible with dict

However the commented_map part raises an exception

Traceback (most recent call last):
  File "ruamel_bug.py", line 14, in <module>
    commented_map.update(**replacement)
  File "D:\venv\Ruamel\lib\site-packages\ruamel\yaml\comments.py", line 881, in update
    self._ok.add(*kw.keys())
TypeError: set.add() takes exactly one argument (2 given)

My current work-around is to edit the source code to modify line 881 into

for key in kw.keys(): self._ok.add(key)

then everything became OK!!! Therefore I believe it is a bug in line 881.

I was going to create a ticket for the project, but the instruction asked me to post here first, to clarify that it is not a faulty usage of the library.

Is it a bug? Or my misusage?

Thank you in advance.

Environment: ruamel.yaml==0.17.21 with Python 3.11 on 64 bit Windows


Solution

  • Since the intention is that CommentedMap behaves like a dict, and this has been implemeted for dict's so long ago, this should probably be called a bug instead of a missing feature. I added a ticket on sourceforge for this.

    In ruamel.ordereddict, the implementation of the original C mapping that kept insertion order (on which CommentedMap for Python 2 relies heavily), the .update() doesn't take keywords either. That package was developed back in 2007, still three years after Python 2.4 was released, so that is IMO somewhat more understandable it is not in there. So maybe that is why this is not (fully) implemented in ruamel.yaml

    Until that has been fixed (done in 0.17.23), you can do update(replacement) (i.e. pass in the dict as parameter) instead of expanding them to key-value parameters with update(**replacement)