argparse-usage 0.1.0__py3-none-any.whl
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.
- argparse_usage/__init__.py +6 -0
- argparse_usage/generator.py +251 -0
- argparse_usage/kdl_utils.py +150 -0
- argparse_usage/py.typed +0 -0
- argparse_usage-0.1.0.dist-info/METADATA +178 -0
- argparse_usage-0.1.0.dist-info/RECORD +7 -0
- argparse_usage-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
"""Generate usage specs from argparse.ArgumentParser."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
from collections.abc import Iterator
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from argparse_usage.kdl_utils import escape_string, format_arg, format_flag
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _get_all_actions(parser: argparse.ArgumentParser) -> Iterator[argparse.Action]:
|
|
11
|
+
"""Yield all actions from parser and its parent parsers."""
|
|
12
|
+
# Get parent parsers first
|
|
13
|
+
parents = getattr(parser, "_parents", None)
|
|
14
|
+
if parents:
|
|
15
|
+
for parent in parents:
|
|
16
|
+
yield from _get_all_actions(parent)
|
|
17
|
+
|
|
18
|
+
# Then get actions from this parser
|
|
19
|
+
for action in parser._actions:
|
|
20
|
+
yield action
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _is_positional(action: argparse.Action) -> bool:
|
|
24
|
+
"""Check if action is a positional argument."""
|
|
25
|
+
return len(action.option_strings) == 0
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _is_flag(action: argparse.Action) -> bool:
|
|
29
|
+
"""Check if action is a flag (optional argument)."""
|
|
30
|
+
return len(action.option_strings) > 0
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _get_flag_names(action: argparse.Action) -> tuple[str | None, str | None]:
|
|
34
|
+
"""Extract short and long flag names from an action."""
|
|
35
|
+
short = None
|
|
36
|
+
long = None
|
|
37
|
+
|
|
38
|
+
for opt in action.option_strings:
|
|
39
|
+
if opt.startswith("--"):
|
|
40
|
+
long = opt
|
|
41
|
+
elif opt.startswith("-"):
|
|
42
|
+
short = opt
|
|
43
|
+
|
|
44
|
+
return short, long
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _get_var_info(nargs: Any) -> tuple[bool, int | None, int | None]:
|
|
48
|
+
"""Extract variadic information from nargs."""
|
|
49
|
+
if nargs in ("*", "+"):
|
|
50
|
+
return True, 1 if nargs == "+" else 0, None
|
|
51
|
+
elif isinstance(nargs, int) and nargs > 1:
|
|
52
|
+
return True, nargs, nargs
|
|
53
|
+
elif nargs is None:
|
|
54
|
+
return False, None, None
|
|
55
|
+
else:
|
|
56
|
+
return False, None, None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _convert_action_to_spec(action: argparse.Action) -> str | None:
|
|
60
|
+
"""Convert an argparse Action to usage spec KDL."""
|
|
61
|
+
# Skip help action
|
|
62
|
+
if isinstance(action, argparse._HelpAction):
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
# Skip subparsers action (handled separately)
|
|
66
|
+
if isinstance(action, argparse._SubParsersAction):
|
|
67
|
+
return None
|
|
68
|
+
|
|
69
|
+
if _is_flag(action):
|
|
70
|
+
return _convert_flag_to_spec(action)
|
|
71
|
+
elif _is_positional(action):
|
|
72
|
+
return _convert_positional_to_spec(action)
|
|
73
|
+
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _convert_flag_to_spec(action: argparse.Action) -> str:
|
|
78
|
+
"""Convert a flag action to usage spec KDL."""
|
|
79
|
+
short, long = _get_flag_names(action)
|
|
80
|
+
help_text = getattr(action, "help", None)
|
|
81
|
+
required = getattr(action, "required", False)
|
|
82
|
+
default = getattr(action, "default", None)
|
|
83
|
+
choices = getattr(action, "choices", None)
|
|
84
|
+
|
|
85
|
+
# Determine action type
|
|
86
|
+
is_count = isinstance(action, argparse._CountAction)
|
|
87
|
+
is_store_true = isinstance(action, argparse._StoreTrueAction)
|
|
88
|
+
is_store_false = isinstance(action, argparse._StoreFalseAction)
|
|
89
|
+
|
|
90
|
+
# Get variadic info
|
|
91
|
+
nargs = getattr(action, "nargs", None)
|
|
92
|
+
var, var_min, var_max = _get_var_info(nargs)
|
|
93
|
+
|
|
94
|
+
# For count actions, default is typically 0
|
|
95
|
+
if is_count and default is None:
|
|
96
|
+
default = 0
|
|
97
|
+
|
|
98
|
+
# For store_true/false, default is opposite of what would happen
|
|
99
|
+
if is_store_true and default is None:
|
|
100
|
+
default = False
|
|
101
|
+
if is_store_false and default is None:
|
|
102
|
+
default = True
|
|
103
|
+
|
|
104
|
+
# Handle ellipsis notation for variadic flags
|
|
105
|
+
if var and long:
|
|
106
|
+
if long.endswith("..."):
|
|
107
|
+
long = long[:-3]
|
|
108
|
+
elif nargs in ("*", "+"):
|
|
109
|
+
# Add ellipsis to long form for variadic
|
|
110
|
+
long = f"{long}..."
|
|
111
|
+
|
|
112
|
+
return format_flag(
|
|
113
|
+
short=short,
|
|
114
|
+
long=long,
|
|
115
|
+
help_text=help_text,
|
|
116
|
+
required=required,
|
|
117
|
+
default=default,
|
|
118
|
+
count=is_count,
|
|
119
|
+
var=var,
|
|
120
|
+
var_min=var_min,
|
|
121
|
+
var_max=var_max,
|
|
122
|
+
choices=choices,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _convert_positional_to_spec(action: argparse.Action) -> str:
|
|
127
|
+
"""Convert a positional argument to usage spec KDL."""
|
|
128
|
+
name = action.dest
|
|
129
|
+
help_text = getattr(action, "help", None)
|
|
130
|
+
required = getattr(action, "required", False)
|
|
131
|
+
default = getattr(action, "default", None)
|
|
132
|
+
choices = getattr(action, "choices", None)
|
|
133
|
+
|
|
134
|
+
# Get variadic info
|
|
135
|
+
nargs = getattr(action, "nargs", None)
|
|
136
|
+
var, var_min, var_max = _get_var_info(nargs)
|
|
137
|
+
|
|
138
|
+
# Handle optional positionals (nargs='?')
|
|
139
|
+
if nargs == "?":
|
|
140
|
+
required = False
|
|
141
|
+
|
|
142
|
+
return format_arg(
|
|
143
|
+
name=name,
|
|
144
|
+
help_text=help_text,
|
|
145
|
+
required=required,
|
|
146
|
+
default=default,
|
|
147
|
+
var=var,
|
|
148
|
+
var_min=var_min,
|
|
149
|
+
var_max=var_max,
|
|
150
|
+
choices=choices,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _format_subcommand(name: str, parser: argparse.ArgumentParser) -> str:
|
|
155
|
+
"""Format a subcommand with its spec."""
|
|
156
|
+
lines = [f"cmd {name} {{"]
|
|
157
|
+
|
|
158
|
+
# Add help text if available
|
|
159
|
+
help_text = getattr(parser, "description", None) or getattr(
|
|
160
|
+
parser, "_help_text", None
|
|
161
|
+
)
|
|
162
|
+
if help_text:
|
|
163
|
+
lines.append(f" help={escape_string(help_text)}")
|
|
164
|
+
|
|
165
|
+
# Process all actions (skip help and subparsers)
|
|
166
|
+
for action in parser._actions:
|
|
167
|
+
if isinstance(action, (argparse._HelpAction, argparse._SubParsersAction)):
|
|
168
|
+
continue
|
|
169
|
+
|
|
170
|
+
spec = _convert_action_to_spec(action)
|
|
171
|
+
if spec:
|
|
172
|
+
# Indent each line
|
|
173
|
+
for line in spec.split("\n"):
|
|
174
|
+
lines.append(f" {line}")
|
|
175
|
+
|
|
176
|
+
# Handle sub-subcommands
|
|
177
|
+
for action in parser._actions:
|
|
178
|
+
if isinstance(action, argparse._SubParsersAction):
|
|
179
|
+
for sub_name, sub_parser in action.choices.items():
|
|
180
|
+
lines.append(_format_subcommand(sub_name, sub_parser))
|
|
181
|
+
|
|
182
|
+
lines.append("}")
|
|
183
|
+
return "\n".join(lines)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def generate_usage_spec(
|
|
187
|
+
parser: argparse.ArgumentParser,
|
|
188
|
+
name: str | None = None,
|
|
189
|
+
version: str | None = None,
|
|
190
|
+
author: str | None = None,
|
|
191
|
+
bin_name: str | None = None,
|
|
192
|
+
) -> str:
|
|
193
|
+
"""Generate a usage spec KDL string from an ArgumentParser.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
parser: The ArgumentParser instance to convert.
|
|
197
|
+
name: The friendly name for the CLI (defaults to parser.prog or description).
|
|
198
|
+
version: The version of the CLI.
|
|
199
|
+
author: The author of the CLI.
|
|
200
|
+
bin_name: The binary name (defaults to parser.prog).
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
A KDL-formatted usage spec string.
|
|
204
|
+
"""
|
|
205
|
+
lines = []
|
|
206
|
+
|
|
207
|
+
lines.append("// @generated by argparse-usage from Python argparse")
|
|
208
|
+
lines.append("")
|
|
209
|
+
|
|
210
|
+
if name:
|
|
211
|
+
lines.append(f"name {escape_string(name)}")
|
|
212
|
+
elif parser.prog:
|
|
213
|
+
lines.append(f"name {escape_string(parser.prog)}")
|
|
214
|
+
elif parser.description:
|
|
215
|
+
# Try to extract name from description
|
|
216
|
+
import re
|
|
217
|
+
|
|
218
|
+
match = re.match(r"^(\w+)", parser.description)
|
|
219
|
+
if match:
|
|
220
|
+
lines.append(f"name {escape_string(match.group(1))}")
|
|
221
|
+
|
|
222
|
+
bin_value = bin_name or parser.prog or "cli"
|
|
223
|
+
lines.append(f"bin {escape_string(bin_value)}")
|
|
224
|
+
|
|
225
|
+
if version:
|
|
226
|
+
lines.append(f"version {escape_string(version)}")
|
|
227
|
+
|
|
228
|
+
if author:
|
|
229
|
+
lines.append(f"author {escape_string(author)}")
|
|
230
|
+
|
|
231
|
+
if parser.description:
|
|
232
|
+
lines.append(f"about {escape_string(parser.description)}")
|
|
233
|
+
|
|
234
|
+
lines.append("")
|
|
235
|
+
|
|
236
|
+
# Process all actions (skip help and subparsers)
|
|
237
|
+
for action in parser._actions:
|
|
238
|
+
if isinstance(action, (argparse._HelpAction, argparse._SubParsersAction)):
|
|
239
|
+
continue
|
|
240
|
+
|
|
241
|
+
spec = _convert_action_to_spec(action)
|
|
242
|
+
if spec:
|
|
243
|
+
lines.append(spec)
|
|
244
|
+
|
|
245
|
+
# Handle subcommands
|
|
246
|
+
for action in parser._actions:
|
|
247
|
+
if isinstance(action, argparse._SubParsersAction):
|
|
248
|
+
for name, sub_parser in action.choices.items():
|
|
249
|
+
lines.append(_format_subcommand(name, sub_parser))
|
|
250
|
+
|
|
251
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"""KDL formatting utilities for usage spec generation."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def escape_string(value: str) -> str:
|
|
7
|
+
"""Escape a string value for KDL format."""
|
|
8
|
+
if not value:
|
|
9
|
+
return '""'
|
|
10
|
+
|
|
11
|
+
value = str(value)
|
|
12
|
+
|
|
13
|
+
# If string contains special characters, use raw string format r#"..."#
|
|
14
|
+
needs_raw = any(c in value for c in ['"', "\n", "\r", "\t", "\\"])
|
|
15
|
+
|
|
16
|
+
if needs_raw:
|
|
17
|
+
# Use raw string format to avoid escaping
|
|
18
|
+
return f'r#"{value}"#'
|
|
19
|
+
else:
|
|
20
|
+
return f'"{value}"'
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def format_flag(
|
|
24
|
+
short: str | None,
|
|
25
|
+
long: str | None,
|
|
26
|
+
help_text: str | None = None,
|
|
27
|
+
required: bool = False,
|
|
28
|
+
default: str | bool | int | None = None,
|
|
29
|
+
count: bool = False,
|
|
30
|
+
var: bool = False,
|
|
31
|
+
var_min: int | None = None,
|
|
32
|
+
var_max: int | None = None,
|
|
33
|
+
choices: list[str] | None = None,
|
|
34
|
+
long_help: str | None = None,
|
|
35
|
+
) -> str:
|
|
36
|
+
"""Format a flag definition for KDL spec."""
|
|
37
|
+
parts = []
|
|
38
|
+
|
|
39
|
+
# Build flag name (e.g., "-f --force")
|
|
40
|
+
flag_names = []
|
|
41
|
+
if short:
|
|
42
|
+
flag_names.append(short)
|
|
43
|
+
if long:
|
|
44
|
+
flag_names.append(long)
|
|
45
|
+
flag_str = " ".join(flag_names)
|
|
46
|
+
|
|
47
|
+
# Check if we need a block (for complex attributes like choices or long_help)
|
|
48
|
+
needs_block = choices is not None or (long_help and len(long_help) > 50)
|
|
49
|
+
|
|
50
|
+
# Simple attributes (inline)
|
|
51
|
+
attrs = []
|
|
52
|
+
if help_text:
|
|
53
|
+
attrs.append(f"help={escape_string(help_text)}")
|
|
54
|
+
if required:
|
|
55
|
+
attrs.append("required=#true")
|
|
56
|
+
if default is not None and default != argparse.SUPPRESS:
|
|
57
|
+
if isinstance(default, bool):
|
|
58
|
+
attrs.append(f"default=#{str(default).lower()}")
|
|
59
|
+
else:
|
|
60
|
+
attrs.append(f"default={escape_string(str(default))}")
|
|
61
|
+
if count:
|
|
62
|
+
attrs.append("count=#true")
|
|
63
|
+
if var or var_min is not None or var_max is not None:
|
|
64
|
+
attrs.append("var=#true")
|
|
65
|
+
if var_min is not None:
|
|
66
|
+
attrs.append(f"var_min={var_min}")
|
|
67
|
+
if var_max is not None:
|
|
68
|
+
attrs.append(f"var_max={var_max}")
|
|
69
|
+
|
|
70
|
+
if needs_block:
|
|
71
|
+
parts.append(f"flag {escape_string(flag_str)} {{")
|
|
72
|
+
for attr in attrs:
|
|
73
|
+
parts.append(f" {attr}")
|
|
74
|
+
if long_help:
|
|
75
|
+
parts.append(f" long_help={escape_string(long_help)}")
|
|
76
|
+
if choices:
|
|
77
|
+
arg_name = long.lstrip("-") if long else "value"
|
|
78
|
+
parts.append(f' arg "<{arg_name}>" {{')
|
|
79
|
+
parts.append(f" choices {' '.join(escape_string(c) for c in choices)}")
|
|
80
|
+
parts.append(" }")
|
|
81
|
+
parts.append("}")
|
|
82
|
+
else:
|
|
83
|
+
line = f"flag {escape_string(flag_str)}"
|
|
84
|
+
if attrs:
|
|
85
|
+
line += " " + " ".join(attrs)
|
|
86
|
+
parts.append(line)
|
|
87
|
+
|
|
88
|
+
return "\n".join(parts)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def format_arg(
|
|
92
|
+
name: str,
|
|
93
|
+
help_text: str | None = None,
|
|
94
|
+
required: bool = True,
|
|
95
|
+
default: str | None = None,
|
|
96
|
+
var: bool = False,
|
|
97
|
+
var_min: int | None = None,
|
|
98
|
+
var_max: int | None = None,
|
|
99
|
+
choices: list[str] | None = None,
|
|
100
|
+
long_help: str | None = None,
|
|
101
|
+
) -> str:
|
|
102
|
+
"""Format an argument definition for KDL spec."""
|
|
103
|
+
parts = []
|
|
104
|
+
|
|
105
|
+
# Build arg name (e.g., "<file>" or "[file]" or "<file>..." or "[file]...")
|
|
106
|
+
if not var and not var_min and not var_max:
|
|
107
|
+
arg_name = f"[{name}]" if not required else f"<{name}>"
|
|
108
|
+
else:
|
|
109
|
+
# Variadic argument
|
|
110
|
+
if var_min == 0:
|
|
111
|
+
# Zero or more -> optional
|
|
112
|
+
arg_name = f"[{name}]..."
|
|
113
|
+
else:
|
|
114
|
+
# One or more -> required
|
|
115
|
+
arg_name = f"<{name}>..."
|
|
116
|
+
|
|
117
|
+
# Check if we need a block (for complex attributes like choices or long_help)
|
|
118
|
+
needs_block = choices is not None or (long_help and len(long_help) > 50)
|
|
119
|
+
|
|
120
|
+
# Simple attributes (inline)
|
|
121
|
+
attrs = []
|
|
122
|
+
if help_text:
|
|
123
|
+
attrs.append(f"help={escape_string(help_text)}")
|
|
124
|
+
if not required:
|
|
125
|
+
attrs.append("required=#false")
|
|
126
|
+
if default is not None and default != argparse.SUPPRESS:
|
|
127
|
+
attrs.append(f"default={escape_string(str(default))}")
|
|
128
|
+
if var:
|
|
129
|
+
attrs.append("var=#true")
|
|
130
|
+
if var_min is not None:
|
|
131
|
+
attrs.append(f"var_min={var_min}")
|
|
132
|
+
if var_max is not None:
|
|
133
|
+
attrs.append(f"var_max={var_max}")
|
|
134
|
+
|
|
135
|
+
if needs_block:
|
|
136
|
+
parts.append(f"arg {escape_string(arg_name)} {{")
|
|
137
|
+
for attr in attrs:
|
|
138
|
+
parts.append(f" {attr}")
|
|
139
|
+
if long_help:
|
|
140
|
+
parts.append(f" long_help={escape_string(long_help)}")
|
|
141
|
+
if choices:
|
|
142
|
+
parts.append(f" choices {' '.join(escape_string(c) for c in choices)}")
|
|
143
|
+
parts.append("}")
|
|
144
|
+
else:
|
|
145
|
+
line = f"arg {escape_string(arg_name)}"
|
|
146
|
+
if attrs:
|
|
147
|
+
line += " " + " ".join(attrs)
|
|
148
|
+
parts.append(line)
|
|
149
|
+
|
|
150
|
+
return "\n".join(parts)
|
argparse_usage/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: argparse-usage
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Generate usage spec KDL files from Python argparse.ArgumentParser
|
|
5
|
+
Author: acidghost
|
|
6
|
+
Author-email: acidghost <1787979+acidghost@users.noreply.github.com>
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
|
|
10
|
+
# argparse-usage
|
|
11
|
+
|
|
12
|
+
Generate [usage](https://usage.jdx.dev/) KDL files from Python's `argparse.ArgumentParser`.
|
|
13
|
+
|
|
14
|
+
This library converts Python `argparse` definitions to the [usage spec format](https://usage.jdx.dev/spec/reference/), enabling automatic generation of:
|
|
15
|
+
|
|
16
|
+
- Markdown documentation
|
|
17
|
+
- Manpages
|
|
18
|
+
- Shell completions (bash, fish, zsh, powershell, nu)
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install argparse-usage
|
|
24
|
+
# or
|
|
25
|
+
uv add argparse-usage
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
import argparse
|
|
32
|
+
import argparse_usage
|
|
33
|
+
|
|
34
|
+
# Create your ArgumentParser as usual
|
|
35
|
+
parser = argparse.ArgumentParser(
|
|
36
|
+
prog='mycli',
|
|
37
|
+
description='My CLI tool',
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
parser.add_argument('-v', '--verbose', action='count', default=0, help='Increase verbosity')
|
|
41
|
+
parser.add_argument('-f', '--force', action='store_true', help='Force operation')
|
|
42
|
+
parser.add_argument('files', nargs='+', help='Files to process')
|
|
43
|
+
|
|
44
|
+
# Add subcommands
|
|
45
|
+
subparsers = parser.add_subparsers(dest='command')
|
|
46
|
+
build_cmd = subparsers.add_parser('build', help='Build project')
|
|
47
|
+
test_cmd = subparsers.add_parser('test', help='Run tests')
|
|
48
|
+
|
|
49
|
+
# Generate usage spec
|
|
50
|
+
spec = argparse_usage.generate(
|
|
51
|
+
parser,
|
|
52
|
+
name='My CLI',
|
|
53
|
+
version='1.0.0',
|
|
54
|
+
author='Your Name',
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
print(spec)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Output:
|
|
61
|
+
|
|
62
|
+
```kdl
|
|
63
|
+
// @generated by argparse-usage from Python argparse
|
|
64
|
+
|
|
65
|
+
name "My CLI"
|
|
66
|
+
bin "mycli"
|
|
67
|
+
version "1.0.0"
|
|
68
|
+
author "Your Name"
|
|
69
|
+
about "My CLI tool"
|
|
70
|
+
|
|
71
|
+
arg "<files>..." help="Files to process" var=#true var_min=1
|
|
72
|
+
|
|
73
|
+
flag "-v --verbose" help="Increase verbosity" default="0" count=#true
|
|
74
|
+
flag "-f --force" help="Force operation" default=#false
|
|
75
|
+
|
|
76
|
+
cmd build {
|
|
77
|
+
// options
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
cmd test {
|
|
81
|
+
// options
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Generating Documentation
|
|
86
|
+
|
|
87
|
+
Save the spec and use the `usage` CLI to generate documentation:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Save spec to file
|
|
91
|
+
python mycli.py --usage-spec > mycli.usage.kdl
|
|
92
|
+
|
|
93
|
+
# Generate markdown documentation
|
|
94
|
+
usage generate markdown --file mycli.usage.kdl --out-file README.md
|
|
95
|
+
|
|
96
|
+
# Generate shell completions
|
|
97
|
+
usage generate completion bash mycli --file mycli.usage.kdl > mycli-completion.bash
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Supported Features
|
|
101
|
+
|
|
102
|
+
### Flags (optional arguments)
|
|
103
|
+
|
|
104
|
+
| argparse action | usage spec mapping |
|
|
105
|
+
| ---------------------- | -------------------------------------------------------------- |
|
|
106
|
+
| `action='store_true'` | `flag "--force" default=#false` |
|
|
107
|
+
| `action='store_false'` | `flag "--quiet" default=#true` |
|
|
108
|
+
| `action='count'` | `flag "-v --verbose" count=#true` |
|
|
109
|
+
| `nargs='*'` | `flag "--files..." var=#true` |
|
|
110
|
+
| `nargs='+'` | `flag "--tags..." var=#true var_min=1` |
|
|
111
|
+
| `choices=[...]` | `flag "--format" { arg "<format>" { choices "json" "yaml" } }` |
|
|
112
|
+
|
|
113
|
+
### Arguments (positional)
|
|
114
|
+
|
|
115
|
+
| argparse nargs | usage spec mapping |
|
|
116
|
+
| ---------------------- | --------------------------------------- |
|
|
117
|
+
| `nargs=None` (default) | `arg "<file>"` |
|
|
118
|
+
| `nargs='?'` | `arg "[file]"` |
|
|
119
|
+
| `nargs='*'` | `arg "[files]..." var=#true` |
|
|
120
|
+
| `nargs='+'` | `arg "<files>..." var=#true var_min=1` |
|
|
121
|
+
| `nargs=N` | `arg "<coords>..." var_min=N var_max=N` |
|
|
122
|
+
|
|
123
|
+
### Parent Parsers
|
|
124
|
+
|
|
125
|
+
Inheritance from parent parsers is supported:
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
parent_parser = argparse.ArgumentParser(add_help=False)
|
|
129
|
+
parent_parser.add_argument('-v', '--verbose', action='count')
|
|
130
|
+
|
|
131
|
+
parser = argparse.ArgumentParser(parents=[parent_parser])
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Subcommands
|
|
135
|
+
|
|
136
|
+
Nested subcommands are fully supported:
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
subparsers = parser.add_subparsers()
|
|
140
|
+
add_cmd = subparsers.add_parser('add')
|
|
141
|
+
add_cmd.add_argument('name')
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Output:
|
|
145
|
+
|
|
146
|
+
```kdl
|
|
147
|
+
cmd add {
|
|
148
|
+
arg "<name>"
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## API Reference
|
|
153
|
+
|
|
154
|
+
### `generate(parser, name=None, version=None, author=None, bin_name=None)`
|
|
155
|
+
|
|
156
|
+
Generate a usage spec KDL string from an ArgumentParser.
|
|
157
|
+
|
|
158
|
+
**Parameters:**
|
|
159
|
+
|
|
160
|
+
- `parser` (ArgumentParser): The ArgumentParser instance to convert
|
|
161
|
+
- `name` (str, optional): Friendly name for the CLI (defaults to parser.prog)
|
|
162
|
+
- `version` (str, optional): Version of the CLI
|
|
163
|
+
- `author` (str, optional): Author of the CLI
|
|
164
|
+
- `bin_name` (str, optional): Binary name (defaults to parser.prog)
|
|
165
|
+
|
|
166
|
+
**Returns:**
|
|
167
|
+
|
|
168
|
+
- `str`: KDL-formatted usage spec string
|
|
169
|
+
|
|
170
|
+
## Examples
|
|
171
|
+
|
|
172
|
+
See the [examples/](examples/) directory for complete examples:
|
|
173
|
+
|
|
174
|
+
- [basic_usage.py](examples/basic_usage.py) - Basic usage with flags, args, and subcommands
|
|
175
|
+
|
|
176
|
+
## Contributing
|
|
177
|
+
|
|
178
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
argparse_usage/__init__.py,sha256=reBHhbeJMGSXH6e01vl4CneZM6FtP_Gs3g51B-hWXHA,180
|
|
2
|
+
argparse_usage/generator.py,sha256=Ofr0M0P2ImcejvsWra52n-FJ0XsnXee9L3ojZ9LPGIk,7560
|
|
3
|
+
argparse_usage/kdl_utils.py,sha256=_n91qL_sNdGA8GcT8rAJq2t_dLbucJ2mC4EKh079H6o,4685
|
|
4
|
+
argparse_usage/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
argparse_usage-0.1.0.dist-info/WHEEL,sha256=mydTeHxOpFHo-DnYhAd_3ATePms-g4rrYvM7wJK8P-U,80
|
|
6
|
+
argparse_usage-0.1.0.dist-info/METADATA,sha256=N8yeQQ8a8tYZ7HpkK9vIQjSjGoYYpzdcF5b6_36Q2hk,4867
|
|
7
|
+
argparse_usage-0.1.0.dist-info/RECORD,,
|