Repository: malte-soe/dataclass-cli

Issue:
Ability to add help messages and choices

Discussion:

Patch:
diff --git a/dataclass_cli/__init__.py b/dataclass_cli/__init__.py
index 55577c1..5e7425b 100644
--- a/dataclass_cli/__init__.py
+++ b/dataclass_cli/__init__.py
@@ -1,1 +1,1 @@
-from dataclass_cli.parser import add
+from dataclass_cli.parser import Options, add
diff --git a/dataclass_cli/parser.py b/dataclass_cli/parser.py
index e58f7a4..4ae8182 100644
--- a/dataclass_cli/parser.py
+++ b/dataclass_cli/parser.py
@@ -1,8 +1,14 @@
 import argparse
 import dataclasses
+import enum
 from typing import Dict, List, Union
 
 
+class Options(str, enum.Enum):
+    POSSIBLE_VALUES = enum.auto()
+    HELP_TEXT = enum.auto()
+
+
 def add(
     cls,
     *,
@@ -11,13 +17,19 @@ def add(
     _parser=argparse.ArgumentParser(),
 ):
     assert dataclasses.is_dataclass(cls)
+
     name = cls.__name__.lower()
     assert name not in _classes
     _classes[name] = [arg.name for arg in dataclasses.fields(cls)]
+
     group = _parser.add_argument_group(name)
     for field in dataclasses.fields(cls):
         group.add_argument(
-            f"--{name}_{field.name}", type=field.type, default=field.default,
+            f"--{name}_{field.name}",
+            type=field.type,
+            default=field.default,
+            choices=field.metadata.get(Options.POSSIBLE_VALUES, None),
+            help=field.metadata.get(Options.HELP_TEXT, None),
         )
 
     original_init = cls.__init__


Test patch:
diff --git a/tests/test_dc_cli.py b/tests/test_dc_cli.py
index 5ec6b1c..e3d51ca 100644
--- a/tests/test_dc_cli.py
+++ b/tests/test_dc_cli.py
@@ -1,7 +1,8 @@
 import argparse
 import unittest
-from dataclasses import dataclass
+from dataclasses import dataclass, field
 from functools import partial
+from io import StringIO
 from unittest import mock
 
 import dataclass_cli
@@ -64,3 +65,57 @@ class TestDcCli(unittest.TestCase):
             parsed_dc = Dataclass2()
             self.assertEqual(parsed_dc.name, name2)
             self.assertEqual(parsed_dc.number, number2)
+
+    def test_no_possible_value_option(self):
+        possible_values = [1, 2, 3]
+
+        @self.add
+        @dataclass
+        class DataclassWithChoices:
+            number: int = field(
+                default=1,
+                metadata={dataclass_cli.Options.POSSIBLE_VALUES: possible_values},
+            )
+
+        number = 0
+        testargs = f"test.py --dataclasswithchoices_number {number}".split()
+        with mock.patch("sys.argv", testargs), self.assertRaises(
+            SystemExit
+        ), mock.patch("sys.stderr", new=StringIO()) as fake_out:
+            _ = DataclassWithChoices()
+        self.assertIn("invalid choice", fake_out.getvalue())
+
+    def test_possible_value_option(self):
+        possible_values = [1, 2, 3]
+
+        @self.add
+        @dataclass
+        class DataclassWithChoices:
+            number: int = field(
+                default=1,
+                metadata={dataclass_cli.Options.POSSIBLE_VALUES: possible_values},
+            )
+
+        number = possible_values[0]
+        testargs = f"test.py --dataclasswithchoices_number {number}".split()
+        with mock.patch("sys.argv", testargs):
+            parsed_dc = DataclassWithChoices()
+        self.assertEqual(parsed_dc.number, number)
+
+    def test_help_text(self):
+        help_text = "This is a help message!"
+
+        @self.add
+        @dataclass
+        class DataclassWithHelp:
+            number: int = field(
+                default=1, metadata={dataclass_cli.Options.HELP_TEXT: help_text},
+            )
+
+        testargs = f"test.py -h".split()
+        with mock.patch("sys.argv", testargs), self.assertRaises(
+            SystemExit
+        ), mock.patch("sys.stdout", new=StringIO()) as fake_out:
+            _ = DataclassWithHelp()
+
+        self.assertIn(help_text, fake_out.getvalue())