feat: do not exit() with exit_on_error to False

This commit is contained in:
egarette@silique.fr 2025-10-07 20:58:45 +02:00
parent 1ca99df30e
commit 44378162b2
2 changed files with 36 additions and 9 deletions

View file

@ -306,7 +306,7 @@ def test_leadership_modif_follower_choice(json):
def test_leadership_modif_follower_choice_unknown(json):
output = """usage: prog.py [-h] [--leader.leader [LEADER ...]] [--leader.pop-leader INDEX] [--leader.follower INDEX [FOLLOWER]] [--leader.follower_submulti INDEX [FOLLOWER_SUBMULTI ...]] [--leader.follower_integer INDEX [FOLLOWER_INTEGER]] [--leader.follower_boolean INDEX] [--leader.no-follower_boolean INDEX] [--leader.follower_choice INDEX [{opt1,opt2}]]
prog.py: error: argument --leader.follower_choice: invalid choice: 'opt_unknown' (choose from 'opt1', 'opt2')
prog.py: error: argument --leader.follower_choice: invalid choice: 'opt_unknown' (choose from opt1, opt2)
"""
config = get_config(json)
parser = TiramisuCmdlineParser(config, 'prog.py', formatter_class=TestHelpFormatter)

View file

@ -15,6 +15,7 @@
from typing import Union, List, Dict, Tuple, Optional, Any
from argparse import (
ArgumentParser,
ArgumentError,
Namespace,
SUPPRESS,
_HelpAction,
@ -123,11 +124,13 @@ class TiramisuNamespace(Namespace):
option = self._config.option(true_key)
if option.isfollower():
_setattr = self._setattr_follower
if not value[0].isdecimal():
raise ValueError("index must be a number, not {}".format(value[0]))
index = int(value[0])
index = value[0]
if isinstance(index, str):
if not value[0].isdecimal():
raise ValueError("index must be a number, not {}".format(value[0]))
index = int(index)
option = self._config.option(true_key, index)
true_value = ",".join(value[1:])
true_value = ",".join([str(v) for v in value[1:]])
else:
_setattr = self._setattr
true_value = value
@ -168,7 +171,7 @@ class TiramisuNamespace(Namespace):
"argument {}: invalid choice: '{}' (choose from {})".format(
self.arguments[key],
display_value,
", ".join([f"'{val}'" for val in choices]),
", ".join([f"{val}" for val in choices]),
)
)
else:
@ -177,6 +180,12 @@ class TiramisuNamespace(Namespace):
def _setattr(self, option: "Option", true_key: str, key: str, value: Any) -> None:
if option.ismulti() and value is not None and not isinstance(value, list):
value = [value]
if option.isleader():
# set value for a leader, it began to remove all values!
len_leader = option.value.len()
if len_leader:
for idx in range(len_leader - 1, -1, -1):
option.value.pop(idx)
try:
option.value.set(value)
except PropertiesOptionError as err:
@ -400,6 +409,7 @@ class TiramisuCmdlineParser(ArgumentParser):
self.config,
self.prog,
root=self.root,
exit_on_error=self.exit_on_error,
remove_empty_od=self.remove_empty_od,
display_modified_value=self.display_modified_value,
formatter_class=self.formatter_class,
@ -654,7 +664,7 @@ class TiramisuCmdlineParser(ArgumentParser):
kwargs["nargs"] = 2
if _forhelp and "mandatory" not in properties:
metavar = "[{}]".format(metavar)
if option.type() == "choice":
if _forhelp and option.type() == "choice":
# do not manage choice with argparse there is problem with integer problem
kwargs["metavar"] = (
"INDEX",
@ -673,13 +683,15 @@ class TiramisuCmdlineParser(ArgumentParser):
if _forhelp and option.type() == "boolean":
kwargs["metavar"] = "INDEX"
kwargs["nargs"] = 1
elif option.type() == "choice" and not option.isfollower():
elif _forhelp and option.type() == "choice" and not option.isfollower():
# do not manage choice with argparse there is problem with integer problem
kwargs["choices"] = get_choice_list(option, properties, False)
elif option.type() == "float":
kwargs["type"] = float
else:
pass
if not _forhelp and option.type() != "boolean" and "nargs" not in kwargs.kwargs:
kwargs["nargs"] = "?"
actions.setdefault(kwargs.ga_name, []).append(kwargs)
for option_is_not_default in options_is_not_default.values():
@ -696,7 +708,9 @@ class TiramisuCmdlineParser(ArgumentParser):
def parse_args(self, *args, valid_mandatory=True, **kwargs):
kwargs["namespace"] = self.namespace
try:
namespaces = super().parse_args(*args, **kwargs)
namespaces, unknown = super().parse_known_args(*args, **kwargs)
if unknown:
msg_unknown = 'unrecognized arguments: %s' % ' '.join(unknown)
except PropertiesOptionError as err:
name = err._subconfig.path
properties = self.config.option(name).property.get()
@ -739,6 +753,13 @@ class TiramisuCmdlineParser(ArgumentParser):
self.error(
"the following arguments are required: {}".format(", ".join(errors))
)
if unknown:
if self.exit_on_error:
self.error(msg_unknown)
else:
err = ArgumentError(None, msg_unknown)
err.unknown = unknown
raise err
return namespaces
def format_usage(self, *args, **kwargs):
@ -779,3 +800,9 @@ class TiramisuCmdlineParser(ArgumentParser):
def get_config(self):
return self.config
def error(self, msg):
if self.exit_on_error:
super().error(msg)
else:
raise ArgumentError(None, msg)