lebrice / SimpleParsing

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

Unintuitive inconsistent behavior for `Optional` between builtin classes and `dataclass`es #287

Open anivegesana opened 1 year ago

anivegesana commented 1 year ago

Describe the bug Builtin classes and enums are able to be set as default values in optional fields. dataclasses are not and the default value is replaced with a None.

To Reproduce

from simple_parsing import parse, field
from simple_parsing.helpers.serialization.serializable import to_dict
from enum import Enum
from dataclasses import dataclass
from typing import Optional

class E(Enum):
    E1 = 1
    E2 = 2

@dataclass
class A:
    a: int = 0

@dataclass
class Test:
    b: Optional[bool] = True
    i: Optional[int] = 1
    s: Optional[str] = "string"
    e: Optional[E] = E.E1
    a: Optional[A] = field(default_factory=A)

test = parse(Test)
print(to_dict(test))

Expected behavior I would expect a in this example to be nonnull since it has a default value.

$ python issue.py
{'b': True, 'i': 1, 's': 'string', 'a': A(a=0), 'e': 'E1'}

Actual behavior Instead the a field is None.

$ python issue.py
{'b': True, 'i': 1, 's': 'string', 'a': None, 'e': 'E1'}

Desktop:

Additional context In addition, there is no way to toggle the optional fields to None if None isn't already the default. BooleanOptionalAction only sets the flag to False, but some classes like lightning.pytorch.callbacks.DeviceStatsMonitor have three modes (None/False/True) and that isn't that clearly representable yet. I think this will be easier though. The dataclass default issue seems more challenging.