Domanda

I'm trying to do a very simple thing - have only two options: -p and -c, mutually exclusive and may or may not have an argument, do th...is with optional argument or do th...at without one. This is what I got:

def main():

    import argparse
    parser = argparse.ArgumentParser(description = '''ArgParser with optional argument''',
                                     argument_default = argparse.SUPPRESS)

    group1 = parser.add_mutually_exclusive_group(required=True)

    group1.add_argument('-p', '--project', dest='proj',
                        nargs='?', const='all', type=str,
                        help='list of project(s)')
    group1.add_argument('-c', '--component', dest='comp',
                        nargs='?', const='all', type=str,
                        help='list of Component(s)')

    args = parser.parse_args()
    print(args)

    if args.proj:
        outString = 'project/'+args.proj if args.proj is not 'all' else 'projects'
    elif args.comp:
        outString = 'component/'+args.comp if args.comp is not 'all' else 'components'
    else: pass

    print('OutPutString: '+outString)

if __name__ == "__main__":

    import sys
    try: sys.exit(main())
    except KeyboardInterrupt: pass
    finally: print

which works just fine for the first condition in the if-elif-else block, so it works:

dass@mon105:~$ ./argParseOpts.py -p 
Namespace(proj='all')
OutPutString: projects
#
dass@mon105:~$ ./argParseOpts.py -p testing
Namespace(proj='testing')
OutPutString: project/testing

but not if -c is used instead of -p (i.e. the 2nd condition):

dass@mon105:~$ ./argParseOpts.py -c 
Namespace(comp='all')

Traceback (most recent call last):
  File "./argParseOpts.py", line 32, in <module>
    try: sys.exit(main())
  File "./argParseOpts.py", line 21, in main
    if args.proj:
AttributeError: 'Namespace' object has no attribute 'proj'

Can anyone tell me if I'm missing anything or doing anything fundamentally wrong? Using v2.7.2, if that matters. Cheers!!

È stato utile?

Soluzione

No attributes are set because you set the argument_default value to argparse.SUPPRESS, which means that attributes are never set unless they have an explicit value.

Set can set an explicit default=None value per argument to gain the attributes again for these specific options:

group1.add_argument('-p', '--project', dest='proj',
                    nargs='?', const='all', type=str,
                    help='list of project(s)', default=None)
group1.add_argument('-c', '--component', dest='comp',
                    nargs='?', const='all', type=str,
                    help='list of Component(s)', default=None)

and the args.proj and args.comp attributes will once more always be set. None is a false value, so the if args.proj test will not pass moving to the other branch, for example.

The alternative is to use hasattr() to see if the attribute is set:

if hasattr(args, 'proj'):

Altri suggerimenti

Since the issue is already answered using Argparse, I'd recommend using docopt as an alternative. Write the interface help message in standard form and thats all you need to do. If I understand your usage correctly it would be like so.

Also the key to fix your issue in this case is to make -p and -c flags and use a different variable NAME for the string.

"""Usage:
    myprog (-p [NAME] | -c [NAME])

Options:
    -p, --project        list of project(s)
    -c, --component      list of component(s)
"""
from docopt import docopt

if __name__ == "__main__":
    arguments = docopt(__doc__)
    print(arguments)

    name = 'all' if arguments['NAME'] is None else arguments['NAME']
    if arguments['--project']:
        ...
    elif arguments['--component']:
        ...

If we just print the arguments, the usage patterns will look something like this.

$ python myprog.py
Usage:
...

$ python myprog.py -p
{'--project': True,
 '--component': False,
 'NAME': None}

$ python myprog.py -c foo
{'--project': False,
 '--component': True,
 'NAME': 'foo'}

$ python myprog.py -p foo -c bar
Usage:
...

The cause of the error is the SUPPRESS

parser = argparse.ArgumentParser(description = '''ArgParser with optional argument''',
                                 argument_default = argparse.SUPPRESS)

With this option, if an argument does not appear in the argv, it does not appear in the Namespace.

dass@mon105:~$ ./argParseOpts.py -c 
Namespace(comp='all')

Traceback (most recent call last):
  File "./argParseOpts.py", line 32, in <module>
    try: sys.exit(main())
  File "./argParseOpts.py", line 21, in main
    if args.proj:
AttributeError: 'Namespace' object has no attribute 'proj'

When you gave it -p, args.comp would have returned the same error (except you checked proj first).

I like to test issues like this in an interactive shell (e.g. IPython). There I can enter your parser definition, and issue commands like:

In [35]: parser.parse_args(['-p'])
Out[35]: Namespace(proj='all')

In [36]: parser.parse_args(['-c'])
Out[36]: Namespace(comp='all')

In [37]: parser.parse_args(['-c','test'])
Out[37]: Namespace(comp='test')

where it becomes obvious that only one of the attributes is set.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top