How to Set a Default Subparser using Argparse Module with Python 2.7

前端 未结 2 2014

I\'m using Python 2.7 and I\'m trying to accomplish a shell like behavior using argparse. My issue, in general, that I cannot seem to find a way, in Python 2.7, to use argpa

相关标签:
2条回答
  • 2020-12-10 23:35

    Another idea is to use a 2 stage parsing. One handles 'globals', returning strings it can't handle. Then conditionally handle the extras with subparsers.

    import argparse
    
    def cmd1(args):
        print('cmd1', args)
    def cmd2(args):
        print('cmd2', args)
    
    parser1 = argparse.ArgumentParser()
    
    parser1.add_argument("-i", "--info",  help="Display more information")
    
    parser2 = argparse.ArgumentParser()
    subparsers = parser2.add_subparsers(dest='cmd')
    
    parserCmd1 = subparsers.add_parser("cmd1", help="First Command")
    parserCmd1.set_defaults(func=cmd1)
    
    parserCmd2 = subparsers.add_parser("cmd2", help="Second Command")
    parserCmd2.add_argument("-o", "--output", help="Redirect Output")
    parserCmd2.set_defaults(func=cmd2)
    
    args, extras = parser1.parse_known_args()
    if len(extras)>0 and extras[0] in ['cmd1','cmd2']:
        args = parser2.parse_args(extras, namespace=args)
        args.func(args)
    else:
        print('doing system with', args, extras)
    

    sample runs:

    0901:~/mypy$ python stack46667843.py -i info
    ('doing system with', Namespace(info='info'), [])
    0901:~/mypy$ python stack46667843.py -i info extras for sys
    ('doing system with', Namespace(info='info'), ['extras', 'for', 'sys'])
    0901:~/mypy$ python stack46667843.py -i info cmd1
    ('cmd1', Namespace(cmd='cmd1', func=<function cmd1 at 0xb74b025c>, info='info'))
    0901:~/mypy$ python stack46667843.py -i info cmd2 -o out
    ('cmd2', Namespace(cmd='cmd2', func=<function cmd2 at 0xb719ebc4>, info='info', output='out'))
    0901:~/mypy$ 
    
    0 讨论(0)
  • 2020-12-10 23:44

    A bug/issue (with links) on the topic of 'optional' subparsers.

    https://bugs.python.org/issue29298

    Notice that this has a recent pull request.


    With your script and the addition of

    args = parser.parse_args()
    print(args)
    

    results are

    1008:~/mypy$ python3 stack46667843.py 
    Namespace(info=None)
    1009:~/mypy$ python2 stack46667843.py 
    usage: stack46667843.py [-h] [-i INFO] {cmd1,cmd2} ...
    stack46667843.py: error: too few arguments
    1009:~/mypy$ python2 stack46667843.py cmd1
    Namespace(func=<function cmd1 at 0xb748825c>, info=None)
    1011:~/mypy$ python3 stack46667843.py cmd1
    Namespace(func=<function cmd1 at 0xb7134dac>, info=None)
    

    I thought the 'optional' subparsers affected both Py2 and 3 versions, but apparently it doesn't. I'll have to look at the code to verify why.


    In both languages, subparsers.required is False. If I set it to true

    subparsers.required=True
    

    (and add a dest to the subparsers definition), the PY3 error message is

    1031:~/mypy$ python3 stack46667843.py
    usage: stack46667843.py [-h] [-i INFO] {cmd1,cmd2} ...
    stack46667843.py: error: the following arguments are required: cmd
    

    So there's a difference in how the 2 versions test for required arguments. Py3 pays attention to the required attribute; Py2 (apparently) uses the earlier method of checking whether the positionals list is empty or not.


    Checking for required arguments occurs near the end of parser._parse_known_args.

    Python2.7 includes

        # if we didn't use all the Positional objects, there were too few
        # arg strings supplied.
        if positionals:
            self.error(_('too few arguments'))
    

    before the iteration that checks action.required. That's what's catching the missing cmd and saying too few arguments.

    So a kludge is to edit your argparse.py and remove that block so it matches the corresponding section of the Py3 version.

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