Repository: omni-us/jsonargparse
Pull request #499 (2024-05-15 20:54:54)

Fix nested parameters failing to parse from command line when value includes space
## 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]: ## [![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=499) **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=499&resolved=false&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/component_measures?id=omni-us_jsonargparse&pullRequest=499&metric=new_accepted_issues&view=list)

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=499&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=499&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=omni-us_jsonargparse&pullRequest=499)


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/omni-us/jsonargparse/pull/499?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 [(`c71531b`)](https://app.codecov.io/gh/omni-us/jsonargparse/commit/c71531b97820c0969ed6ff654bd404cd68381c3e?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) to head [(`83d1f13`)](https://app.codecov.io/gh/omni-us/jsonargparse/pull/499?dropdown=coverage&src=pr&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      #499   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           21        21           
  Lines         6116      6121    +5     
=========================================
+ Hits          6116      6121    +5     
```

| [Flag](https://app.codecov.io/gh/omni-us/jsonargparse/pull/499/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/499/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `87.24% <100.00%> (+0.01%)` | :arrow_up: |
| [py3.10_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/499/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%> (+<0.01%)` | :arrow_up: |
| [py3.10_pydantic1](https://app.codecov.io/gh/omni-us/jsonargparse/pull/499/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `49.10% <91.66%> (+0.04%)` | :arrow_up: |
| [py3.10_pydantic2](https://app.codecov.io/gh/omni-us/jsonargparse/pull/499/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `48.88% <91.66%> (+0.04%)` | :arrow_up: |
| [py3.10_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/499/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `98.66% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.11](https://app.codecov.io/gh/omni-us/jsonargparse/pull/499/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `87.22% <100.00%> (+0.01%)` | :arrow_up: |
| [py3.11_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/499/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%> (+<0.01%)` | :arrow_up: |
| [py3.11_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/499/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `98.66% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.12](https://app.codecov.io/gh/omni-us/jsonargparse/pull/499/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `87.37% <100.00%> (+0.01%)` | :arrow_up: |
| [py3.12_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/499/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%> (+<0.01%)` | :arrow_up: |
| [py3.12_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/499/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `98.66% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.7](https://app.codecov.io/gh/omni-us/jsonargparse/pull/499/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `87.78% <100.00%> (+0.01%)` | :arrow_up: |
| [py3.7_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/499/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.25% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.7_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/499/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.25% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.8](https://app.codecov.io/gh/omni-us/jsonargparse/pull/499/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `87.95% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.8_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/499/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.41% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.8_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/499/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.41% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.9](https://app.codecov.io/gh/omni-us/jsonargparse/pull/499/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `87.79% <100.00%> (+<0.01%)` | :arrow_up: |
| [py3.9_all](https://app.codecov.io/gh/omni-us/jsonargparse/pull/499/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%> (+<0.01%)` | :arrow_up: |
| [py3.9_types](https://app.codecov.io/gh/omni-us/jsonargparse/pull/499/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=omni-us) | `99.29% <100.00%> (+<0.01%)` | :arrow_up: |

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/499?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 5eea704..429e533 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -24,6 +24,8 @@ Fixed
 ^^^^^
 - Parameter resolving falling back to assumptions resolver for optional
   ``Union`` types.
+- Nested parameters failing to parse from command line when value includes
+  space.
 
 
 v4.28.0 (2024-04-17)
diff --git a/jsonargparse/_core.py b/jsonargparse/_core.py
index 36141ef..703a724 100644
--- a/jsonargparse/_core.py
+++ b/jsonargparse/_core.py
@@ -265,13 +265,12 @@ class ArgumentParser(ParserDeprecations, ActionsContainer, ArgumentLinking, argp
         return namespace, args
 
     def _parse_optional(self, arg_string):
-        if arg_string == self._print_config:
-            arg_string += "="
-        arg_parsed = super()._parse_optional(arg_string)
-        subclass_arg = ActionTypeHint.parse_argv_item(arg_string, arg_parsed)
+        subclass_arg = ActionTypeHint.parse_argv_item(arg_string)
         if subclass_arg:
             return subclass_arg
-        return arg_parsed
+        if arg_string == self._print_config:
+            arg_string += "="
+        return super()._parse_optional(arg_string)
 
     def _parse_common(
         self,
diff --git a/jsonargparse/_typehints.py b/jsonargparse/_typehints.py
index 1108946..f945b6d 100644
--- a/jsonargparse/_typehints.py
+++ b/jsonargparse/_typehints.py
@@ -160,6 +160,16 @@ allow_default_instance: ContextVar = ContextVar("allow_default_instance", defaul
 sub_defaults: ContextVar = ContextVar("sub_defaults", default=False)
 
 
+def get_parse_optional_num_return() -> int:
+    parser = __import__("argparse").ArgumentParser()
+    parser.add_argument("--test")
+    arg_parsed = parser._parse_optional("--test=x")
+    return len(arg_parsed)
+
+
+parse_optional_num_return = get_parse_optional_num_return()
+
+
 class ActionTypeHint(Action):
     """Action to parse a type hint."""
 
@@ -339,7 +349,7 @@ class ActionTypeHint(Action):
         return result
 
     @staticmethod
-    def parse_argv_item(arg_string, arg_parsed):
+    def parse_argv_item(arg_string):
         parser = subclass_arg_parser.get()
         action = None
         sep = None
@@ -352,7 +362,7 @@ class ActionTypeHint(Action):
 
         typehint = typehint_from_action(action)
         if typehint:
-            if len(arg_parsed) == 4:
+            if parse_optional_num_return == 4:
                 return action, arg_base, sep, explicit_arg
             return action, arg_base, explicit_arg
         return None


Test patch:
diff --git a/jsonargparse_tests/test_typehints.py b/jsonargparse_tests/test_typehints.py
index 2e05d84..f5d284e 100644
--- a/jsonargparse_tests/test_typehints.py
+++ b/jsonargparse_tests/test_typehints.py
@@ -486,6 +486,12 @@ def test_dict_command_line_set_items(parser):
     assert cfg.dict == {"one": 1, "two": 2}
 
 
+def test_dict_command_line_set_items_with_space(parser):
+    parser.add_argument("--dict", type=dict)
+    cfg = parser.parse_args(["--dict.a=x y"])
+    assert {"a": "x y"} == cfg.dict
+
+
 def test_mapping_nested_without_args(parser):
     parser.add_argument("--map", type=Mapping[str, Union[int, Mapping]])
     assert {"a": 1} == parser.parse_args(['--map={"a": 1}']).map