Repository: omni-us/jsonargparse

Issue:
parser.add_dataclass_arguments doesn't support fields with init=False
<!-- If you like this project, please ⭐ star it https://github.com/omni-us/jsonargparse/ -->

## 🐛 Bug report

<!-- A clear and concise description of the bug. -->
If I have a dataclass with protected members (ex: _foo) in a dataclass, meaning I set the init param at False, I have a keyerror during
 
### To reproduce

<!--
Please include a code snippet that reproduces the bug and the output that
running it gives. The following snippet templates might help:

1. Using the CLI function

```python
import jsonargparse

# Here define one or more functions or classes
def func1(param1: int, ...):
    ...

# Run the CLI providing the components
jsonargparse.CLI([func1, ...], error_handler=None)
```

2. Manually constructing a parser

```python
import jsonargparse

parser = jsonargparse.ArgumentParser(error_handler=None)
# Here add to the parser only argument(s) relevant to the problem

# If a yaml config is required, it can be included in the same snippet as follows:
import yaml
parser.add_argument('--config', action=jsonargparse.ActionConfigFile)
config = yaml.safe_dump({
    'key1': 'val1',
})

# Preferable that the command line arguments are given to the parse_args call
result = parser.parse_args([f'--config={config}', '--key2=val2', ...])

# If the problem is in the parsed result, print it to stdout
print(parser.dump(result))

# If the problem is in class instantiation
parser.instantiate_classes(result)
```
-->
if I have a main.py file like this:
```python
from dataclasses import dataclass, field
import jsonargparse
@dataclass
class Model:
    param_1: str
    _param_2: str = field(init=False)

if __name__ == '__main__':
    for field in fields(Model):
        print(f'field name: {field.name}, init: {field.init}')
    parser: ArgumentParser = ArgumentParser(parser_mode='omegaconf')
    parser.add_dataclass_arguments(theclass=Model, nested_key='model')
```
By running python main.py --model my_config.yaml
I'm having an error: KeyError: '_param_2' at line parser.add_dataclass_arguments(theclass=Model, nested_key='model').
It seems related to the [signatures.py, line 418 of add_dataclass_arguments()](https://github.com/omni-us/jsonargparse/blob/1fce98951f6d41064a516719fd7940b3f00ee08b/jsonargparse/signatures.py#L418) :
```python
for field in dataclasses.fields(theclass):
            self._add_signature_parameter(
                group,
                nested_key,
                params[field.name],
                added_args,
                fail_untyped=fail_untyped,
                default=defaults.get(field.name, inspect_empty),
            )

```
Would be nice to cast if a field has its init parameter set to True:
```python
for field in dataclasses.fields(theclass):
    if field.init:
            self._add_signature_parameter(
                group,
                nested_key,
                params[field.name],
                added_args,
                fail_untyped=fail_untyped,
                default=defaults.get(field.name, inspect_empty),
            )
```


### Expected behavior

<!-- Describe how the behavior or output of the reproduction snippet should be. -->
add_dataclass_arguments should accept non initializabled parameters

### Environment

<!-- Fill in the list below. -->

- jsonargparse version ( 4.8.0):
- Python version (3.10):
- How jsonargparse was installed (`conda install jsonargparse`):
- OS (Linux, Ubuntu):


Comments:

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 1aafaf5..1b29009 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -17,12 +17,13 @@ v4.20.1 (2023-03-??)
 
 Fixed
 ^^^^^
-- Allow ``discard_init_args_on_class_path_change`` to handle more nested contexts `#247
-  <https://github.com/omni-us/jsonargparse/issues/247>`__.
-
 - Dump not working for partial callable with return instance
   `pytorch-lightning#15340 (comment)
   <https://github.com/Lightning-AI/lightning/issues/15340#issuecomment-1439203008>`__.
+- Allow ``discard_init_args_on_class_path_change`` to handle more nested
+  contexts `#247 <https://github.com/omni-us/jsonargparse/issues/247>`__.
+- Failure with dataclasses that have field with ``init=False`` `#252
+  <https://github.com/omni-us/jsonargparse/issues/252>`__.
 
 
 v4.20.0 (2023-02-20)
diff --git a/jsonargparse/signatures.py b/jsonargparse/signatures.py
index cc90a23..f8648e4 100644
--- a/jsonargparse/signatures.py
+++ b/jsonargparse/signatures.py
@@ -416,14 +416,15 @@ class SignatureArguments(LoggerProperty):
         added_args: List[str] = []
         params = {p.name: p for p in get_signature_parameters(theclass, None, logger=self.logger)}
         for field in dataclasses.fields(theclass):
-            self._add_signature_parameter(
-                group,
-                nested_key,
-                params[field.name],
-                added_args,
-                fail_untyped=fail_untyped,
-                default=defaults.get(field.name, inspect_empty),
-            )
+            if field.name in params:
+                self._add_signature_parameter(
+                    group,
+                    nested_key,
+                    params[field.name],
+                    added_args,
+                    fail_untyped=fail_untyped,
+                    default=defaults.get(field.name, inspect_empty),
+                )
 
         return added_args
 


Test patch:
diff --git a/jsonargparse_tests/test_signatures.py b/jsonargparse_tests/test_signatures.py
index 3c85032..3dce258 100755
--- a/jsonargparse_tests/test_signatures.py
+++ b/jsonargparse_tests/test_signatures.py
@@ -1615,6 +1615,19 @@ class DataclassesTests(unittest.TestCase):
         self.assertRaises(ArgumentError, lambda: parser.parse_args([]))
 
 
+    def test_dataclass_field_init_false(self):
+
+        @dataclasses.dataclass
+        class DataInitFalse:
+            p1: str = '-'
+            p2: str = dataclasses.field(init=False)
+
+        parser = ArgumentParser(exit_on_error=False)
+        added = parser.add_dataclass_arguments(DataInitFalse, 'd')
+        self.assertEqual(added, ['d.p1'])
+        self.assertEqual(parser.get_defaults(), Namespace(d=Namespace(p1='-')))
+
+
     def test_dataclass_field_default_factory(self):
 
         @dataclasses.dataclass