nested-argparse 0.1.1__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Stephen Zhao
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,137 @@
1
+ Metadata-Version: 2.4
2
+ Name: nested-argparse
3
+ Version: 0.2.0
4
+ Summary: A python module that extends argparser to create nested namespace trees for subparsers.
5
+ Author-email: Stephen Zhao <mail@zhaostephen.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/stephen-zhao/nested_argparse
8
+ Project-URL: Source, https://github.com/stephen-zhao/nested_argparse
9
+ Keywords: argparse,nested,namespace,subparser,conflict,parser,cli,command,subcommand
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: System Administrators
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Natural Language :: English
20
+ Classifier: Operating System :: OS Independent
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Topic :: Utilities
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.10
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Dynamic: license-file
28
+
29
+ # nested-argparse 💬 → 🅰.🅱.🆒
30
+
31
+ [![PyPI](https://img.shields.io/pypi/v/nested-argparse?color=brightgreen&label=pypi%20package)](https://pypi.org/project/nested-argparse/)
32
+ ![PyPI - Status](https://img.shields.io/pypi/status/nested-argparse)
33
+ ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/nested-argparse)
34
+ [![PyPI - License](https://img.shields.io/pypi/l/nested-argparse)](https://github.com/stephen-zhao/nested_argparse/blob/main/LICENSE)
35
+
36
+ nested-argparse is a python module that non-invasively builds on top of the built-in `argparse` library to allow subparsers to parse into their own nested namespaces.
37
+
38
+ The library exposes a class `NestedArgumentParser` which allows arbitrary nesting without worry of namespace conflicts. This is achieved with the following principles of this library:
39
+
40
+ - **Inversion of Control:** A parser, when adding a subparser, is in control of what name to use for the sub-namespace which the subparser sends its parsed args to.
41
+ - **Drop-In Replacement:** The constructor for `nested_argparse.NestedArgumentParser` can be substituted in directly to where the constructor for `argparse.ArgumentParser` is being used. All subsequent method calls and subparser API calls should work without any additional code change!
42
+ - **Customizeability:** There are additional `kwargs` exposed to further customize the nesting options to your liking, if the defaults do not suit your scenario.
43
+
44
+ The main difference between this library and its built-in counterpart is the return value of the `parse_args` method. Instead of a flat namespace containing all parsed arguments across all subparsers, `NestedArgumentParser` will produce a namespace tree.
45
+
46
+ ## Simple Conceptual Example
47
+
48
+ Given the following parser:
49
+
50
+ ```
51
+ Root Parser
52
+ ├─ positional_1
53
+ ├─ --optional_1
54
+ ├─ --optional_2
55
+ └─ sub parsers with dest='subcommand'
56
+ ├─ Sub Parser 1 with name='sub1'
57
+ │ ├─ --optional_1
58
+ │ └─ --optional_2 with dest='optional2AltName'
59
+ └─ Sub Parser 2 with name='sub2'
60
+ ├─ --optional_1
61
+ └─ --optional_2
62
+ ```
63
+
64
+ And the following args to parse:
65
+
66
+ ```sh
67
+ Alice --optional_1=Bob sub1 --optional_1=Carol --optional_2=David
68
+ ```
69
+
70
+ The built-in `ArgumentParser` would not be able to handle the duplication in `dest`s, but `NestedArgumentParser` will produce the following result when run through `parse_args`:
71
+
72
+ ```py
73
+ Namespace(
74
+ subcommand='sub1',
75
+ positional_1='Alice',
76
+ optional_1='Bob',
77
+ sub1=Namespace(
78
+ optional_1='Carol',
79
+ optional2AltName='David'
80
+ )
81
+ )
82
+ ```
83
+
84
+ ## API Documentation
85
+
86
+ The library exposes the following modules.
87
+
88
+ ### Module `nested_argparse`
89
+
90
+ The module exports the following classes.
91
+
92
+ #### Class `NestedArgumentParser`
93
+
94
+ - extends `argparser.ArgumentParser`
95
+ - for documentation for the superclass, see the official [Python API reference docs for `argparse`](https://docs.python.org/3/library/argparse.html).
96
+
97
+ ##### Constructor
98
+
99
+ In addition to the parameters available to `ArgumentParser` constructor, the following parameters are also accepted:
100
+
101
+ - Param `nest_dir`, optional, type: `Optional[str]`
102
+ - When a string is passed in, it is used as the attribute name in the parent namespace to which the nested namespace, where the parsed values will be stored, is assigned to.
103
+ - When `None` is passed in, no nested namespace is created, and parsed values are directly assigned to the parent namespace. This is the behavior of the base `ArgumentParser`.
104
+ - Default value: `None`.
105
+
106
+ - Param `nest_separator`, optional, type: `str`
107
+ - It is used as the separator to delimit components in the nest path when representing the path as a string (for example, this is used to generate `dest`s)
108
+ - Default value: `'__'`.
109
+
110
+ - Param `nest_path`, optional, type: `Optional[List[str]]`
111
+ - When a list of strings is passed in, it is used as a sequence of nested attribute names from the parent namespace which locates the nested namespace where the parsed values will be stored.
112
+ - When `None` is passed in, no nested namespace is created, and parsed values are directly assigned to the parent namespace. This is the behavior of the base `ArgumentParser`.
113
+
114
+ ##### Override `NestedArgumentParser.add_argument`
115
+
116
+ Instead of adding an argument definition which stores the parsed value to `dest` in the flat top-level namespace, the parsed value will be stored at attribute with the name given by `dest` in the namesapce at the nesting path associated with this parser.
117
+
118
+ ##### Override `NestedArgumentParser.add_subparsers`
119
+
120
+ The return value of this method is an instance of internal subparser handler `_NestedSubParsersAction`, which exposes extra options for adding subparsers.
121
+
122
+ ##### Override `NestedArgumentParser.parse_args`
123
+
124
+ The return value of this method is a namespace tree rather than a flat namespace. The tree is built according to the nesting paths associated with each of the parsed values.
125
+
126
+ ##### Override `NestedArgumentParser.parse_known_args`
127
+
128
+ The return value of this method is a namespace tree rather than a flat namespace. The tree is built according to the nesting paths associated with each of the parsed values.
129
+
130
+ #### Class `_NestedSubParsersAction`
131
+
132
+ ##### Override `_NestedSubParsersAction.add_parser`
133
+
134
+ - Param `nest_dir`, optional, type: `Optional[str]`
135
+ - When a string is passed in, it is used as the attribute name in the parent namespace to which the subparser will store its parsed values to.
136
+ - When `None` is passed in, the `dest` field is used as the nesting directory instead.
137
+ - Default value: `None`.
@@ -0,0 +1,109 @@
1
+ # nested-argparse 💬 → 🅰.🅱.🆒
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/nested-argparse?color=brightgreen&label=pypi%20package)](https://pypi.org/project/nested-argparse/)
4
+ ![PyPI - Status](https://img.shields.io/pypi/status/nested-argparse)
5
+ ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/nested-argparse)
6
+ [![PyPI - License](https://img.shields.io/pypi/l/nested-argparse)](https://github.com/stephen-zhao/nested_argparse/blob/main/LICENSE)
7
+
8
+ nested-argparse is a python module that non-invasively builds on top of the built-in `argparse` library to allow subparsers to parse into their own nested namespaces.
9
+
10
+ The library exposes a class `NestedArgumentParser` which allows arbitrary nesting without worry of namespace conflicts. This is achieved with the following principles of this library:
11
+
12
+ - **Inversion of Control:** A parser, when adding a subparser, is in control of what name to use for the sub-namespace which the subparser sends its parsed args to.
13
+ - **Drop-In Replacement:** The constructor for `nested_argparse.NestedArgumentParser` can be substituted in directly to where the constructor for `argparse.ArgumentParser` is being used. All subsequent method calls and subparser API calls should work without any additional code change!
14
+ - **Customizeability:** There are additional `kwargs` exposed to further customize the nesting options to your liking, if the defaults do not suit your scenario.
15
+
16
+ The main difference between this library and its built-in counterpart is the return value of the `parse_args` method. Instead of a flat namespace containing all parsed arguments across all subparsers, `NestedArgumentParser` will produce a namespace tree.
17
+
18
+ ## Simple Conceptual Example
19
+
20
+ Given the following parser:
21
+
22
+ ```
23
+ Root Parser
24
+ ├─ positional_1
25
+ ├─ --optional_1
26
+ ├─ --optional_2
27
+ └─ sub parsers with dest='subcommand'
28
+ ├─ Sub Parser 1 with name='sub1'
29
+ │ ├─ --optional_1
30
+ │ └─ --optional_2 with dest='optional2AltName'
31
+ └─ Sub Parser 2 with name='sub2'
32
+ ├─ --optional_1
33
+ └─ --optional_2
34
+ ```
35
+
36
+ And the following args to parse:
37
+
38
+ ```sh
39
+ Alice --optional_1=Bob sub1 --optional_1=Carol --optional_2=David
40
+ ```
41
+
42
+ The built-in `ArgumentParser` would not be able to handle the duplication in `dest`s, but `NestedArgumentParser` will produce the following result when run through `parse_args`:
43
+
44
+ ```py
45
+ Namespace(
46
+ subcommand='sub1',
47
+ positional_1='Alice',
48
+ optional_1='Bob',
49
+ sub1=Namespace(
50
+ optional_1='Carol',
51
+ optional2AltName='David'
52
+ )
53
+ )
54
+ ```
55
+
56
+ ## API Documentation
57
+
58
+ The library exposes the following modules.
59
+
60
+ ### Module `nested_argparse`
61
+
62
+ The module exports the following classes.
63
+
64
+ #### Class `NestedArgumentParser`
65
+
66
+ - extends `argparser.ArgumentParser`
67
+ - for documentation for the superclass, see the official [Python API reference docs for `argparse`](https://docs.python.org/3/library/argparse.html).
68
+
69
+ ##### Constructor
70
+
71
+ In addition to the parameters available to `ArgumentParser` constructor, the following parameters are also accepted:
72
+
73
+ - Param `nest_dir`, optional, type: `Optional[str]`
74
+ - When a string is passed in, it is used as the attribute name in the parent namespace to which the nested namespace, where the parsed values will be stored, is assigned to.
75
+ - When `None` is passed in, no nested namespace is created, and parsed values are directly assigned to the parent namespace. This is the behavior of the base `ArgumentParser`.
76
+ - Default value: `None`.
77
+
78
+ - Param `nest_separator`, optional, type: `str`
79
+ - It is used as the separator to delimit components in the nest path when representing the path as a string (for example, this is used to generate `dest`s)
80
+ - Default value: `'__'`.
81
+
82
+ - Param `nest_path`, optional, type: `Optional[List[str]]`
83
+ - When a list of strings is passed in, it is used as a sequence of nested attribute names from the parent namespace which locates the nested namespace where the parsed values will be stored.
84
+ - When `None` is passed in, no nested namespace is created, and parsed values are directly assigned to the parent namespace. This is the behavior of the base `ArgumentParser`.
85
+
86
+ ##### Override `NestedArgumentParser.add_argument`
87
+
88
+ Instead of adding an argument definition which stores the parsed value to `dest` in the flat top-level namespace, the parsed value will be stored at attribute with the name given by `dest` in the namesapce at the nesting path associated with this parser.
89
+
90
+ ##### Override `NestedArgumentParser.add_subparsers`
91
+
92
+ The return value of this method is an instance of internal subparser handler `_NestedSubParsersAction`, which exposes extra options for adding subparsers.
93
+
94
+ ##### Override `NestedArgumentParser.parse_args`
95
+
96
+ The return value of this method is a namespace tree rather than a flat namespace. The tree is built according to the nesting paths associated with each of the parsed values.
97
+
98
+ ##### Override `NestedArgumentParser.parse_known_args`
99
+
100
+ The return value of this method is a namespace tree rather than a flat namespace. The tree is built according to the nesting paths associated with each of the parsed values.
101
+
102
+ #### Class `_NestedSubParsersAction`
103
+
104
+ ##### Override `_NestedSubParsersAction.add_parser`
105
+
106
+ - Param `nest_dir`, optional, type: `Optional[str]`
107
+ - When a string is passed in, it is used as the attribute name in the parent namespace to which the subparser will store its parsed values to.
108
+ - When `None` is passed in, the `dest` field is used as the nesting directory instead.
109
+ - Default value: `None`.
@@ -0,0 +1,87 @@
1
+ [project]
2
+ name = "nested-argparse"
3
+ version = "0.2.0"
4
+ description = "A python module that extends argparser to create nested namespace trees for subparsers."
5
+ readme = { file = "README.md", content-type = "text/markdown" }
6
+ requires-python = ">=3.10"
7
+ dependencies = [
8
+ ]
9
+ classifiers = [
10
+ "Development Status :: 3 - Alpha",
11
+ "Intended Audience :: Developers",
12
+ "Intended Audience :: System Administrators",
13
+ "Programming Language :: Python :: 3",
14
+ "Programming Language :: Python :: 3.10",
15
+ "Programming Language :: Python :: 3.11",
16
+ "Programming Language :: Python :: 3.12",
17
+ "Programming Language :: Python :: 3.13",
18
+ "Programming Language :: Python :: 3.14",
19
+ "Natural Language :: English",
20
+ "Operating System :: OS Independent",
21
+ "Topic :: Software Development :: Libraries :: Python Modules",
22
+ "Topic :: Utilities",
23
+ "Typing :: Typed",
24
+ ]
25
+ license = "MIT"
26
+ license-files = ["LICENSE"]
27
+ authors = [
28
+ { name = "Stephen Zhao", email = "mail@zhaostephen.com" },
29
+ ]
30
+ keywords = [
31
+ "argparse",
32
+ "nested",
33
+ "namespace",
34
+ "subparser",
35
+ "conflict",
36
+ "parser",
37
+ "cli",
38
+ "command",
39
+ "subcommand",
40
+ ]
41
+
42
+ [project.urls]
43
+ Homepage = "https://github.com/stephen-zhao/nested_argparse"
44
+ Source = "https://github.com/stephen-zhao/nested_argparse"
45
+
46
+ [build-system]
47
+ requires = ["setuptools >= 77.0.3"]
48
+ build-backend = "setuptools.build_meta"
49
+
50
+ [tool.setuptools.package-dir]
51
+ "" = "src"
52
+
53
+ [tool.setuptools.packages.find]
54
+ where = ["src"]
55
+ include = ["nested_argparse"]
56
+
57
+ [tool.setuptools.package-data]
58
+ nested_argparse = ["py.typed"]
59
+
60
+ [tool.uv]
61
+ dev-dependencies = [
62
+ "bump-my-version>=1.2.1",
63
+ "pytest>=8.4.1",
64
+ "uv>=0.8.11",
65
+ ]
66
+
67
+ [tool.bumpversion]
68
+ current_version = "0.2.0"
69
+ parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
70
+ serialize = ["{major}.{minor}.{patch}"]
71
+ search = "{current_version}"
72
+ replace = "{new_version}"
73
+ regex = false
74
+ ignore_missing_version = false
75
+ ignore_missing_files = false
76
+ tag = true
77
+ sign_tags = false
78
+ tag_name = "v{new_version}"
79
+ tag_message = "Bump version: {current_version} → {new_version}"
80
+ allow_dirty = false
81
+ commit = true
82
+ message = "Bump version: {current_version} → {new_version}"
83
+ moveable_tags = []
84
+ commit_args = ""
85
+ setup_hooks = []
86
+ pre_commit_hooks = []
87
+ post_commit_hooks = []
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -1,5 +1,11 @@
1
1
  import argparse
2
- from typing import Any, Dict, List, Optional, Tuple
2
+ from typing import Any
3
+
4
+ DEBUG = False
5
+
6
+ def _debug_log(*args):
7
+ if DEBUG:
8
+ print(*args)
3
9
 
4
10
  class NestedArgumentParser(argparse.ArgumentParser):
5
11
  def __init__(self,
@@ -29,26 +35,25 @@ class NestedArgumentParser(argparse.ArgumentParser):
29
35
  nest_components = nest_path
30
36
 
31
37
  # Save the nest path and related config
32
- self.nest_dir: Optional[str] = nest_components[-1] if len(nest_components) > 0 else None
33
- self.nest_path_components: List[str] = nest_components
38
+ self.nest_dir: str | None = nest_components[-1] if len(nest_components) > 0 else None
39
+ self.nest_path_components: list[str] = nest_components
34
40
  self.nest_separator: str = nest_separator
35
41
 
36
42
  # Mapping from nested dest back to the original dest for contained Actions
37
- self._original_dest_by_nested_dest = {}
38
-
39
- superinit = super(NestedArgumentParser, self).__init__
40
- superinit(prog=prog,
41
- usage=usage,
42
- description=description,
43
- epilog=epilog,
44
- parents=parents,
45
- formatter_class=formatter_class,
46
- prefix_chars=prefix_chars,
47
- fromfile_prefix_chars=fromfile_prefix_chars,
48
- argument_default=argument_default,
49
- conflict_handler=conflict_handler,
50
- add_help=add_help,
51
- allow_abbrev=allow_abbrev)
43
+ self._original_dest_by_nested_dest: dict[str, str] = {}
44
+
45
+ super().__init__(prog=prog,
46
+ usage=usage,
47
+ description=description,
48
+ epilog=epilog,
49
+ parents=parents,
50
+ formatter_class=formatter_class,
51
+ prefix_chars=prefix_chars,
52
+ fromfile_prefix_chars=fromfile_prefix_chars,
53
+ argument_default=argument_default,
54
+ conflict_handler=conflict_handler,
55
+ add_help=add_help,
56
+ allow_abbrev=allow_abbrev)
52
57
 
53
58
  # Override the subparsers action to use the Nested edition
54
59
  self.register('action', 'parsers', _NestedSubParsersAction)
@@ -78,9 +83,12 @@ class NestedArgumentParser(argparse.ArgumentParser):
78
83
  # Command line argument parsing methods
79
84
  # =====================================
80
85
 
81
- def parse_known_args(self, args=None, namespace=None) -> Tuple[argparse.Namespace, List[str]]:
86
+ def parse_known_args(self, args=None, namespace=None) -> tuple[argparse.Namespace, list[str]]:
87
+ _debug_log('IN override parse_known_args given args=', args, '; namespace=', namespace)
82
88
  parsed_args, unknown_args = super().parse_known_args(args=args, namespace=namespace)
83
- return self._deflatten_namespace(parsed_args), unknown_args
89
+ deflattened_args = self._deflatten_namespace(parsed_args)
90
+ _debug_log('OUT override parse_known_args deflattened_args=', deflattened_args, '; unknown_args=', unknown_args)
91
+ return deflattened_args, unknown_args
84
92
 
85
93
  # ==================================
86
94
  # Namespace default accessor methods
@@ -102,9 +110,11 @@ class NestedArgumentParser(argparse.ArgumentParser):
102
110
  # ==================================
103
111
 
104
112
  def _deflatten_namespace(self, namespace: argparse.Namespace) -> argparse.Namespace:
113
+ _debug_log('deflattening..........')
105
114
  root_namespace = argparse.Namespace()
106
115
  # Loop through all attributes in the original namespace
107
116
  for key, value in vars(namespace).items():
117
+ _debug_log('key=', key, '; value=', value)
108
118
  components = key.split(self.nest_separator)
109
119
  # Start at the root namespace
110
120
  curr_namespace = root_namespace
@@ -126,40 +136,56 @@ class NestedArgumentParser(argparse.ArgumentParser):
126
136
  raise ValueError(f'Cannot merge namespaces due to conflict at key "{key}".')
127
137
  else:
128
138
  setattr(curr_namespace, components[-1], value)
139
+ _debug_log(root_namespace)
129
140
  return root_namespace
130
141
 
131
142
  def _recursively_merge_namespaces(self, dest_namespace: argparse.Namespace, src_namespace: argparse.Namespace) -> argparse.Namespace:
143
+ _debug_log('both are namespaces, so recursively merge:')
144
+ _debug_log(' merge destination:', dest_namespace)
145
+ _debug_log(' merge source:', src_namespace)
132
146
  for attr, src_value in vars(src_namespace).items():
133
147
  # Check if destination has attribute with same name
134
148
  if hasattr(dest_namespace, attr):
135
149
  dest_value = getattr(dest_namespace, attr)
136
150
  # Check if both are namespaces, in which case we can recursively merge
137
151
  if isinstance(dest_value, argparse.Namespace) and isinstance(src_value, argparse.Namespace):
152
+ _debug_log(' --> both', attr, 'are namespaces, so setting', attr, 'by merging')
138
153
  setattr(dest_namespace, attr, self._recursively_merge_namespaces(dest_value, src_value))
139
154
  else:
140
155
  raise ValueError(f'Cannot merge namespaces due to conflict at attribute "{attr}".')
141
156
  else:
157
+ _debug_log(' --> no', attr, 'yet, so setting', attr, '=', src_value)
142
158
  setattr(dest_namespace, attr, src_value)
159
+ return dest_namespace
143
160
 
144
161
  def _add_container_actions(self, container: argparse._ActionsContainer) -> None:
145
- if isinstance(container, NestedArgumentParser):
146
- for action in container._actions:
147
- if action.dest is not None and action.dest in container._original_dest_by_nested_dest:
148
- original_dest = container._original_dest_by_nested_dest[action.dest]
149
- action.dest = self._get_nested_dest_and_save_original(original_dest)
150
- for group in container._action_groups:
151
- for action in group._group_actions:
152
- if action.dest is not None and action.dest in container._original_dest_by_nested_dest:
153
- original_dest = container._original_dest_by_nested_dest[action.dest]
154
- action.dest = self._get_nested_dest_and_save_original(original_dest)
155
- for mutex_group in container._mutually_exclusive_groups:
156
- for action in mutex_group._group_actions:
157
- if action.dest is not None and action.dest in container._original_dest_by_nested_dest:
158
- original_dest = container._original_dest_by_nested_dest[action.dest]
159
- action.dest = self._get_nested_dest_and_save_original(original_dest)
162
+ self._remap_container_dests(container)
160
163
  return super()._add_container_actions(container)
161
164
 
162
- def _get_positional_kwargs(self, dest: str, **kwargs: Any) -> Dict[str, Any]:
165
+ def _remap_container_dests(self, container: argparse._ActionsContainer) -> None:
166
+ _debug_log('remapping container', container)
167
+ original_defaults = container._defaults
168
+ nested_defaults = { self._get_nested_dest(dest): value for dest, value in original_defaults.items() }
169
+ _debug_log(' remapping defaults', original_defaults)
170
+ _debug_log(' ->', nested_defaults)
171
+ container._defaults = nested_defaults
172
+ for action in container._actions:
173
+ self._remap_action_dest(action)
174
+
175
+ def _remap_action_dest(self, action: argparse.Action) -> None:
176
+ _debug_log(' remapping action', action)
177
+ if action.dest is not None:
178
+ original_dest = action.dest
179
+ nested_dest = self._get_nested_dest_and_save_original(original_dest)
180
+ _debug_log(' ...', original_dest)
181
+ _debug_log(' ->', nested_dest)
182
+ action.dest = nested_dest
183
+ if isinstance(action, _NestedSubParsersAction) and action.choices is not None:
184
+ for _, subparser in action.choices.items():
185
+ if isinstance(subparser, NestedArgumentParser):
186
+ self._remap_container_dests(subparser)
187
+
188
+ def _get_positional_kwargs(self, dest: str, **kwargs: Any) -> dict[str, Any]:
163
189
  # Get the nested dest
164
190
  nested_dest = self._get_nested_dest_and_save_original(dest.replace('-', '_'))
165
191
 
@@ -168,7 +194,7 @@ class NestedArgumentParser(argparse.ArgumentParser):
168
194
 
169
195
  return super()._get_positional_kwargs(nested_dest, **kwargs)
170
196
 
171
- def _get_optional_kwargs(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
197
+ def _get_optional_kwargs(self, *args: Any, **kwargs: Any) -> dict[str, Any]:
172
198
  # Extract dest from args and kwargs
173
199
  dest = self._extract_dest(*args, **kwargs)
174
200
 
@@ -239,14 +265,13 @@ class _NestedSubParsersAction(argparse._SubParsersAction):
239
265
  required=False,
240
266
  help=None,
241
267
  metavar=None) -> None:
242
- superinit = super(_NestedSubParsersAction, self).__init__
243
- superinit(option_strings,
244
- prog,
245
- parser_class,
246
- dest=dest,
247
- required=required,
248
- help=help,
249
- metavar=metavar)
268
+ super().__init__(option_strings,
269
+ prog,
270
+ parser_class,
271
+ dest=dest,
272
+ required=required,
273
+ help=help,
274
+ metavar=metavar)
250
275
 
251
276
  self.base_nest_path_components = base_nest_path
252
277
  self.nest_separator = nest_separator
File without changes
@@ -0,0 +1,137 @@
1
+ Metadata-Version: 2.4
2
+ Name: nested-argparse
3
+ Version: 0.2.0
4
+ Summary: A python module that extends argparser to create nested namespace trees for subparsers.
5
+ Author-email: Stephen Zhao <mail@zhaostephen.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/stephen-zhao/nested_argparse
8
+ Project-URL: Source, https://github.com/stephen-zhao/nested_argparse
9
+ Keywords: argparse,nested,namespace,subparser,conflict,parser,cli,command,subcommand
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: System Administrators
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Natural Language :: English
20
+ Classifier: Operating System :: OS Independent
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Topic :: Utilities
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.10
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Dynamic: license-file
28
+
29
+ # nested-argparse 💬 → 🅰.🅱.🆒
30
+
31
+ [![PyPI](https://img.shields.io/pypi/v/nested-argparse?color=brightgreen&label=pypi%20package)](https://pypi.org/project/nested-argparse/)
32
+ ![PyPI - Status](https://img.shields.io/pypi/status/nested-argparse)
33
+ ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/nested-argparse)
34
+ [![PyPI - License](https://img.shields.io/pypi/l/nested-argparse)](https://github.com/stephen-zhao/nested_argparse/blob/main/LICENSE)
35
+
36
+ nested-argparse is a python module that non-invasively builds on top of the built-in `argparse` library to allow subparsers to parse into their own nested namespaces.
37
+
38
+ The library exposes a class `NestedArgumentParser` which allows arbitrary nesting without worry of namespace conflicts. This is achieved with the following principles of this library:
39
+
40
+ - **Inversion of Control:** A parser, when adding a subparser, is in control of what name to use for the sub-namespace which the subparser sends its parsed args to.
41
+ - **Drop-In Replacement:** The constructor for `nested_argparse.NestedArgumentParser` can be substituted in directly to where the constructor for `argparse.ArgumentParser` is being used. All subsequent method calls and subparser API calls should work without any additional code change!
42
+ - **Customizeability:** There are additional `kwargs` exposed to further customize the nesting options to your liking, if the defaults do not suit your scenario.
43
+
44
+ The main difference between this library and its built-in counterpart is the return value of the `parse_args` method. Instead of a flat namespace containing all parsed arguments across all subparsers, `NestedArgumentParser` will produce a namespace tree.
45
+
46
+ ## Simple Conceptual Example
47
+
48
+ Given the following parser:
49
+
50
+ ```
51
+ Root Parser
52
+ ├─ positional_1
53
+ ├─ --optional_1
54
+ ├─ --optional_2
55
+ └─ sub parsers with dest='subcommand'
56
+ ├─ Sub Parser 1 with name='sub1'
57
+ │ ├─ --optional_1
58
+ │ └─ --optional_2 with dest='optional2AltName'
59
+ └─ Sub Parser 2 with name='sub2'
60
+ ├─ --optional_1
61
+ └─ --optional_2
62
+ ```
63
+
64
+ And the following args to parse:
65
+
66
+ ```sh
67
+ Alice --optional_1=Bob sub1 --optional_1=Carol --optional_2=David
68
+ ```
69
+
70
+ The built-in `ArgumentParser` would not be able to handle the duplication in `dest`s, but `NestedArgumentParser` will produce the following result when run through `parse_args`:
71
+
72
+ ```py
73
+ Namespace(
74
+ subcommand='sub1',
75
+ positional_1='Alice',
76
+ optional_1='Bob',
77
+ sub1=Namespace(
78
+ optional_1='Carol',
79
+ optional2AltName='David'
80
+ )
81
+ )
82
+ ```
83
+
84
+ ## API Documentation
85
+
86
+ The library exposes the following modules.
87
+
88
+ ### Module `nested_argparse`
89
+
90
+ The module exports the following classes.
91
+
92
+ #### Class `NestedArgumentParser`
93
+
94
+ - extends `argparser.ArgumentParser`
95
+ - for documentation for the superclass, see the official [Python API reference docs for `argparse`](https://docs.python.org/3/library/argparse.html).
96
+
97
+ ##### Constructor
98
+
99
+ In addition to the parameters available to `ArgumentParser` constructor, the following parameters are also accepted:
100
+
101
+ - Param `nest_dir`, optional, type: `Optional[str]`
102
+ - When a string is passed in, it is used as the attribute name in the parent namespace to which the nested namespace, where the parsed values will be stored, is assigned to.
103
+ - When `None` is passed in, no nested namespace is created, and parsed values are directly assigned to the parent namespace. This is the behavior of the base `ArgumentParser`.
104
+ - Default value: `None`.
105
+
106
+ - Param `nest_separator`, optional, type: `str`
107
+ - It is used as the separator to delimit components in the nest path when representing the path as a string (for example, this is used to generate `dest`s)
108
+ - Default value: `'__'`.
109
+
110
+ - Param `nest_path`, optional, type: `Optional[List[str]]`
111
+ - When a list of strings is passed in, it is used as a sequence of nested attribute names from the parent namespace which locates the nested namespace where the parsed values will be stored.
112
+ - When `None` is passed in, no nested namespace is created, and parsed values are directly assigned to the parent namespace. This is the behavior of the base `ArgumentParser`.
113
+
114
+ ##### Override `NestedArgumentParser.add_argument`
115
+
116
+ Instead of adding an argument definition which stores the parsed value to `dest` in the flat top-level namespace, the parsed value will be stored at attribute with the name given by `dest` in the namesapce at the nesting path associated with this parser.
117
+
118
+ ##### Override `NestedArgumentParser.add_subparsers`
119
+
120
+ The return value of this method is an instance of internal subparser handler `_NestedSubParsersAction`, which exposes extra options for adding subparsers.
121
+
122
+ ##### Override `NestedArgumentParser.parse_args`
123
+
124
+ The return value of this method is a namespace tree rather than a flat namespace. The tree is built according to the nesting paths associated with each of the parsed values.
125
+
126
+ ##### Override `NestedArgumentParser.parse_known_args`
127
+
128
+ The return value of this method is a namespace tree rather than a flat namespace. The tree is built according to the nesting paths associated with each of the parsed values.
129
+
130
+ #### Class `_NestedSubParsersAction`
131
+
132
+ ##### Override `_NestedSubParsersAction.add_parser`
133
+
134
+ - Param `nest_dir`, optional, type: `Optional[str]`
135
+ - When a string is passed in, it is used as the attribute name in the parent namespace to which the subparser will store its parsed values to.
136
+ - When `None` is passed in, the `dest` field is used as the nesting directory instead.
137
+ - Default value: `None`.
@@ -1,10 +1,12 @@
1
+ LICENSE
1
2
  README.md
2
- setup.cfg
3
- setup.py
3
+ pyproject.toml
4
4
  src/nested_argparse/__init__.py
5
5
  src/nested_argparse/nested_argparse.py
6
+ src/nested_argparse/py.typed
6
7
  src/nested_argparse.egg-info/PKG-INFO
7
8
  src/nested_argparse.egg-info/SOURCES.txt
8
9
  src/nested_argparse.egg-info/dependency_links.txt
9
10
  src/nested_argparse.egg-info/top_level.txt
11
+ test/test_multinesting.py
10
12
  test/test_sanity_check.py
@@ -0,0 +1,41 @@
1
+ from nested_argparse import NestedArgumentParser
2
+
3
+ def test_multinesting():
4
+ subparser_3 = NestedArgumentParser()
5
+ subparser_3.add_argument('--level', type=int, default=3)
6
+ subparser_3.add_argument('--some_flag')
7
+ subparser_2 = NestedArgumentParser()
8
+ subparser_2.add_argument('--level', type=int, default=2)
9
+ subparser_2.add_argument('--some_flag')
10
+ subparser_2.add_subparsers(dest='sub').add_parser('sub_3', parents=[subparser_3], add_help=False)
11
+ subparser_1 = NestedArgumentParser()
12
+ subparser_1.add_argument('--level', type=int, default=1)
13
+ subparser_1.add_argument('--some_flag')
14
+ subparser_1.add_subparsers(dest='sub').add_parser('sub_2', parents=[subparser_2], add_help=False)
15
+ main_parser = NestedArgumentParser()
16
+ main_parser.add_argument('--level', type=int, default=0)
17
+ main_parser.add_argument('--some_flag')
18
+ main_parser.add_subparsers(dest='sub').add_parser('sub_1', parents=[subparser_1], add_help=False)
19
+
20
+
21
+ # Run the parser on test argv
22
+ test_argv = ['--some_flag=A', 'sub_1', '--some_flag=B', 'sub_2', '--some_flag=C', 'sub_3', '--some_flag=D']
23
+ args = main_parser.parse_args(test_argv)
24
+
25
+ assert 'some_flag' in args and args.some_flag == 'A'
26
+ assert 'level' in args and args.level == 0
27
+ assert 'sub' in args and args.sub == 'sub_1'
28
+ assert 'sub_1' in args
29
+
30
+ assert 'some_flag' in args.sub_1 and args.sub_1.some_flag == 'B'
31
+ assert 'level' in args.sub_1 and args.sub_1.level == 1
32
+ assert 'sub' in args.sub_1 and args.sub_1.sub == 'sub_2'
33
+ assert 'sub_2' in args.sub_1
34
+
35
+ assert 'some_flag' in args.sub_1.sub_2 and args.sub_1.sub_2.some_flag == 'C'
36
+ assert 'level' in args.sub_1.sub_2 and args.sub_1.sub_2.level == 2
37
+ assert 'sub' in args.sub_1.sub_2 and args.sub_1.sub_2.sub == 'sub_3'
38
+ assert 'sub_3' in args.sub_1.sub_2
39
+
40
+ assert 'some_flag' in args.sub_1.sub_2.sub_3 and args.sub_1.sub_2.sub_3.some_flag == 'D'
41
+ assert 'level' in args.sub_1.sub_2.sub_3 and args.sub_1.sub_2.sub_3.level == 3
@@ -1,4 +1,4 @@
1
- from src.nested_argparse import NestedArgumentParser
1
+ from nested_argparse import NestedArgumentParser
2
2
 
3
3
  def test_sanity_check():
4
4
  # Create a subparser as a standalone parser first
@@ -1,75 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: nested_argparse
3
- Version: 0.1.1
4
- Summary: A python module that extends argparser to create nested namespace trees for subparsers.
5
- Home-page: UNKNOWN
6
- Author: Stephen Zhao
7
- Author-email: mail@zhaostephen.com
8
- License: MIT License
9
- Description: # nested-argparse 💬 → 🅰.🅱.🆒
10
-
11
- [![PyPI](https://img.shields.io/pypi/v/nested-argparse?color=brightgreen&label=pypi%20package)](https://pypi.org/project/nested-argparse/)
12
- ![PyPI - Status](https://img.shields.io/pypi/status/nested-argparse)
13
- ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/nested-argparse)
14
- [![PyPI - License](https://img.shields.io/pypi/l/nested-argparse)](https://github.com/stephen-zhao/nested_argparse/blob/main/LICENSE)
15
-
16
- nested-argparse is a python module that non-invasively builds on top of the built-in `argparse` library to allow subparsers to parse into their own nested namespaces.
17
-
18
- The library exposes a class `NestedArgumentParser` which allows arbitrary nesting without worry of namespace conflicts. This is achieved with the following principles of this library:
19
-
20
- - **Inversion of Control:** A parser, when adding a subparser, is in control of what name to use for the sub-namespace which the subparser sends its parsed args to.
21
- - **Drop-In Replacement:** The constructor for `nested_argparse.NestedArgumentParser` can be substituted in directly to where the constructor for `argparse.ArgumentParser` is being used. All subsequent method calls and subparser API calls should work without any additional code change!
22
- - **Customizeability:** There are additional `kwargs` exposed to further customize the nesting options to your liking, if the defaults do not suit your scenario.
23
-
24
- The main difference between this library and its built-in counterpart is the return value of the `parse_args` method. Instead of a flat namespace containing all parsed arguments across all subparsers, `NestedArgumentParser` will produce a namespace tree.
25
-
26
- ## Quick Example
27
-
28
- Given the following parser:
29
-
30
- ```
31
- Root Parser with prog='root'
32
- ├─ positional_1
33
- ├─ --optional_1
34
- ├─ --optional_2
35
- └─ sub parsers with dest='subcommand'
36
- ├─ Sub Parser 1 with name='sub1'
37
- │ ├─ --optional_1
38
- │ └─ --optional_2 with dest='optional2AltName'
39
- └─ Sub Parser 2 with name='sub2'
40
- ├─ --optional_1
41
- └─ --optional_2
42
- ```
43
-
44
- And the following args to parse:
45
-
46
- ```sh
47
- Alice --optional_1=Bob sub1 --optional_1=Carol --optional_2=David
48
- ```
49
-
50
- The built-in `ArgumentParser` would not be able to handle the duplication in `dest`s, but `NestedArgumentParser` will produce the following result when run through `parse_args`:
51
-
52
- ```py
53
- Namespace(
54
- root=Namespace(
55
- subcommand='sub1',
56
- positional_1='Alice',
57
- optional_1='Bob',
58
- sub1=Namespace(
59
- optional_1='Carol',
60
- optional2AltName='David'
61
- )
62
- )
63
- )
64
- ```
65
- Keywords: argparse,nested,namespace,subparser,conflict,parser,cli,command,subcommand
66
- Platform: UNKNOWN
67
- Classifier: Development Status :: 2 - Pre-Alpha
68
- Classifier: Intended Audience :: Developers
69
- Classifier: Programming Language :: Python :: 3
70
- Classifier: License :: OSI Approved :: MIT License
71
- Classifier: Operating System :: OS Independent
72
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
73
- Classifier: Topic :: Utilities
74
- Requires-Python: >=3.6
75
- Description-Content-Type: text/markdown
@@ -1,56 +0,0 @@
1
- # nested-argparse 💬 → 🅰.🅱.🆒
2
-
3
- [![PyPI](https://img.shields.io/pypi/v/nested-argparse?color=brightgreen&label=pypi%20package)](https://pypi.org/project/nested-argparse/)
4
- ![PyPI - Status](https://img.shields.io/pypi/status/nested-argparse)
5
- ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/nested-argparse)
6
- [![PyPI - License](https://img.shields.io/pypi/l/nested-argparse)](https://github.com/stephen-zhao/nested_argparse/blob/main/LICENSE)
7
-
8
- nested-argparse is a python module that non-invasively builds on top of the built-in `argparse` library to allow subparsers to parse into their own nested namespaces.
9
-
10
- The library exposes a class `NestedArgumentParser` which allows arbitrary nesting without worry of namespace conflicts. This is achieved with the following principles of this library:
11
-
12
- - **Inversion of Control:** A parser, when adding a subparser, is in control of what name to use for the sub-namespace which the subparser sends its parsed args to.
13
- - **Drop-In Replacement:** The constructor for `nested_argparse.NestedArgumentParser` can be substituted in directly to where the constructor for `argparse.ArgumentParser` is being used. All subsequent method calls and subparser API calls should work without any additional code change!
14
- - **Customizeability:** There are additional `kwargs` exposed to further customize the nesting options to your liking, if the defaults do not suit your scenario.
15
-
16
- The main difference between this library and its built-in counterpart is the return value of the `parse_args` method. Instead of a flat namespace containing all parsed arguments across all subparsers, `NestedArgumentParser` will produce a namespace tree.
17
-
18
- ## Quick Example
19
-
20
- Given the following parser:
21
-
22
- ```
23
- Root Parser with prog='root'
24
- ├─ positional_1
25
- ├─ --optional_1
26
- ├─ --optional_2
27
- └─ sub parsers with dest='subcommand'
28
- ├─ Sub Parser 1 with name='sub1'
29
- │ ├─ --optional_1
30
- │ └─ --optional_2 with dest='optional2AltName'
31
- └─ Sub Parser 2 with name='sub2'
32
- ├─ --optional_1
33
- └─ --optional_2
34
- ```
35
-
36
- And the following args to parse:
37
-
38
- ```sh
39
- Alice --optional_1=Bob sub1 --optional_1=Carol --optional_2=David
40
- ```
41
-
42
- The built-in `ArgumentParser` would not be able to handle the duplication in `dest`s, but `NestedArgumentParser` will produce the following result when run through `parse_args`:
43
-
44
- ```py
45
- Namespace(
46
- root=Namespace(
47
- subcommand='sub1',
48
- positional_1='Alice',
49
- optional_1='Bob',
50
- sub1=Namespace(
51
- optional_1='Carol',
52
- optional2AltName='David'
53
- )
54
- )
55
- )
56
- ```
@@ -1,41 +0,0 @@
1
- [metadata]
2
- name = nested_argparse
3
- version = 0.1.1
4
- author = Stephen Zhao
5
- author_email = mail@zhaostephen.com
6
- description = A python module that extends argparser to create nested namespace trees for subparsers.
7
- long_description = file: README.md
8
- long_description_content_type = text/markdown
9
- license = MIT License
10
- keywords =
11
- argparse
12
- nested
13
- namespace
14
- subparser
15
- conflict
16
- parser
17
- cli
18
- command
19
- subcommand
20
- classifiers =
21
- Development Status :: 2 - Pre-Alpha
22
- Intended Audience :: Developers
23
- Programming Language :: Python :: 3
24
- License :: OSI Approved :: MIT License
25
- Operating System :: OS Independent
26
- Topic :: Software Development :: Libraries :: Python Modules
27
- Topic :: Utilities
28
-
29
- [options]
30
- packages = find:
31
- package_dir =
32
- =src
33
- python_requires = >=3.6
34
-
35
- [options.packages.find]
36
- where = src
37
-
38
- [egg_info]
39
- tag_build =
40
- tag_date = 0
41
-
@@ -1,4 +0,0 @@
1
- from setuptools import setup
2
-
3
- if __name__ == '__main__':
4
- setup()
@@ -1,75 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: nested-argparse
3
- Version: 0.1.1
4
- Summary: A python module that extends argparser to create nested namespace trees for subparsers.
5
- Home-page: UNKNOWN
6
- Author: Stephen Zhao
7
- Author-email: mail@zhaostephen.com
8
- License: MIT License
9
- Description: # nested-argparse 💬 → 🅰.🅱.🆒
10
-
11
- [![PyPI](https://img.shields.io/pypi/v/nested-argparse?color=brightgreen&label=pypi%20package)](https://pypi.org/project/nested-argparse/)
12
- ![PyPI - Status](https://img.shields.io/pypi/status/nested-argparse)
13
- ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/nested-argparse)
14
- [![PyPI - License](https://img.shields.io/pypi/l/nested-argparse)](https://github.com/stephen-zhao/nested_argparse/blob/main/LICENSE)
15
-
16
- nested-argparse is a python module that non-invasively builds on top of the built-in `argparse` library to allow subparsers to parse into their own nested namespaces.
17
-
18
- The library exposes a class `NestedArgumentParser` which allows arbitrary nesting without worry of namespace conflicts. This is achieved with the following principles of this library:
19
-
20
- - **Inversion of Control:** A parser, when adding a subparser, is in control of what name to use for the sub-namespace which the subparser sends its parsed args to.
21
- - **Drop-In Replacement:** The constructor for `nested_argparse.NestedArgumentParser` can be substituted in directly to where the constructor for `argparse.ArgumentParser` is being used. All subsequent method calls and subparser API calls should work without any additional code change!
22
- - **Customizeability:** There are additional `kwargs` exposed to further customize the nesting options to your liking, if the defaults do not suit your scenario.
23
-
24
- The main difference between this library and its built-in counterpart is the return value of the `parse_args` method. Instead of a flat namespace containing all parsed arguments across all subparsers, `NestedArgumentParser` will produce a namespace tree.
25
-
26
- ## Quick Example
27
-
28
- Given the following parser:
29
-
30
- ```
31
- Root Parser with prog='root'
32
- ├─ positional_1
33
- ├─ --optional_1
34
- ├─ --optional_2
35
- └─ sub parsers with dest='subcommand'
36
- ├─ Sub Parser 1 with name='sub1'
37
- │ ├─ --optional_1
38
- │ └─ --optional_2 with dest='optional2AltName'
39
- └─ Sub Parser 2 with name='sub2'
40
- ├─ --optional_1
41
- └─ --optional_2
42
- ```
43
-
44
- And the following args to parse:
45
-
46
- ```sh
47
- Alice --optional_1=Bob sub1 --optional_1=Carol --optional_2=David
48
- ```
49
-
50
- The built-in `ArgumentParser` would not be able to handle the duplication in `dest`s, but `NestedArgumentParser` will produce the following result when run through `parse_args`:
51
-
52
- ```py
53
- Namespace(
54
- root=Namespace(
55
- subcommand='sub1',
56
- positional_1='Alice',
57
- optional_1='Bob',
58
- sub1=Namespace(
59
- optional_1='Carol',
60
- optional2AltName='David'
61
- )
62
- )
63
- )
64
- ```
65
- Keywords: argparse,nested,namespace,subparser,conflict,parser,cli,command,subcommand
66
- Platform: UNKNOWN
67
- Classifier: Development Status :: 2 - Pre-Alpha
68
- Classifier: Intended Audience :: Developers
69
- Classifier: Programming Language :: Python :: 3
70
- Classifier: License :: OSI Approved :: MIT License
71
- Classifier: Operating System :: OS Independent
72
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
73
- Classifier: Topic :: Utilities
74
- Requires-Python: >=3.6
75
- Description-Content-Type: text/markdown