lebrice / SimpleParsing

Simple, Elegant, Typed Argument Parsing with argparse
MIT License
410 stars 52 forks source link

Serialization of Union[float, bool] fields #264

Open halirutan opened 1 year ago

halirutan commented 1 year ago

I'm "enhancing" existing code and have to work with some of the more questionable choices when it comes to types. In this particular case, a field can be either bool or float. When using serialization, it revealed a deficiency that I try to understand.

To Reproduce

from dataclasses import dataclass
from typing import Union
from simple_parsing import Serializable

@dataclass
class MyOptions(Serializable):
    value: Union[float, bool] = False

opt1 = MyOptions()
opt1.save("/tmp/opt1.json")
opt1_loaded = MyOptions.load("/tmp/opt1.json")
print(opt1_loaded)

When executing this, the value is deserialized as 0.0 and I also get a warning that this might happen. What I don't understand is that a custom decoding function is absolutely straightforward and does the trick. Why is the default behavior not able to handle such cases?

Here is what I used as decoding function

from simple_parsing.helpers.serialization import register_decoding_fn

def decode_boolean_float(raw_value):
    if isinstance(raw_value, float):
        return raw_value
    elif isinstance(raw_value, bool):
        return raw_value
    else:
        raise RuntimeError(f"Could not decode JSON value {raw_value}")

register_decoding_fn(Union[float, bool], decode_boolean_float)

Desktop macOS:

lebrice commented 1 year ago

Hi @halirutan , thanks for posting!

This is a nice bug. Indeed, I'd expect this to work as expected out-of-the-box. Good catch!