Repository: huggingface/transformers

Issue:
hf_argparser doesn't set the required flag on non-defaulted enums
## Environment info

- `transformers` version: 3.0.0-4.3.3
- Platform: macOS
- Python version: 3.9
- PyTorch version (GPU?): n/a
- Tensorflow version (GPU?): n/a
- Using GPU in script?: n/a
- Using distributed or parallel set-up in script?: n/a

### Who can help

I'm not sure who the owner is of hf_argparser.

## Information

We're using hf_argparser to parse our experiment config into dataclasses before training.

## To reproduce

Steps to reproduce the behavior:

1. Add an enum argument without a default to a dataclass
2. Parse the command line arguments without supplying the enum argument
3. Should have raised an exception and printed the usage, instead defaults the value to `None`.

## Expected behavior

It should raise an exception. The issue is on https://github.com/huggingface/transformers/blob/master/src/transformers/hf_argparser.py#L100, the if statement should have an else which sets `kwargs["required"]=True`, the same way line [134](https://github.com/huggingface/transformers/blob/master/src/transformers/hf_argparser.py#L134) does.

I can work up a patch if you agree this is an issue. I think it will also occur with anything that falls into [this branch](https://github.com/huggingface/transformers/blob/master/src/transformers/hf_argparser.py#L118) of the if too.



Discussion:
I agree with your assessment and your proposed fix, so by all means, please suggest a PR! Thanks!

Resolving patch:
diff --git a/src/transformers/hf_argparser.py b/src/transformers/hf_argparser.py
--- a/src/transformers/hf_argparser.py
+++ b/src/transformers/hf_argparser.py
@@ -99,6 +99,8 @@ def _add_dataclass_arguments(self, dtype: DataClassType):
                 kwargs["type"] = type(kwargs["choices"][0])
                 if field.default is not dataclasses.MISSING:
                     kwargs["default"] = field.default
+                else:
+                    kwargs["required"] = True
             elif field.type is bool or field.type == Optional[bool]:
                 if field.default is True:
                     self.add_argument(f"--no_{field.name}", action="store_false", dest=field.name, **kwargs)
@@ -124,6 +126,8 @@ def _add_dataclass_arguments(self, dtype: DataClassType):
                 ), "{} cannot be a List of mixed types".format(field.name)
                 if field.default_factory is not dataclasses.MISSING:
                     kwargs["default"] = field.default_factory()
+                elif field.default is dataclasses.MISSING:
+                    kwargs["required"] = True
             else:
                 kwargs["type"] = field.type
                 if field.default is not dataclasses.MISSING:


Test patch:
diff --git a/tests/test_hf_argparser.py b/tests/test_hf_argparser.py
--- a/tests/test_hf_argparser.py
+++ b/tests/test_hf_argparser.py
@@ -78,6 +78,16 @@ class ListExample:
     foo_float: List[float] = list_field(default=[0.1, 0.2, 0.3])
 
 
+@dataclass
+class RequiredExample:
+    required_list: List[int] = field()
+    required_str: str = field()
+    required_enum: BasicEnum = field()
+
+    def __post_init__(self):
+        self.required_enum = BasicEnum(self.required_enum)
+
+
 class HfArgumentParserTest(unittest.TestCase):
     def argparsersEqual(self, a: argparse.ArgumentParser, b: argparse.ArgumentParser) -> bool:
         """
@@ -186,6 +196,15 @@ def test_with_optional(self):
         args = parser.parse_args("--foo 12 --bar 3.14 --baz 42 --ces a b c --des 1 2 3".split())
         self.assertEqual(args, Namespace(foo=12, bar=3.14, baz="42", ces=["a", "b", "c"], des=[1, 2, 3]))
 
+    def test_with_required(self):
+        parser = HfArgumentParser(RequiredExample)
+
+        expected = argparse.ArgumentParser()
+        expected.add_argument("--required_list", nargs="+", type=int, required=True)
+        expected.add_argument("--required_str", type=str, required=True)
+        expected.add_argument("--required_enum", type=str, choices=["titi", "toto"], required=True)
+        self.argparsersEqual(parser, expected)
+
     def test_parse_dict(self):
         parser = HfArgumentParser(BasicExample)