python, argparse: enable input parameter when another one has been specified

前端 未结 3 1555
悲&欢浪女
悲&欢浪女 2021-01-04 08:34

In my python script, I want to be able to use an optional input parameter only when another optional parameter has been specified. Example:

$ python         


        
相关标签:
3条回答
  • 2021-01-04 08:59

    You can use parse_known_args() to check if --parameter1 was given before adding --parameter2 to the parser.

    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
    

    Results:

    $ 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
    
    0 讨论(0)
  • 2021-01-04 09:17

    Use a custom action:

    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())
    

    This can even be done in a little easier to maintain way if you're OK with relying on some undocumented behavior of 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())
    

    In this example, we get the default for foo and it's destination from the action object returned by add_argument (add_argument's return value isn't documented anywhere that I can find). This is still a little fragile (If you want to specify a type= keyword to the --foo argument for example).

    Finally, you can check sys.argv before parsing.

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

    This gets a little more tricky if --parameter1 could also be triggered by --p1, but you get the idea. Then you could use

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

    The advantage here is that it doesn't require any particular order. (--p2 doesn't need to follow --p1 on the commandline). And, as before, you can get the list of command strings that will trigger your particular action via the option_strings attribute returned by parser.add_argument(...). e.g.

    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))
    
    0 讨论(0)
  • 2021-01-04 09:20

    One idea would be to manually check the output of calling parser.parse_args() and raise an exception or otherwise fail if --parameter2 is used but --parameter1 isn't.

    You could also try setting a custom action (scroll down a bit) to --parameter2 check if --parameter1 exists inside the namespace.

    Finally, you could try using subparsers, although I don't know if it'll let you specify a value for --parameter1.

    0 讨论(0)
提交回复
热议问题