Fix handling of --help and --version

This commit is contained in:
Kovid Goyal
2025-04-27 15:25:51 +05:30
parent 9961bdf6b5
commit 1413d8fb85
4 changed files with 38 additions and 13 deletions

View File

@@ -565,21 +565,32 @@ class Options:
self.seq = seq
self.usage, self.message, self.appname = usage, message, appname
self.names_map, self.alias_map, self.values_map = get_option_maps(seq)
self.help_called = self.version_called = False
def handle_help(self) -> NoReturn:
if self.do_print:
print_help_for_seq(self.seq, self.usage, self.message, self.appname or appname)
self.help_called = True
raise SystemExit(0)
def handle_version(self) -> NoReturn:
self.version_called = True
if self.do_print:
print(version())
raise SystemExit(0)
def parse_cmdline(oc: Options, disabled: OptionSpecSeq, ans: Any, args: list[str] | None = None) -> list[str]:
names_map = oc.names_map.copy()
values_map = oc.values_map.copy()
if 'help' not in names_map:
names_map['help'] = {'type': 'bool-set', 'aliases': ('--help', '-h')} # type: ignore
values_map['help'] = False
if 'version' not in names_map:
names_map['version'] = {'type': 'bool-set', 'aliases': ('--version', '-v')} # type: ignore
values_map['version'] = False
try:
vals, leftover_args = parse_cli_from_spec(sys.argv[1:] if args is None else args, oc.names_map, oc.values_map)
vals, leftover_args = parse_cli_from_spec(sys.argv[1:] if args is None else args, names_map, values_map)
except Exception as e:
raise SystemExit(str(e))

View File

@@ -274,7 +274,7 @@ parse_cli_loop(CLISpec *spec, int argc, char **argv) { // argv must contain arg
if (!process_cli_arg(spec, flag, payload)) return false;
} else {
state = EXPECTING_ARG;
current_option = arg;
current_option = flag;
}
if (spec->errmsg) return false;
}

View File

@@ -252,6 +252,13 @@ def c_str(x: str) -> str:
def generate_c_parser_for(funcname: str, spec: str) -> Iterator[str]:
names_map, _, defaults_map = get_option_maps(parse_option_spec(spec)[0])
if 'help' not in names_map:
names_map['help'] = {'type': 'bool-set', 'aliases': ('--help', '-h')} # type: ignore
defaults_map['help'] = False
if 'version' not in names_map:
names_map['version'] = {'type': 'bool-set', 'aliases': ('--version', '-v')} # type: ignore
defaults_map['version'] = False
yield f'static void\nparse_cli_for_{funcname}(CLISpec *spec, int argc, char **argv) {{' # }}
yield '\tFlagSpec flag;'
for name, opt in names_map.items():

View File

@@ -77,18 +77,25 @@ version
oc = Options(seq, usage='xxx', message='yyy', appname='test')
oc.do_print = False
def t(args, leftover=(), fails=False, **expected):
def t(args, leftover=(), fails=False, version_called=False, help_called=False, **expected):
oc.help_called = oc.version_called = False
args = list(shlex_split(args))
ans = CLIOptions()
if fails:
with self.assertRaises(SystemExit):
parse_cmdline(oc, disabled, ans, args=args)
if isinstance(fails, str):
with self.assertRaisesRegex(SystemExit, fails):
parse_cmdline(oc, disabled, ans, args=args)
else:
with self.assertRaises(SystemExit):
parse_cmdline(oc, disabled, ans, args=args)
else:
actual_leftover = parse_cmdline(oc, disabled, ans, args=args)
self.assertEqual(tuple(leftover), tuple(actual_leftover), f'{args}\n{ans}')
for dest, defval in oc.values_map.items():
val = expected.get(dest, defval)
self.assertEqual(val, getattr(ans, dest, BaseTest), f'Failed to parse {dest} correctly for: {args} \n{ans}')
self.assertEqual(version_called, oc.version_called)
self.assertEqual(help_called, oc.help_called)
t('-1', bool_set=True)
t('-01', bool_reset=False, bool_set=True)
@@ -101,14 +108,14 @@ version
t('--simple-string --help -- -1', leftover=['-1'], simple_string='--help')
t('-1l=a --list=b -c b --list c', bool_set=True, choice='b', list=list('abc'))
t('-1s= -l "" --list= x', leftover=['x'], bool_set=True, simple_string='', list=['', ''])
t('--choice moo', fails=True)
t('-1c moo', fails=True)
t('-10c=moo', fails=True)
t('-1 -h', fails=True)
t('-1 --help', fails=True)
t('-1 -0v', fails=True)
t('-1 -v0', fails=True)
t('-1 --version', fails=True)
t('--choice moo', fails='a, b, c')
t('-1c moo', fails='a, b, c')
t('-10c=moo', fails='a, b, c')
t('-1 -h', fails=True, help_called=True)
t('-1 --help', fails=True, help_called=True)
t('-1 -0v', fails=True, version_called=True)
t('-1 -v0', fails=True, version_called=True)
t('-1 --version', fails=True, version_called=True)
t('-f=3.142 --int 17', float=3.142, int=17)