Repository: omni-us/jsonargparse
Pull request #417 (2023-11-01 15:22:51)

Fix links applied on parse failing when source is a class with a nested callable
## Before submitting

- [x] Did you read the [contributing guideline](https://github.com/omni-us/jsonargparse/blob/main/CONTRIBUTING.rst)?
- [n/a] Did you update **the documentation**? (readme and public docstrings)
- [x] Did you write **unit tests** such that there is 100% coverage on related code? (required for bug fixes and new features)
- [x] Did you verify that new and existing **tests pass locally**?
- [x] Did you make sure that all changes preserve **backward compatibility**?
- [x] Did you update **the CHANGELOG**? (not for typos, docs, test updates, or minor internal changes/refactors)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=omni-us_jsonargparse&pullRequest=417)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=417&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=417&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=417&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=417&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=417&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=417&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=omni-us_jsonargparse&pullRequest=417&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=omni-us_jsonargparse&pullRequest=417&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=omni-us_jsonargparse&pullRequest=417&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=417&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=417&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=omni-us_jsonargparse&pullRequest=417&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=omni-us_jsonargparse&pullRequest=417) No Coverage information  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=omni-us_jsonargparse&pullRequest=417&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=omni-us_jsonargparse&pullRequest=417&metric=new_duplicated_lines_density&view=list)


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/omni-us/jsonargparse/pull/417?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:
> Comparison is base [(`99378fe`)](https://app.codecov.io/gh/omni-us/jsonargparse/commit/99378fee7227cb8423db58c8d43a87e77c83d507?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) 100.00% compared to head [(`913b227`)](https://app.codecov.io/gh/omni-us/jsonargparse/pull/417?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) 100.00%.

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


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

| [Flag](https://app.codecov.io/gh/omni-us/jsonargparse/pull/417/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/417/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `88.24% <100.00%> (ø)` | |
| [py3.10_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/417/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.10_pydantic1](https://app.codecov.io/gh/omni-us/jsonargparse/pull/417/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `48.30% <0.00%> (ø)` | |
| [py3.10_pydantic2](https://app.codecov.io/gh/omni-us/jsonargparse/pull/417/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `48.03% <0.00%> (ø)` | |
| [py3.10_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/417/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/417/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `88.22% <100.00%> (ø)` | |
| [py3.11_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/417/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_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/417/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/417/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `88.22% <100.00%> (ø)` | |
| [py3.12_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/417/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_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/417/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.7](https://app.codecov.io/gh/omni-us/jsonargparse/pull/417/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `88.81% <100.00%> (ø)` | |
| [py3.7_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/417/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.48% <100.00%> (ø)` | |
| [py3.7_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/417/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.47% <100.00%> (ø)` | |
| [py3.8](https://app.codecov.io/gh/omni-us/jsonargparse/pull/417/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `88.96% <100.00%> (ø)` | |
| [py3.8_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/417/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.51% <100.00%> (ø)` | |
| [py3.8_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/417/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.49% <100.00%> (ø)` | |
| [py3.9](https://app.codecov.io/gh/omni-us/jsonargparse/pull/417/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `88.81% <100.00%> (ø)` | |
| [py3.9_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/417/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.37% <100.00%> (ø)` | |
| [py3.9_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/417/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.37% <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.

| [Files](https://app.codecov.io/gh/omni-us/jsonargparse/pull/417?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | Coverage Δ | |
|---|---|---|
| [jsonargparse/\_typehints.py](https://app.codecov.io/gh/omni-us/jsonargparse/pull/417?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us#diff-anNvbmFyZ3BhcnNlL190eXBlaGludHMucHk=) | `100.00% <100.00%> (ø)` | |


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/omni-us/jsonargparse/pull/417?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 bf1260d..553028a 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -12,6 +12,14 @@ The semantic versioning only considers the public API as described in
 paths are considered internals and can change in minor and patch releases.
 
 
+v4.26.3 (2023-11-??)
+--------------------
+
+Fixed
+^^^^^
+- Links applied on parse failing when source is a class with a nested callable.
+
+
 v4.26.2 (2023-10-26)
 --------------------
 
diff --git a/jsonargparse/_typehints.py b/jsonargparse/_typehints.py
index 0cd7afe..3042de3 100644
--- a/jsonargparse/_typehints.py
+++ b/jsonargparse/_typehints.py
@@ -472,7 +472,7 @@ class ActionTypeHint(Action):
 
                 prev_val = cfg.get(self.dest) if cfg else None
                 if prev_val is None and not sub_defaults.get() and is_subclass_spec(self.default):
-                    prev_val = Namespace(class_path=self.default.class_path)
+                    prev_val = Namespace(class_path=self.default["class_path"])
 
                 kwargs = {
                     "sub_add_kwargs": getattr(self, "sub_add_kwargs", {}),


Test patch:
diff --git a/jsonargparse_tests/test_link_arguments.py b/jsonargparse_tests/test_link_arguments.py
index 53b44a5..83c71c2 100644
--- a/jsonargparse_tests/test_link_arguments.py
+++ b/jsonargparse_tests/test_link_arguments.py
@@ -2,7 +2,7 @@ from __future__ import annotations
 
 from calendar import Calendar, TextCalendar
 from dataclasses import dataclass
-from typing import Any, List, Mapping, Optional, Union
+from typing import Any, Callable, List, Mapping, Optional, Union
 
 import pytest
 import yaml
@@ -417,6 +417,31 @@ def test_on_parse_save_required_target_subclass_param(parser, tmp_cwd):
     assert saved == {"a": {"a": 1}, "c": {"class_path": f"{__name__}.RequiredTargetC", "init_args": {}}}
 
 
+class Optimizer:
+    def __init__(self, params: List[int], lr: float):
+        self.params = params
+        self.lr = lr
+
+
+class Model:
+    def __init__(self, label: str, optimizer: Callable[[List[int]], Optimizer] = lambda p: Optimizer(p, lr=0.01)):
+        self.label = label
+        self.optimizer = optimizer
+
+
+def get_model_label(model):
+    return model.init_args.label
+
+
+def test_on_parse_nested_callable(parser):
+    parser.add_subclass_arguments(Model, "model")
+    parser.add_argument("--data.label", type=str)
+    parser.link_arguments("model", "data.label", compute_fn=get_model_label)
+    cfg = parser.parse_args([f"--model={__name__}.Model", "--model.label=value"])
+    assert cfg.data == Namespace(label="value")
+    assert cfg.model.init_args.optimizer.init_args == Namespace(lr=0.01)
+
+
 # tests for links applied on instantiate