Python argparse: command-line argument that can be

2020-03-01 08:42发布

I am trying to make a Python program that uses the argparse module to parse command-line options.

I want to make an optional argument that can either be named or positional. For example, I want myScript --username=batman to do the same thing as myScript batman. I also want myScript without a username to be valid. Is this possible? If so, how can it be done?

I tried various things similar to the code below without any success.

parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument("-u", "--user-name", default="admin")
group.add_argument("user-name", default="admin")
args = parser.parse_args()

EDIT: The above code throws an exception saying ValueError: mutually exclusive arguments must be optional.

I am using Python 2.7.2 on OS X 10.8.4.

EDIT: I tried Gabriel Jacobsohn's suggestion but I couldn't get it working correctly in all cases.

I tried this:

parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument("-u", "--user-name", default="admin", nargs="?")
group.add_argument("user_name", default="admin", nargs="?")
args = parser.parse_args()
print(args)

and running myScript batman would print Namespace(user_name='batman'), but myScript -u batman and myScript --user-name=batman would print Namespace(user_name='admin').

I tried changing the name user-name to user_name in the 1st add_argument line and that sometimes resulted in both batman and admin in the namespace or an error, depending on how I ran the program.

I tried changing the name user_name to user-name in the 2nd add_argument line but that would print either Namespace(user-name='batman', user_name='admin') or Namespace(user-name='admin', user_name='batman'), depending on how I ran the program.

3条回答
beautiful°
2楼-- · 2020-03-01 09:09

The way the ArgumentParser works, it always checks for any trailing positional arguments after it has parsed the optional arguments. So if you have a positional argument with the same name as an optional argument, and it doesn't appear anywhere on the command line, it's guaranteed to override the optional argument (either with its default value or None).

Frankly this seems like a bug to me, at least when used in a mutually exclusive group, since if you had specified the parameter explicitly it would have been an error.

That said, my suggested solution, is to give the postional argument a different name.

parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument('-u','--username')
group.add_argument('static_username',nargs='?',default='admin')

Then when parsing, you use the optional username if present, otherwise fallback to the positional static_username.

results = parser.parse_args()
username = results.username or results.static_username

I realise this isn't a particularly neat solution, but I don't think any of the answers will be.

查看更多
孤傲高冷的网名
3楼-- · 2020-03-01 09:11

Here is a solution that I think does everything you want:

import argparse
if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument("-u", "--user-name", default="admin")
    # Gather all extra args into a list named "args.extra"
    parser.add_argument("extra", nargs='*')
    args = parser.parse_args()
    # Set args.user_name to first extra arg if it is not given and len(args.extra) > 0
    if args.user_name == parser.get_default("user_name") and args.extra:
        args.user_name = args.extra.pop(0)
    print args

If you run myScript -u batman or myScript --user-name=batman, args.user_name is set to 'batman'. If you do myScript batman, args.user_name is again set to 'batman'. And finally, if you just do myScript, args.user_name is set to 'admin'.

Also, as an added bonus, you now have all of the extra arguments that were passed to the script stored in args.extra. Hope this helps.

查看更多
做自己的国王
4楼-- · 2020-03-01 09:20

Try to use the "nargs" parameter of the add_argument methode. This way it works for me. Now you can add the username twice, so you have to check it and raise an error, if you want.

import argparse
if __name__ == '__main__':
   parser = argparse.ArgumentParser()
   parser.add_argument("-u", "--user-name", default="admin")
   parser.add_argument("user_name", default="admin", nargs="?")
   args = parser.parse_args()
   print(args)
查看更多
登录 后发表回答