0

I have a utility which allows a user to read their ~/.aws/credentials file and export environment variables.

Currently, the CLI interface looks like this:

usage: aws-env [-h] [-n] profile

Extract AWS credentials for a given profile as environment variables.

positional arguments:
  profile          The profile in ~/.aws/credentials to extract credentials
                   for.

optional arguments:
  -h, --help       show this help message and exit
  -n, --no-export  Do not use export on the variables.

What I'd like to do here is provide a ls subparser that will allow the user to list the valid profile names in their ~/.aws/credentials.

The interface would be something like this:

$ aws-env ls
profile-1
profile-2

...etcetera. Is there a way that I can natively do this in argparse so that an option appears in my -h output which shows that ls is a valid command?

Naftuli Kay
  • 87,710
  • 93
  • 269
  • 411
  • Have you actually tried adding a subparser? https://docs.python.org/3/library/argparse.html#sub-commands – jonrsharpe Apr 03 '17 at 19:38
  • I need a single subparser for the `ls` command and a generic subparser for matching a single profile name which could be anything. Is that possible? – Naftuli Kay Apr 03 '17 at 19:43
  • I'm not sure I'm following is `ls` the sub command with the result being the output `profile-1\nprofile-2` or you want `profile-X` to part of the arg parser? If the latter, given you aren't giving an option for the credentials file, just read it and extract the profiles and add them to the parser. – AChampion Apr 03 '17 at 20:27
  • @jonsharpe - your SO link basically says, use caution when mixing a positional argument and subparsers. In my experience subparsers work best when all arguments for the main parser are flagged (optionals). – hpaulj Apr 03 '17 at 22:12

1 Answers1

2

If you go the subparsers route, you could define two parsers, 'ls' and 'extract'. 'ls' wouldn't have any arguments; 'extract' would take one positional, 'profile'.

subparsers are optional, (Argparse with required subparser), but 'profile', as currently defined, is required.

An alternative is to define two optionals, and omit the positional.

'-ls', True/False, if True to the list
'-e profile', if not None, do the extract.

Or you could leave the positional profile, but make it optional (nargs='?').

Another possibility is to look at the profile value after parsing. If it is a string like 'ls', then list instead of extract. This feels like the cleanest choice, however, the usage will not document this.


parser.add_argument('-l','--ls', action='store_true', help='list')
parser.add_argument('profile', nargs='?', help='The profile')

or

sp = parser.add_subparsers(dest='cmd')
sp.add_parser('ls')
sp1 = sp.add_parser('extract')
sp1.add_argument('profile', help='The profile')

A required mutually exclusive group

gp = parser.add_mutually_exclusive_group(required=True)
gp.add_argument('--ls', action='store_true', help='list')
gp.add_argument('profile', nargs='?', default='adefault', help='The profile')

produces:

usage: aws-env [-h] [-n] (--ls | profile)
Community
  • 1
  • 1
hpaulj
  • 221,503
  • 14
  • 230
  • 353