From 5913049bda29282fca66d2127249d3bfb6c177aa Mon Sep 17 00:00:00 2001 From: Yamada Date: Wed, 7 Jun 2023 07:29:07 +0900 Subject: [PATCH 1/5] Support __futre__.annotations When importing __future__.annotations, class annotations are evaluated lazily, and dataclasses.Field.type becomes str instead of actual type. To access its accutual type, we patch the member. Ref: https://github.com/mivade/argparse_dataclass/issues/47 --- argparse_dataclass.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/argparse_dataclass.py b/argparse_dataclass.py index b93ac8e..4fa4876 100644 --- a/argparse_dataclass.py +++ b/argparse_dataclass.py @@ -316,6 +316,20 @@ def parse_known_args( return options_class(**kwargs), others +def _fields(options_class: typing.Type[OptionsType]) -> typing.Tuple[Field, ...]: + """Get tuple of Field for dataclass.""" + type_hits = get_type_hints(options_class) + + def _ensure_type(_f): + # When importing __future__.annotations, `Field.type` becomes `str` + # Ref: https://github.com/mivade/argparse_dataclass/issues/47 + if isinstance(_f.type, str): + _f.type = type_hits[_f.name] + return _f + + return tuple(_ensure_type(_f) for _f in fields(options_class)) + + def _add_dataclass_options( options_class: typing.Type[OptionsType], parser: argparse.ArgumentParser ) -> None: From e0eadcd611db79ba99fcb05e7adeb5fb01e37b7f Mon Sep 17 00:00:00 2001 From: Yamada Date: Thu, 8 Jun 2023 05:34:55 +0900 Subject: [PATCH 2/5] Fix typo: type_hits -> type_hints --- argparse_dataclass.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/argparse_dataclass.py b/argparse_dataclass.py index 4fa4876..da1cd3c 100644 --- a/argparse_dataclass.py +++ b/argparse_dataclass.py @@ -318,13 +318,13 @@ def parse_known_args( def _fields(options_class: typing.Type[OptionsType]) -> typing.Tuple[Field, ...]: """Get tuple of Field for dataclass.""" - type_hits = get_type_hints(options_class) + type_hints = get_type_hints(options_class) def _ensure_type(_f): # When importing __future__.annotations, `Field.type` becomes `str` # Ref: https://github.com/mivade/argparse_dataclass/issues/47 if isinstance(_f.type, str): - _f.type = type_hits[_f.name] + _f.type = type_hints[_f.name] return _f return tuple(_ensure_type(_f) for _f in fields(options_class)) From d935a590743fdb0e0d2a8c454d19f6bac4c784d0 Mon Sep 17 00:00:00 2001 From: Yamada Date: Thu, 8 Jun 2023 05:35:17 +0900 Subject: [PATCH 3/5] Fix: Use patched fields --- argparse_dataclass.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/argparse_dataclass.py b/argparse_dataclass.py index da1cd3c..41989dd 100644 --- a/argparse_dataclass.py +++ b/argparse_dataclass.py @@ -336,7 +336,7 @@ def _add_dataclass_options( if not is_dataclass(options_class): raise TypeError("cls must be a dataclass") - for field in fields(options_class): + for field in _fields(options_class): args = field.metadata.get("args", [f"--{field.name.replace('_', '-')}"]) positional = not args[0].startswith("-") kwargs = { From 9120ef0598b179f5a3dff74efa51d37c9be87995 Mon Sep 17 00:00:00 2001 From: Yamada Date: Thu, 8 Jun 2023 05:38:08 +0900 Subject: [PATCH 4/5] Fix: missing module --- argparse_dataclass.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/argparse_dataclass.py b/argparse_dataclass.py index 41989dd..13df728 100644 --- a/argparse_dataclass.py +++ b/argparse_dataclass.py @@ -318,7 +318,7 @@ def parse_known_args( def _fields(options_class: typing.Type[OptionsType]) -> typing.Tuple[Field, ...]: """Get tuple of Field for dataclass.""" - type_hints = get_type_hints(options_class) + type_hints = typing.get_type_hints(options_class) def _ensure_type(_f): # When importing __future__.annotations, `Field.type` becomes `str` From bbc24de1f77459c86375208fb68ad6cda55becf0 Mon Sep 17 00:00:00 2001 From: Yamada Date: Thu, 8 Jun 2023 07:33:53 +0900 Subject: [PATCH 5/5] Add Test under __futuer__.annotations --- tests/test_annotations.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/test_annotations.py diff --git a/tests/test_annotations.py b/tests/test_annotations.py new file mode 100644 index 0000000..c514174 --- /dev/null +++ b/tests/test_annotations.py @@ -0,0 +1,23 @@ +from __future__ import annotations +import unittest +from argparse_dataclass import dataclass + + +@dataclass +class Opt: + x: int = 42 + y: bool = False + + +class ArgParseTests(unittest.TestCase): + def test_basic(self): + params = Opt.parse_args([]) + self.assertEqual(42, params.x) + self.assertEqual(False, params.y) + params = Opt.parse_args(["--x=10", "--y"]) + self.assertEqual(10, params.x) + self.assertEqual(True, params.y) + + +if __name__ == "__main__": + unittest.main()