lebrice / SimpleParsing

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

The default_factory for subgroups will not set the default metavar in FieldWrapper #187

Closed zhiruiluo closed 1 year ago

zhiruiluo commented 1 year ago

Describe the bug The default_factory for subgroups will not set the default metavar in FieldWrapper.

To Reproduce

# test_subgroups_default_factory.py
from typing import Union
from dataclasses import dataclass, is_dataclass, MISSING
import pytest

from simple_parsing import subgroups

from .testutils import TestSetup

@dataclass
class Foo:
    a: int = 1
    b: int = 2

@dataclass
class Bar:
    c: int = 1
    d: int = 2

@dataclass
class Bob(TestSetup):
    thing: Union[Foo, Bar] = subgroups({"foo_thing": Foo, "bar_thing": Bar}, default_factory=lambda: Bar(d=3))
    # the original design will only work when using the "default" argument.
    # thing: Union[Foo, Bar] = subgroups({"foo_thing": Foo, "bar_thing": Bar}, default=Bar(d=3)) 

def test_default_factory_subgroups():
    assert '--thing.c' in Bob.get_help_text('--help')

Expected behavior Test passed

Actual behavior Failed

AssertionError: assert '--thing.c' in ''

Desktop (please complete the following information):

Additional context The below default_value function check both field.default and field.default_factory.

# utilis.py
def default_value(field: dataclasses.Field) -> T | _MISSING_TYPE:
    """Returns the default value of a field in a dataclass, if available.
    When not available, returns `dataclasses.MISSING`.

    Args:
        field (dataclasses.Field): The dataclasses.Field to get the default value of.

    Returns:
        Union[T, _MISSING_TYPE]: The default value for that field, if present, or None otherwise.
    """
    if field.default is not dataclasses.MISSING:
        return field.default
    elif field.default_factory is not dataclasses.MISSING:  # type: ignore
        constructor = field.default_factory  # type: ignore
        return constructor()
    else:
        return dataclasses.MISSING

However, the subgroups give the default value of "default" "None" instead of MISSING.

def subgroups(
    subgroups: Dict[str, Type[T]],
    *args,
    default: Union[T, Type[T], None] = None,
    **kwargs,
) -> T:
zhiruiluo commented 1 year ago

Closed with PR #185.