Repository: omni-us/jsonargparse
Pull request #720 (2025-05-08 06:02:27)

Deprecate namespace_to_dict
## Before submitting

- [x] Did you read the [contributing guideline](https://github.com/omni-us/jsonargparse/blob/main/CONTRIBUTING.rst)?
- [x] 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)


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

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/omni-us/jsonargparse/pull/720?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).

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



Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index f79dc9f..49cfc7c 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -27,8 +27,11 @@ Deprecated
 ^^^^^^^^^^
 - ``LoggerProperty`` is deprecated and will be removed in v5.0.0. There is no
   replacement since jsonargparse is not a logging library. A similar class can
-  be found in ``reconplogger`` (`#???
-  <https://github.com/omni-us/jsonargparse/pull/???>`__).
+  be found in ``reconplogger`` (`#719
+  <https://github.com/omni-us/jsonargparse/pull/719>`__).
+- ``namespace_to_dict`` is deprecated and will be removed in v5.0.0. Instead use
+  ``.clone().as_dict()`` or ``.as_dict()`` (`#720
+  <https://github.com/omni-us/jsonargparse/pull/720>`__).
 
 
 v4.39.0 (2025-04-29)
diff --git a/jsonargparse/_deprecated.py b/jsonargparse/_deprecated.py
index 463ea42..8e29a50 100644
--- a/jsonargparse/_deprecated.py
+++ b/jsonargparse/_deprecated.py
@@ -25,6 +25,7 @@ __all__ = [
     "LoggerProperty",
     "ParserError",
     "get_config_read_mode",
+    "namespace_to_dict",
     "null_logger",
     "set_docstring_parse_options",
     "set_config_read_mode",
@@ -689,3 +690,14 @@ class ActionJsonnetExtVars:
 )
 class LoggerProperty(InternalLoggerProperty):
     """Adds a logger property, intended for internal use."""
+
+
+@deprecated(
+    """
+    namespace_to_dict was deprecated in v4.40.0 and will be removed in v5.0.0.
+    Instead you can use ``.clone().as_dict()`` or ``.as_dict()``.
+"""
+)
+def namespace_to_dict(namespace: Namespace) -> Dict[str, Any]:
+    """Returns a copy of a nested namespace converted into a nested dictionary."""
+    return namespace.clone().as_dict()
diff --git a/jsonargparse/_namespace.py b/jsonargparse/_namespace.py
index eb94a2f..c0116fd 100644
--- a/jsonargparse/_namespace.py
+++ b/jsonargparse/_namespace.py
@@ -18,7 +18,6 @@ from typing import (
 
 __all__ = [
     "Namespace",
-    "namespace_to_dict",
     "dict_to_namespace",
     "strip_meta",
 ]
@@ -331,11 +330,6 @@ def del_clash_mark(key: str) -> str:
     return key
 
 
-def namespace_to_dict(namespace: Namespace) -> Dict[str, Any]:
-    """Returns a copy of a nested namespace converted into a nested dictionary."""
-    return namespace.clone().as_dict()
-
-
 def expand_dict(cfg):
     for k, v in cfg.items():
         if isinstance(v, dict) and all(isinstance(k, str) for k in v.keys()):
@@ -348,7 +342,13 @@ def expand_dict(cfg):
 
 
 def dict_to_namespace(cfg_dict: Union[Dict[str, Any], Namespace]) -> Namespace:
-    """Converts a nested dictionary into a nested namespace."""
+    """Converts a nested dictionary into a nested namespace.
+
+    Note: Using this function is generally discouraged because it may not
+          produce the same results as a parser would. However, it remains part
+          of the public API to support valid use cases and ensure backward
+          compatibility.
+    """
     cfg_dict = recreate_branches(cfg_dict)
     return expand_dict(cfg_dict)
 


Test patch:
diff --git a/jsonargparse_tests/test_deprecated.py b/jsonargparse_tests/test_deprecated.py
index 2c1ca0d..d6996f0 100644
--- a/jsonargparse_tests/test_deprecated.py
+++ b/jsonargparse_tests/test_deprecated.py
@@ -33,6 +33,7 @@ from jsonargparse._deprecated import (
     LoggerProperty,
     ParserError,
     deprecation_warning,
+    namespace_to_dict,
     shown_deprecation_warnings,
     usage_and_exit_error_handler,
 )
@@ -708,3 +709,20 @@ def test_add_dataclass_arguments(parser, subtests):
         help_str = get_parser_help(parser)
         if docstring_parser_support:
             assert "CustomA title:" in help_str
+
+
+def test_namespace_to_dict():
+    ns = Namespace()
+    ns["w"] = 1
+    ns["x.y"] = 2
+    ns["x.z"] = 3
+    with catch_warnings(record=True) as w:
+        dic1 = namespace_to_dict(ns)
+    dic2 = ns.as_dict()
+    assert dic1 == dic2
+    assert dic1 is not dic2
+    assert_deprecation_warn(
+        w,
+        message="namespace_to_dict was deprecated",
+        code="dic1 = namespace_to_dict(ns)",
+    )
diff --git a/jsonargparse_tests/test_namespace.py b/jsonargparse_tests/test_namespace.py
index b3ccc12..921e68f 100644
--- a/jsonargparse_tests/test_namespace.py
+++ b/jsonargparse_tests/test_namespace.py
@@ -5,7 +5,7 @@ import platform
 
 import pytest
 
-from jsonargparse import Namespace, dict_to_namespace, namespace_to_dict
+from jsonargparse import Namespace, dict_to_namespace
 from jsonargparse._namespace import meta_keys
 
 skip_if_no_setattr_insertion_order = pytest.mark.skipif(
@@ -217,17 +217,6 @@ def test_init_invalid():
         Namespace(argparse.Namespace(), x=1)
 
 
-def test_namespace_to_dict():
-    ns = Namespace()
-    ns["w"] = 1
-    ns["x.y"] = 2
-    ns["x.z"] = 3
-    dic1 = namespace_to_dict(ns)
-    dic2 = ns.as_dict()
-    assert dic1 == dic2
-    assert dic1 is not dic2
-
-
 def test_dict_to_namespace():
     ns1 = Namespace(a=1, b=Namespace(c=2), d=[Namespace(e=3)])
     dic = {"a": 1, "b": {"c": 2}, "d": [{"e": 3}]}
@@ -259,7 +248,7 @@ def test_shallow_clashing_keys():
     assert ns.as_flat() == argparse.Namespace(pop=2)
     del ns["pop"]
     assert ns == Namespace()
-    assert namespace_to_dict(Namespace(update=3)) == {"update": 3}
+    assert Namespace(update=3).as_dict() == {"update": 3}
 
 
 def test_leaf_clashing_keys():