J'ai une exigence comme suit:
./xyifier --prox --lport lport --rport rport
pour l'argument prox, j'utilise action = 'store_true' pour vérifier s'il est présent ou non. Je n'ai besoin d'aucun des arguments. Mais, si --prox est défini sur I require rport et lport également. Existe-t-il un moyen simple de faire cela avec argparse sans écrire de codage conditionnel personnalisé?.
Plus de code:
non_int.add_argument('--prox', action='store_true', help='Flag to turn on proxy')
non_int.add_argument('--lport', type=int, help='Listen Port.')
non_int.add_argument('--rport', type=int, help='Proxy port.')
Non, il n'y a aucune option dans argparse pour créer des ensembles d'options mutuellement inclusif.
Le moyen le plus simple de gérer cela serait:
if args.prox and (args.lport is None or args.rport is None):
parser.error("--prox requires --lport and --rport.")
Vous parlez d'avoir des arguments requis conditionnellement. Comme @borntyping, vous pouvez vérifier l’erreur et faire parser.error()
, ou vous pouvez simplement appliquer une exigence liée à --prox
lorsque vous ajoutez un nouvel argument.
Une solution simple pour votre exemple pourrait être:
non_int.add_argument('--prox', action='store_true', help='Flag to turn on proxy')
non_int.add_argument('--lport', required='--prox' in sys.argv, type=int)
non_int.add_argument('--rport', required='--prox' in sys.argv, type=int)
De cette façon, required
reçoit soit True
ou False
selon que l'utilisateur a utilisé tel que --prox
. Cela garantit également que -lport
et -rport
ont un comportement indépendant entre eux.
Que diriez-vous d’utiliser la méthode parser.parse_known_args()
puis d’ajouter la méthode --lport
et --rport
args comme argument requis si --prox
est présent.
# just add --prox arg now
non_int = argparse.ArgumentParser(description="stackoverflow question",
usage="%(prog)s [-h] [--prox --lport port --rport port]")
non_int.add_argument('--prox', action='store_true',
help='Flag to turn on proxy, requires additional args lport and rport')
opts, rem_args = non_int.parse_known_args()
if opts.prox:
non_int.add_argument('--lport', required=True, type=int, help='Listen Port.')
non_int.add_argument('--rport', required=True, type=int, help='Proxy port.')
# use options and namespace from first parsing
non_int.parse_args(rem_args, namespace = opts)
N'oubliez pas non plus que vous pouvez fournir l'espace de noms opts
généré après la première analyse, tout en analysant les arguments restants la deuxième fois. Ainsi, à la fin, une fois l’analyse terminée, vous aurez un seul espace de noms avec toutes les options.
Désavantages:
--prox
n'est pas présent, les deux autres options dépendantes ne sont même pas présentes dans l'espace de noms. Bien que basé sur votre cas d'utilisation, si --prox
n'est pas présent, ce qui arrive aux autres options est sans importance.--lport
et --rport
ne s'affiche pas dans le message d'aideUtilisez-vous lport
lorsque prox
n'est pas défini. Sinon, pourquoi ne pas créer lport
et rport
arguments de prox
? par exemple.
parser.add_argument('--prox', nargs=2, type=int, help='Prox: listen and proxy ports')
Cela évite à vos utilisateurs de taper. Il est tout aussi facile de tester if args.prox is not None:
comme if args.prox:
.
La réponse acceptée a très bien fonctionné pour moi! Puisque tout le code est cassé sans test, voici comment j'ai testé la réponse acceptée. parser.error()
ne déclenche pas un argparse.ArgumentError
_ error il quitte plutôt le processus. Vous devez tester SystemExit
.
avec pytest
import pytest
from . import parse_arguments # code that rasises parse.error()
def test_args_parsed_raises_error():
with pytest.raises(SystemExit):
parse_arguments(["argument that raises error"])
avec unittests
from unittest import TestCase
from . import parse_arguments # code that rasises parse.error()
class TestArgs(TestCase):
def test_args_parsed_raises_error():
with self.assertRaises(SystemExit) as cm:
parse_arguments(["argument that raises error"])
inspiré de: tilisation de unittest pour tester argparse - erreurs de sortie