Repository: omni-us/jsonargparse

Issue:
Appending to dataclass list uses last item as defaults
<!-- If you like this project, please ⭐ star it https://github.com/omni-us/jsonargparse/ -->

## 🐛 Bug report

When the type of an argument is list of dataclass instances, the first item in the list uses the dataclass's defaults as expected, but appending further items always uses the last item's values as defaults instead of using the actual defaults.

### To reproduce

`test.py`:
```python
from dataclasses import dataclass
import jsonargparse

@dataclass
class A:
    x: int = 1
    foo: str = "bar"

def main(a: list[A] = None) -> None:
    print(a)

jsonargparse.CLI(main)
```

Command:
```
python test.py --a+='{}' --a+='{"x":2, "foo": "spam"}' --a+='{"x": 3}'
```

Output (`foo='spam'` is copied from the 2nd item to the 3rd):
```python
[A(x=1, foo='bar'), A(x=2, foo='spam'), A(x=3, foo='spam')]
```

### Expected behavior

Expected output (3rd item uses the default `foo='bar'`):
```python
[A(x=1, foo='bar'), A(x=2, foo='spam'), A(x=3, foo='bar')]
```

### Environment

- jsonargparse version: 4.31.0
- Python version: 3.10.13
- How jsonargparse was installed: `pip install jsonargparse`
- OS: Linux


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=560) **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=560&issueStatuses=OPEN,CONFIRMED&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=560&issueStatuses=ACCEPTED)

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=560&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=omni-us_jsonargparse&pullRequest=560&metric=new_coverage&view=list)  
![](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=560&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=omni-us_jsonargparse&pullRequest=560)


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=560) **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=560&issueStatuses=OPEN,CONFIRMED&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=560&issueStatuses=ACCEPTED)

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=560&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=omni-us_jsonargparse&pullRequest=560&metric=new_coverage&view=list)  
![](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=560&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=omni-us_jsonargparse&pullRequest=560)


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/omni-us/jsonargparse/pull/560?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 [(`c634a88`)](https://app.codecov.io/gh/omni-us/jsonargparse/commit/c634a88f120b96d87f80e079417f0138aabe466f?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) to head [(`1f4039d`)](https://app.codecov.io/gh/omni-us/jsonargparse/commit/1f4039da53bba535ddfec1484d85207c610f53d0?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      #560   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           22        22           
  Lines         6445      6445           
=========================================
  Hits          6445      6445           
```

| [Flag](https://app.codecov.io/gh/omni-us/jsonargparse/pull/560/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/560/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `85.73% <100.00%> (ø)` | |
| [py3.10_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/560/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `98.69% <100.00%> (ø)` | |
| [py3.10_pydantic1](https://app.codecov.io/gh/omni-us/jsonargparse/pull/560/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `48.73% <100.00%> (+0.10%)` | :arrow_up: |
| [py3.10_pydantic2](https://app.codecov.io/gh/omni-us/jsonargparse/pull/560/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `48.51% <100.00%> (+0.10%)` | :arrow_up: |
| [py3.10_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/560/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `98.71% <100.00%> (ø)` | |
| [py3.11](https://app.codecov.io/gh/omni-us/jsonargparse/pull/560/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `85.72% <100.00%> (ø)` | |
| [py3.11_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/560/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `98.69% <100.00%> (ø)` | |
| [py3.11_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/560/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `98.71% <100.00%> (ø)` | |
| [py3.12](https://app.codecov.io/gh/omni-us/jsonargparse/pull/560/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `85.86% <100.00%> (ø)` | |
| [py3.12_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/560/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `98.69% <100.00%> (ø)` | |
| [py3.12_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/560/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `?` | |
| [py3.7](https://app.codecov.io/gh/omni-us/jsonargparse/pull/560/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `86.17% <100.00%> (ø)` | |
| [py3.7_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/560/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.20% <100.00%> (ø)` | |
| [py3.7_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/560/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/560/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `86.37% <100.00%> (ø)` | |
| [py3.8_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/560/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.36% <100.00%> (ø)` | |
| [py3.8_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/560/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.39% <100.00%> (ø)` | |
| [py3.9](https://app.codecov.io/gh/omni-us/jsonargparse/pull/560/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `86.26% <100.00%> (ø)` | |
| [py3.9_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/560/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%> (ø)` | |
| [py3.9_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/560/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.30% <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/560?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 e531ed7..7fc944c 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -19,6 +19,8 @@ Fixed
 ^^^^^
 - ``dict`` types not correctly forwarding previous nested values when parsing
   (`#559 <https://github.com/omni-us/jsonargparse/pull/559>`__).
+- ``List`` type incorrectly using defaults from previous item (`#560
+  <https://github.com/omni-us/jsonargparse/pull/560>`__).
 
 
 v4.32.0 (2024-07-19)
diff --git a/jsonargparse/_typehints.py b/jsonargparse/_typehints.py
index 4c90a74..98b79c3 100644
--- a/jsonargparse/_typehints.py
+++ b/jsonargparse/_typehints.py
@@ -871,10 +871,11 @@ def adapt_typehints(
         elif not isinstance(val, list):
             raise_unexpected_value(f"Expected a {typehint_origin}", val)
         if subtypehints is not None:
-            adapt_kwargs_n = adapt_kwargs
             for n, v in enumerate(val):
                 if isinstance(prev_val, list) and len(prev_val) == len(val):
-                    adapt_kwargs_n = {**adapt_kwargs, "prev_val": prev_val[n]}
+                    adapt_kwargs_n = {**deepcopy(adapt_kwargs), "prev_val": prev_val[n]}
+                else:
+                    adapt_kwargs_n = deepcopy(adapt_kwargs)
                 val[n] = adapt_typehints(v, subtypehints[0], list_item=True, **adapt_kwargs_n)
 
     # Dict, Mapping


Test patch:
diff --git a/jsonargparse_tests/test_dataclass_like.py b/jsonargparse_tests/test_dataclass_like.py
index 963eeaf..818eb3b 100644
--- a/jsonargparse_tests/test_dataclass_like.py
+++ b/jsonargparse_tests/test_dataclass_like.py
@@ -203,6 +203,18 @@ def test_list_nested_optional_dataclass(parser):
     assert cfg.b == [Namespace(a=Namespace(x=1))]
 
 
+@dataclasses.dataclass
+class ItemData:
+    x: int = 1
+    y: str = "one"
+
+
+def test_list_append_defaults(parser):
+    parser.add_argument("--list", type=List[ItemData])
+    cfg = parser.parse_args(["--list+={}", '--list+={"x":2,"y":"two"}', '--list+={"x":3}'])
+    assert cfg.list == [Namespace(x=1, y="one"), Namespace(x=2, y="two"), Namespace(x=3, y="one")]
+
+
 def test_add_argument_dataclass_type(parser):
     parser.add_argument("--b", type=DataClassB, default=DataClassB(b1=7.0))
     cfg = parser.get_defaults()