Repository: huggingface/transformers (Python)
Date: 2022-09-27 18:49:45+00:00
Task type: Feature

Issue:
HfArgumentParser support yaml parser
### Feature request

HfArgumentParser now supports for parsing dict and json files, will it be possible to support for parsing the widely used yaml files?

### Motivation

I think using yaml is a good way to record arguments.

### Your contribution

Not yet.


Issue discussion:
cc @sgugger 

If you want to open a PR, please go ahead!
You can just use
`parser.parse_dict(yaml.safe_load(f))`
Which could all go in a `parse_yaml_file` method :-) Doing this and also refactoring the `parse_json_file` to use `parse_dict`, as well as adding small tests would be nice additions that shouldn't be too hard, so putting the "Good first issue" label here.

To summarize:
- [ ] adding as `parse_yaml_file` method to `HfArgumentParser` with the code above
- [ ] refactor the dupe code between `parse_json_file` and `parse_dict` similar to the code above
- [ ] add a small test of `parse_yaml_file`
- [ ] add a small test of `parse_json_file`

This could be done in a single PR or separate ones :-)

Hi, I would like to work on it


How can i write test for  `parse_yaml_file` and `parse_json_file` it will require an external json and yaml file to testing
No, you can create it during the test by saving some dictionary (look at the `parse_dict` tests) into a temporary file.
Hey, @sgugger I have written the test for `parse_yaml_file` and `parse_json_file` using tempfile is it acceptable?? Also it passes the tests.
![Capture](https://user-images.githubusercontent.com/88665786/192546217-56e3f77f-85ca-4b7f-b1fd-cd54d6f6a3bb.PNG)

You can also use the context manager for a temp dir.
```
with tempfile.TemporaryDirectory() as tmp_dir:
    # Save file in tmp_dir as usual
    # do the tests
```
The plus for this is that it's automatically cleaned up when you exit the with block (whereas the temp file will stay until the next restart).
Okay I will change that!

Solution 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
@@ -281,7 +281,9 @@ def parse_json_file(self, json_file: str, allow_extra_keys: bool = False) -> Tup
 
                 - the dataclass instances in the same order as they were passed to the initializer.
         """
-        outputs = self.parse_dict(json.loads(Path(json_file).read_text()), allow_extra_keys=allow_extra_keys)
+        open_json_file = open(Path(json_file))
+        data = json.loads(open_json_file.read())
+        outputs = self.parse_dict(data, allow_extra_keys=allow_extra_keys)
         return tuple(outputs)
 
     def parse_yaml_file(self, yaml_file: str, allow_extra_keys: bool = False) -> Tuple[DataClass, ...]:
@@ -301,5 +303,5 @@ def parse_yaml_file(self, yaml_file: str, allow_extra_keys: bool = False) -> Tup
 
                 - the dataclass instances in the same order as they were passed to the initializer.
         """
-        outputs = self.parse_dict(yaml.safe_load(yaml_file), allow_extra_keys=allow_extra_keys)
+        outputs = self.parse_dict(yaml.safe_load(Path(yaml_file).read_text()), allow_extra_keys=allow_extra_keys)
         return tuple(outputs)


Test patch:
diff --git a/tests/utils/test_hf_argparser.py b/tests/utils/test_hf_argparser.py
--- a/tests/utils/test_hf_argparser.py
+++ b/tests/utils/test_hf_argparser.py
@@ -13,12 +13,17 @@
 # limitations under the License.
 
 import argparse
+import json
+import os
+import tempfile
 import unittest
 from argparse import Namespace
 from dataclasses import dataclass, field
 from enum import Enum
+from pathlib import Path
 from typing import List, Optional
 
+import yaml
 from transformers import HfArgumentParser, TrainingArguments
 from transformers.hf_argparser import string_to_bool
 
@@ -258,6 +263,43 @@ def test_parse_dict_extra_key(self):
 
         self.assertRaises(ValueError, parser.parse_dict, args_dict, allow_extra_keys=False)
 
+    def test_parse_json(self):
+        parser = HfArgumentParser(BasicExample)
+
+        args_dict_for_json = {
+            "foo": 12,
+            "bar": 3.14,
+            "baz": "42",
+            "flag": True,
+        }
+        with tempfile.TemporaryDirectory() as tmp_dir:
+            temp_local_path = os.path.join(tmp_dir, "temp_json")
+            os.mkdir(temp_local_path)
+            with open(temp_local_path + ".json", "w+") as f:
+                json.dump(args_dict_for_json, f)
+            parsed_args = parser.parse_yaml_file(Path(temp_local_path + ".json"))[0]
+
+        args = BasicExample(**args_dict_for_json)
+        self.assertEqual(parsed_args, args)
+
+    def test_parse_yaml(self):
+        parser = HfArgumentParser(BasicExample)
+
+        args_dict_for_yaml = {
+            "foo": 12,
+            "bar": 3.14,
+            "baz": "42",
+            "flag": True,
+        }
+        with tempfile.TemporaryDirectory() as tmp_dir:
+            temp_local_path = os.path.join(tmp_dir, "temp_yaml")
+            os.mkdir(temp_local_path)
+            with open(temp_local_path + ".yaml", "w+") as f:
+                yaml.dump(args_dict_for_yaml, f)
+            parsed_args = parser.parse_yaml_file(Path(temp_local_path + ".yaml"))[0]
+        args = BasicExample(**args_dict_for_yaml)
+        self.assertEqual(parsed_args, args)
+
     def test_integration_training_args(self):
         parser = HfArgumentParser(TrainingArguments)
         self.assertIsNotNone(parser)