Repository: omni-us/jsonargparse

Issue:
Cannot parse a list of dataclasses with optional dataclass member
<!-- If you like this project, please ⭐ star it https://github.com/omni-us/jsonargparse/ -->

## 🐛 Bug report

I am getting an when parsing a list of `dataclass` objects, where the `dataclass` has an optional member which is another `dataclass`.

### To reproduce

In `script.py`
```python
from dataclasses import dataclass
from typing import Optional
from jsonargparse import CLI

@dataclass
class A:
    x: int

@dataclass
class B:
    a: Optional[A] = None

def f(b: list[B]):
    print(b)

CLI(f, as_positional=False)
```

In `config.yaml`
```yaml
b:
  - a:
      x: 1
```

In terminal
```
script.py --config config.yaml
```

### Expected behavior

Expected console output
```
[B(a=A(x=1))]
```

Actual console output
```
usage: script.py [-h] [--config CONFIG] [--print_config[=flags]] --b B
error: Validation failed: No action for key "a.x" to set its default.
```

### Environment

- jsonargparse version: 4.29.0
- Python version: 3.11.4
- How jsonargparse was installed: `pip install jsonargparse[signatures]`
- OS: Windows 11 Enterprise, 10.0.22631


Discussion:
sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=omni-us_jsonargparse&pullRequest=527) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=527&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=527&resolutions=WONTFIX)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=omni-us_jsonargparse&pullRequest=527&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/no-data-16px.png '') No data about Coverage  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=omni-us_jsonargparse&pullRequest=527&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=omni-us_jsonargparse&pullRequest=527)


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/omni-us/jsonargparse/pull/527?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 100.00%. Comparing base [(`3a3c6a0`)](https://app.codecov.io/gh/omni-us/jsonargparse/commit/3a3c6a0ebfc18901d80c862040deda2d03a81f9d?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) to head [(`f347395`)](https://app.codecov.io/gh/omni-us/jsonargparse/commit/f34739593e2ed7bf31a98ca06c7826220ee1b7fe?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us).


<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff            @@
##              main      #527   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           21        21           
  Lines         6188      6188           
=========================================
  Hits          6188      6188           
```

| [Flag](https://app.codecov.io/gh/omni-us/jsonargparse/pull/527/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | Coverage Δ | |
|---|---|---|
| [py3.10](https://app.codecov.io/gh/omni-us/jsonargparse/pull/527/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `87.35% <100.00%> (ø)` | |
| [py3.10_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/527/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `98.64% <100.00%> (ø)` | |
| [py3.10_pydantic1](https://app.codecov.io/gh/omni-us/jsonargparse/pull/527/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `49.28% <100.00%> (ø)` | |
| [py3.10_pydantic2](https://app.codecov.io/gh/omni-us/jsonargparse/pull/527/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `49.06% <100.00%> (ø)` | |
| [py3.10_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/527/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `98.65% <100.00%> (ø)` | |
| [py3.11](https://app.codecov.io/gh/omni-us/jsonargparse/pull/527/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `87.34% <100.00%> (ø)` | |
| [py3.11_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/527/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `98.64% <100.00%> (ø)` | |
| [py3.11_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/527/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `98.65% <100.00%> (ø)` | |
| [py3.12](https://app.codecov.io/gh/omni-us/jsonargparse/pull/527/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `87.48% <100.00%> (ø)` | |
| [py3.12_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/527/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `98.64% <100.00%> (ø)` | |
| [py3.12_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/527/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `98.65% <100.00%> (ø)` | |
| [py3.7](https://app.codecov.io/gh/omni-us/jsonargparse/pull/527/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `87.86% <100.00%> (ø)` | |
| [py3.7_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/527/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.21% <100.00%> (ø)` | |
| [py3.7_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/527/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.24% <100.00%> (ø)` | |
| [py3.8](https://app.codecov.io/gh/omni-us/jsonargparse/pull/527/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `88.05% <100.00%> (ø)` | |
| [py3.8_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/527/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.38% <100.00%> (ø)` | |
| [py3.8_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/527/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.40% <100.00%> (ø)` | |
| [py3.9](https://app.codecov.io/gh/omni-us/jsonargparse/pull/527/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `87.90% <100.00%> (ø)` | |
| [py3.9_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/527/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.27% <100.00%> (ø)` | |
| [py3.9_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/527/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.28% <100.00%> (ø)` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us#carryforward-flags-in-the-pull-request-comment) to find out more.


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/omni-us/jsonargparse/pull/527?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us).


Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 232e5aa..16db672 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -32,6 +32,8 @@ Fixed
   <https://github.com/omni-us/jsonargparse/issues/517>`__).
 - Callable that returns class not using required parameter default from lambda
   (`#523 <https://github.com/omni-us/jsonargparse/pull/523>`__).
+- Failing to parse list of dataclasses with nested optional dataclass (`#527
+  <https://github.com/omni-us/jsonargparse/pull/527>`__).
 
 
 v4.29.0 (2024-05-24)
diff --git a/jsonargparse/_signatures.py b/jsonargparse/_signatures.py
index 8a40804..133f9c0 100644
--- a/jsonargparse/_signatures.py
+++ b/jsonargparse/_signatures.py
@@ -103,7 +103,7 @@ class SignatureArguments(LoggerProperty):
             prefix = nested_key + "." if nested_key else ""
             defaults = default.lazy_get_init_args()
             if defaults:
-                defaults = {prefix + k: v for k, v in defaults.items() if k not in skip}
+                defaults = {prefix + k: v for k, v in defaults.__dict__.items() if k not in skip}
                 self.set_defaults(**defaults)  # type: ignore[attr-defined]
 
         return added_args


Test patch:
diff --git a/jsonargparse_tests/test_dataclass_like.py b/jsonargparse_tests/test_dataclass_like.py
index cbaa97d..995f740 100644
--- a/jsonargparse_tests/test_dataclass_like.py
+++ b/jsonargparse_tests/test_dataclass_like.py
@@ -186,6 +186,22 @@ def test_add_class_optional_without_default(parser):
     assert parser.parse_args(["--param=null"]) == Namespace(param=None)
 
 
+@dataclasses.dataclass
+class ListOptionalA:
+    x: int
+
+
+@dataclasses.dataclass
+class ListOptionalB:
+    a: Optional[ListOptionalA] = None
+
+
+def test_list_nested_optional_dataclass(parser):
+    parser.add_argument("--b", type=List[ListOptionalB])
+    cfg = parser.parse_args(['--b=[{"a":{"x":1}}]'])
+    assert cfg.b == [Namespace(a=Namespace(x=1))]
+
+
 def test_add_argument_dataclass_type(parser):
     parser.add_argument("--b", type=DataClassB, default=DataClassB(b1=7.0))
     cfg = parser.get_defaults()