HyperPyYAML icon indicating copy to clipboard operation
HyperPyYAML copied to clipboard

Saving config files with !new tags

Open egaznep opened this issue 2 years ago • 2 comments

Hello,

I have the following code (a MWE - minimum working example), where I want to dump a config with a !new instatiation.

yaml_string = """
foo: !new:collections.Counter
  a: 4
bar: !ref <foo>
baz: !copy <foo>
"""
loaded_yaml = load_hyperpyyaml(yaml_string)
from hyperpyyaml import dump_hyperpyyaml
# Importing the StringIO module.
from io import StringIO

dump_hyperpyyaml(loaded_yaml, StringIO())

This fails with the following

RepresenterError                          Traceback (most recent call last)

[<ipython-input-6-c8804319bfb8>](https://localhost:8080/#) in <cell line: 14>()
     12 from io import StringIO
     13 
---> 14 dump_hyperpyyaml(loaded_yaml, StringIO())

9 frames

[/usr/local/lib/python3.10/dist-packages/ruamel/yaml/representer.py](https://localhost:8080/#) in represent_undefined(self, data)
    343 
    344     def represent_undefined(self, data: Any) -> None:
--> 345         raise RepresenterError(f'cannot represent an object: {data!s}')
    346 
    347 

RepresenterError: cannot represent an object: Counter({'a': 4})

Neither the docs, nor the given Colab notebook contains an example of dumping with the !new tag. How could this be done?

egaznep avatar Nov 26 '23 20:11 egaznep

tl;dr this is not possible with the current way this is structured.

Longer answer:

There's no general way to extract the arguments used to construct an object. On the other hand, if you wanted to use it for specific cases, this would have to be done with something like the add_representer in the current dump_hyperpyyaml where the to_yaml function tells the representer how to convert the class to a string:

def dump_hyperpyyaml(yaml_tree, output_stream, *args, **kwargs):
    ruamel_yaml = ruamel.yaml.YAML()
    ruamel_yaml.representer.add_representer(RefTag, RefTag.to_yaml)
    ruamel_yaml.representer.add_representer(Placeholder, Placeholder.to_yaml)
    ruamel_yaml.dump(yaml_tree, output_stream, *args, **kwargs)

I suppose one simple change that might allow a user to do this is to do something like:

def dump_hyperpyyaml(yaml_tree, output_stream, represented_classes=[RefTag, Placeholder], *args, **kwargs):
    ruamel_yaml = ruamel.yaml.YAML()
    for represented_class in represented_classes:
        ruamel_yaml.representer.add_representer(represented_class, represented_class.to_yaml)
    ruamel_yaml.dump(yaml_tree, output_stream, *args, **kwargs)

Is this something that would likely work for your use case?

pplantinga avatar Dec 13 '23 14:12 pplantinga

I understand. Ideally, as a user I would appreciate something more towards how hydra manages to do this - not requiring extra effort from the users. I did not study how that works though - could be requiring some changes to how hyperpyyaml functions. For the time being, I just want to dump the configuration for certain speech processing pipeline segments - so I can get away with making a base class dumpable (according to your first snippet) and everything else can inherit. Thank you for the reply :+1:

egaznep avatar Dec 14 '23 08:12 egaznep