J'aimerais utiliser argparse pour analyser des arguments de ligne de commande booléens écrits sous la forme "--foo True" ou "--foo False". Par exemple:
my_program --my_boolean_flag False
Cependant, le code de test suivant ne fait pas ce que je voudrais:
import argparse
parser = argparse.ArgumentParser(description="My parser")
parser.add_argument("--my_bool", type=bool)
cmd_line = ["--my_bool", "False"]
parsed_args = parser.parse(cmd_line)
Malheureusement, parsed_args.my_bool
est évalué à True
. C'est le cas même lorsque je modifie cmd_line
en ["--my_bool", ""]
, ce qui est surprenant, car bool("")
est évalué à False
.
Comment puis-je obtenir argparse pour analyser "False"
, "F"
, et leurs variantes minuscules pour être False
?
Encore une autre solution utilisant les suggestions précédentes, mais avec l'erreur d'analyse "correcte" de argparse
:
def str2bool(v):
if isinstance(v, bool):
return v
if v.lower() in ('yes', 'true', 't', 'y', '1'):
return True
Elif v.lower() in ('no', 'false', 'f', 'n', '0'):
return False
else:
raise argparse.ArgumentTypeError('Boolean value expected.')
Ceci est très utile pour effectuer des commutations avec des valeurs par défaut. par exemple
parser.add_argument("--Nice", type=str2bool, nargs='?',
const=True, default=False,
help="Activate Nice mode.")
me permet d'utiliser:
script --Nice
script --Nice <bool>
et utilisez toujours une valeur par défaut (spécifique aux paramètres de l'utilisateur). Un inconvénient (indirectement lié) à cette approche est que les "nargos" peuvent attraper un argument de position - voir cette question connexe et ce rapport de bogue argparse .
Je pense qu'un moyen plus canonique de faire cela est via:
command --feature
et
command --no-feature
argparse
supporte joliment cette version:
parser.add_argument('--feature', dest='feature', action='store_true')
parser.add_argument('--no-feature', dest='feature', action='store_false')
parser.set_defaults(feature=True)
Bien sûr, si vous voulez vraiment la version --arg <True|False>
, vous pouvez passer ast.literal_eval
comme "type" ou une fonction définie par l'utilisateur ...
def t_or_f(arg):
ua = str(arg).upper()
if 'TRUE'.startswith(ua):
return True
Elif 'FALSE'.startswith(ua):
return False
else:
pass #error condition maybe?
Je recommande la réponse de mgilson mais avec un groupe mutuellement exclusif
afin que vous ne puissiez pas utiliser --feature
et --no-feature
en même temps.
command --feature
et
command --no-feature
mais non
command --feature --no-feature
Scénario:
feature_parser = parser.add_mutually_exclusive_group(required=False)
feature_parser.add_argument('--feature', dest='feature', action='store_true')
feature_parser.add_argument('--no-feature', dest='feature', action='store_false')
parser.set_defaults(feature=True)
Vous pouvez ensuite utiliser cette aide si vous souhaitez en définir plusieurs:
def add_bool_arg(parser, name, default=False):
group = parser.add_mutually_exclusive_group(required=False)
group.add_argument('--' + name, dest=name, action='store_true')
group.add_argument('--no-' + name, dest=name, action='store_false')
parser.set_defaults(**{name:default})
add_bool_arg(parser, 'useful-feature')
add_bool_arg(parser, 'even-more-useful-feature')
Il semble y avoir une certaine confusion quant à ce que pourraient signifier type=bool
et type='bool'
. Un (ou les deux) doit-il signifier "exécuter la fonction bool()
, ou" retourner un booléen "? En l'état, type='bool'
ne veut rien dire. add_argument
génère une erreur 'bool' is not callable
, comme si vous utilisiez type='foobar'
ou type='int'
.
Mais argparse
a un registre qui vous permet de définir des mots clés comme celui-ci. Il est principalement utilisé pour action
, par exemple. `action = 'store_true'. Vous pouvez voir les mots-clés enregistrés avec:
parser._registries
qui affiche un dictionnaire
{'action': {None: argparse._StoreAction,
'append': argparse._AppendAction,
'append_const': argparse._AppendConstAction,
...
'type': {None: <function argparse.identity>}}
Il y a beaucoup d'actions définies, mais un seul type, celui par défaut, argparse.identity
.
Ce code définit un mot clé 'bool':
def str2bool(v):
#susendberg's function
return v.lower() in ("yes", "true", "t", "1")
p = argparse.ArgumentParser()
p.register('type','bool',str2bool) # add type keyword to registries
p.add_argument('-b',type='bool') # do not use 'type=bool'
# p.add_argument('-b',type=str2bool) # works just as well
p.parse_args('-b false'.split())
Namespace(b=False)
parser.register()
n'est pas documenté, mais n'est pas caché. Pour la plupart, le programmeur n'a pas besoin de le savoir car type
et action
prennent des valeurs de fonction et de classe. Il existe de nombreux exemples d'empilement de pile permettant de définir des valeurs personnalisées pour les deux.
Au cas où cela ne serait pas évident dans la discussion précédente, bool()
ne signifie pas "analyser une chaîne". De la documentation Python:
bool (x): convertit une valeur en booléen à l'aide de la procédure de test de vérité standard.
Cela contraste avec
int (x): Convertit un nombre ou une chaîne x en un entier.
bon mot:
parser.add_argument('--is_debug', default=False, type=lambda x: (str(x).lower() == 'true'))
Voici une autre variante sans rangée/s supplémentaire pour définir les valeurs par défaut. La valeur booléenne a toujours une valeur attribuée pour pouvoir être utilisée dans des instructions logiques sans pré-vérification.
import argparse
parser = argparse.ArgumentParser(description="Parse bool")
parser.add_argument("--do-something", default=False, action="store_true" , help="Flag to do something")
args = parser.parse_args()
if args.do_something == True:
print("Do something")
else:
print("Don't do something")
print("Check that args.do_something=" + str(args.do_something) + " is always a bool")
Je cherchais le même problème, et à mon avis, la jolie solution est la suivante:
def str2bool(v):
return v.lower() in ("yes", "true", "t", "1")
et en utilisant cela pour analyser la chaîne booléenne comme suggéré ci-dessus.
En plus de ce que @mgilson a dit, il convient de noter qu’il existe également une méthode ArgumentParser.add_mutually_exclusive_group(required=False)
qui rendrait trivial l’application du fait que --flag
et --no-flag
ne sont pas utilisés à la en même temps.
Une méthode assez similaire consiste à utiliser:
feature.add_argument('--feature',action='store_true')
et si vous définissez l'argument --feature dans votre commande
command --feature
l'argument sera True, si vous ne définissez pas le type --feature, la valeur par défaut des arguments est toujours False!
Cela fonctionne pour tout ce que je pense:
add_boolean_argument(parser, 'foo', default=True)
parser.parse_args([]) # Whatever the default was
parser.parse_args(['--foo']) # True
parser.parse_args(['--nofoo']) # False
parser.parse_args(['--foo=true']) # True
parser.parse_args(['--foo=false']) # False
parser.parse_args(['--foo', '--nofoo']) # Error
Le code:
def _str_to_bool(s):
"""Convert string to bool (in argparse context)."""
if s.lower() not in ['true', 'false']:
raise ValueError('Need bool; got %r' % s)
return {'true': True, 'false': False}[s.lower()]
def add_boolean_argument(parser, name, default=False):
"""Add a boolean argument to an ArgumentParser instance."""
group = parser.add_mutually_exclusive_group()
group.add_argument(
'--' + name, nargs='?', default=default, const=True, type=_str_to_bool)
group.add_argument('--no' + name, dest=name, action='store_false')
Un moyen plus simple serait d'utiliser comme ci-dessous.
parser.add_argument('--feature', type=lambda s: s.lower() in ['true', 't', 'yes', '1'])
class FlagAction(argparse.Action):
# From http://bugs.python.org/issue8538
def __init__(self, option_strings, dest, default=None,
required=False, help=None, metavar=None,
positive_prefixes=['--'], negative_prefixes=['--no-']):
self.positive_strings = set()
self.negative_strings = set()
for string in option_strings:
assert re.match(r'--[A-z]+', string)
suffix = string[2:]
for positive_prefix in positive_prefixes:
self.positive_strings.add(positive_prefix + suffix)
for negative_prefix in negative_prefixes:
self.negative_strings.add(negative_prefix + suffix)
strings = list(self.positive_strings | self.negative_strings)
super(FlagAction, self).__init__(option_strings=strings, dest=dest,
nargs=0, const=None, default=default, type=bool, choices=None,
required=required, help=help, metavar=metavar)
def __call__(self, parser, namespace, values, option_string=None):
if option_string in self.positive_strings:
setattr(namespace, self.dest, True)
else:
setattr(namespace, self.dest, False)
Le moyen le plus simple serait d'utiliser choix:
parser = argparse.ArgumentParser()
parser.add_argument('--my-flag',choices=('True','False'))
args = parser.parse_args()
flag = args.my_flag == 'True'
print(flag)
Ne pas transmettre --my-flag est évalué à False. L'option required = True peut être ajoutée si vous souhaitez toujours que l'utilisateur spécifie explicitement un choix.
Je pense que la manière la plus canonique sera:
parser.add_argument('--ensure', nargs='*', default=None)
ENSURE = config.ensure is None