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.
- nested_argparse-0.2.0/LICENSE +21 -0
- nested_argparse-0.2.0/PKG-INFO +137 -0
- nested_argparse-0.2.0/README.md +109 -0
- nested_argparse-0.2.0/pyproject.toml +87 -0
- nested_argparse-0.2.0/setup.cfg +4 -0
- {nested_argparse-0.1.1 → nested_argparse-0.2.0}/src/nested_argparse/__init__.py +0 -0
- {nested_argparse-0.1.1 → nested_argparse-0.2.0}/src/nested_argparse/nested_argparse.py +70 -45
- nested_argparse-0.2.0/src/nested_argparse/py.typed +0 -0
- nested_argparse-0.2.0/src/nested_argparse.egg-info/PKG-INFO +137 -0
- {nested_argparse-0.1.1 → nested_argparse-0.2.0}/src/nested_argparse.egg-info/SOURCES.txt +4 -2
- {nested_argparse-0.1.1 → nested_argparse-0.2.0}/src/nested_argparse.egg-info/dependency_links.txt +0 -0
- {nested_argparse-0.1.1 → nested_argparse-0.2.0}/src/nested_argparse.egg-info/top_level.txt +0 -0
- nested_argparse-0.2.0/test/test_multinesting.py +41 -0
- {nested_argparse-0.1.1 → nested_argparse-0.2.0}/test/test_sanity_check.py +1 -1
- nested_argparse-0.1.1/PKG-INFO +0 -75
- nested_argparse-0.1.1/README.md +0 -56
- nested_argparse-0.1.1/setup.cfg +0 -41
- nested_argparse-0.1.1/setup.py +0 -4
- nested_argparse-0.1.1/src/nested_argparse.egg-info/PKG-INFO +0 -75
|
@@ -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
|
+
[](https://pypi.org/project/nested-argparse/)
|
|
32
|
+

|
|
33
|
+

|
|
34
|
+
[](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
|
+
[](https://pypi.org/project/nested-argparse/)
|
|
4
|
+

|
|
5
|
+

|
|
6
|
+
[](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 = []
|
|
File without changes
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import argparse
|
|
2
|
-
from typing import Any
|
|
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:
|
|
33
|
-
self.nest_path_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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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) ->
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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) ->
|
|
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
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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
|
+
[](https://pypi.org/project/nested-argparse/)
|
|
32
|
+

|
|
33
|
+

|
|
34
|
+
[](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
|
-
|
|
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
|
{nested_argparse-0.1.1 → nested_argparse-0.2.0}/src/nested_argparse.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
@@ -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
|
nested_argparse-0.1.1/PKG-INFO
DELETED
|
@@ -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
|
-
[](https://pypi.org/project/nested-argparse/)
|
|
12
|
-

|
|
13
|
-

|
|
14
|
-
[](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
|
nested_argparse-0.1.1/README.md
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
# nested-argparse 💬 → 🅰.🅱.🆒
|
|
2
|
-
|
|
3
|
-
[](https://pypi.org/project/nested-argparse/)
|
|
4
|
-

|
|
5
|
-

|
|
6
|
-
[](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
|
-
```
|
nested_argparse-0.1.1/setup.cfg
DELETED
|
@@ -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
|
-
|
nested_argparse-0.1.1/setup.py
DELETED
|
@@ -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
|
-
[](https://pypi.org/project/nested-argparse/)
|
|
12
|
-

|
|
13
|
-

|
|
14
|
-
[](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
|