Pergunta sobre argparse, arguments, parameters, python – python, argparse: permite o parâmetro de entrada quando outro foi especificado

12

No meu script python, quero poder usar um parâmetro de entrada opcionalsó quando outro parâmetro opcional foi especificado. Exemplo:

$ python myScript.py --parameter1 value1
$ python myScript.py --parameter1 value1 --parameter2 value2

Mas não:

$ python myScript.py --parameter2 value2

Como faço isso com argparse?

Obrigado!

bugs.python.org/issue11588 é uma solicitação de bug para ummutually_inclusive_group característica. Isso não funcionaria muito nesse caso, já que--parameter1 pode ocorrer sem--parameter2. Contribuir para esse problema se você tiver ideias de como essa ou outras restrições relacionadas podem ser implementadas. hpaulj
O que você quer que aconteça semyScript.py --parameter2 value2 --parameter1 value1? Este é um caso muito difícil de lidar se você quer que ele passe. mgilson

Sua resposta

3   a resposta
10

Use uma ação personalizada:

import argparse

foo_default=None    

class BarAction(argparse.Action):
    def __call__(self,parser,namespace,values,option_string=None):
        didfoo=getattr(namespace,'foo',foo_default)
        if(didfoo == foo_default):
            parser.error( "foo before bar!")
        else:
            setattr(namespace,self.dest,values)

parser=argparse.ArgumentParser()
parser.add_argument('--foo',default=foo_default)
parser.add_argument('--bar',action=BarAction,help="Only use this if --foo is set")

#testing.
print parser.parse_args('--foo baz'.split())
print parser.parse_args('--foo baz --bar cat'.split())
print parser.parse_args('--bar dog'.split())

Isso pode até ser feito de maneira um pouco mais fácil de manter, se você estiver certo em confiar em algum comportamento não documentado de argparse:

import argparse

parser=argparse.ArgumentParser()
first_action=parser.add_argument('--foo',dest='cat',default=None)

class BarAction(argparse.Action):
    def __call__(self,parser,namespace,values,option_string=None):
        didfoo=getattr(namespace,first_action.dest,first_action.default)
        if(didfoo == first_action.default):
            parser.error( "foo before bar!")
        else:
            setattr(namespace,self.dest,values)

parser.add_argument('--bar',action=BarAction,
                    help="Only use this if --foo is set")

#testing.
print parser.parse_args('--foo baz'.split())
print parser.parse_args('--foo baz --bar cat'.split())
print parser.parse_args('--bar dog'.split())

Neste exemplo, obtemos o padrão parafoo e é o destino do objeto de ação retornado poradd_argument (o valor de retorno do add_argument não está documentado em nenhum lugar que eu possa encontrar). Isso ainda é um pouco frágil (se você quiser especificartype= palavra-chave para o--foo argumento por exemplo).

Finalmente, você pode verificarsys.argv antes de analisar.

import sys
if ("--parameter2" in sys.argv) and ("--parameter1" not in sys.argv):
    parser.error("parameter1 must be given if parameter2 is given")

Isso fica um pouco mais complicado se--parameter1 também poderia ser desencadeada por--p1, mas você entendeu a ideia. Então você poderia usar

if (set(sys.argv).intersection(('--p2',...)) and 
    not set(sys.argv).intersection(('--p1',...)))

A vantagem aqui é que não requer nenhuma ordem em particular. (--p2 não precisa seguir--p1 na linha de comando). E, como antes, você pode obter a lista de seqüências de comando que acionará sua ação específica por meio dooption_strings atributo retornado porparser.add_argument(...). por exemplo.

import argparse
import sys   
parser=argparse.ArgumentParser()
action1=parser.add_argument('--foo')
action2=parser.add_argument('--bar',
                            help="Only use this if --foo is set")

argv=set(sys.argv)
if (( argv & set(action2.option_strings) ) and 
      not ( argv & set(action1.option_strings) )):
                #^ set intersection
     parser.error(' or '.join(action1.option_strings)+
                  ' must be given with '+
                  ' or '.join(action2.option_strings))
2

Uma ideia seria verificar manualmente a saída da chamadaparser.parse_args() e levantar uma exceção ou falhar de outra forma se--parameter2 é usado, mas--parameter1 não é.

Você também pode tentar definir umação personalizada (role um pouco para baixo) para--parameter2 verifique se--parameter1 existe dentro do namespace.

Finalmente, você poderia tentar usarparágrafos, embora eu não saiba se vai permitir que você especifique um valor para--parameter1.

2

Você pode usarparse_known_args () para verificar se--parameter1 foi dado antes de adicionar--parameter2 para o analisador.

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--parameter1', dest='p1')
args = parser.parse_known_args()[0]
if args.p1:
  parser.add_argument('--parameter2', dest='p2')
args = parser.parse_args()

if args.p1:
  print args.p1
if args.p2:
  print args.p2

Resultados:

$ python myScript.py --parameter1 a
a
$ python myScript.py --parameter1 a --parameter2 b
a
b
$ python myScript.py --parameter2 b
usage: myScript.py [-h] [--parameter1 P1]
myScript.py: error: unrecognized arguments: --parameter2 b
--help vai ser impreciso embora. pmav99

Perguntas relacionadas