lebrice / SimpleParsing

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

support `dataclasses.InitVar` for python>=3.8 #171

Closed mixilchenko closed 1 year ago

lebrice commented 1 year ago

Wow, really interesting stuff @mixilchenko ! I'll do a proper review soon!

mixilchenko commented 1 year ago

Speaking about dataclasses, InitVar always looked for me like some crutch when

I am not so good with examples but here it is (https://mypy-play.net/?mypy=0.981&python=3.10 launchable)

from dataclasses import dataclass, field, InitVar
from functools import cached_property

@dataclass
class A1:
    value: int | str

    def __post_init__(self) -> None:
        self.value = int(self.value)

@dataclass
class A2:
    value: int

    def __post_init__(self) -> None:
        self.value = int(self.value)

@dataclass
class A3:
    arg: InitVar[int | str]
    value: int = field(init=False)

    def __post_init__(self, arg: int | str) -> None:
        self.value = int(arg)

class A4:
    arg: int | str

    @cached_property
    def value(self) -> int:
        return int(self.arg)

a1 = A1('2')
reveal_type(a1.value)  # Revealed type is "Union[builtins.int, builtins.str]"

a2 = A2('2')  # Argument 1 to "A2" has incompatible type "str"; expected "int"
reveal_type(a2.value)  # Revealed type is "builtins.int"

a3 = A3('2')
reveal_type(a3.value)  # Revealed type is "builtins.int"

a4 = A4('2')
reveal_type(a4.value)  # Revealed type is "builtins.int"

In that case, A3 is preferred over A4 for me because arg is not saved inside

Speaking about argument parser, I don't think that any additional marks are needed. InitVars do look like usual __init__ arguments and that seems totally reasonable for me.