Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using the argparse output to call functions

Currently my code looks like this. It allows me to parse multiple parameters my program script gets. Is there a different way that is closer to 'best practices'? I haven't seen code actually using the output of argparse, only how to set it up.

def useArguments():
    x = 0
    while x <= 5:
        if x == 0:                      
            if args.getweather != None:
                getWeather(args.getweather)
        if x == 1:
            if args.post != None:
                post(args.post)
        if x == 2:
            if args.custompost != None:
                custompost(args.custompost)
        if x == 3:
            if args.list != None:
                listAccounts(args.list)
        if x == 4:
            if args.add != None:
                addAccount(args.add[0])
        if x == 5:
            if args.edit != None:
                editAccount(args.edit[0])
        x = x + 1    


if __name__ == '__main__':

    updateConfig()

    parser = argparse.ArgumentParser(description='Post Yahoo weather to Twitter.', epilog="Report any bugs to [email protected]", prog='Program')

    parser.add_argument('-a', '--add', nargs=1, help='Add a new account. Use the desired account name as an argument.')
    parser.add_argument('-e', '--edit', nargs=1, choices=accountListSTR[:-1], help='Edit an account. Use the desired account name as an argument.')
    parser.add_argument('-g', '--getweather', nargs='*', choices=accountListSTR, help='Get weather and post here. Specify account(s) as argument. Use "all" for all accounts. If you specify multiple accounts, separate by a space NOT a comma.')
    parser.add_argument('-p', '--post', nargs='*', choices=accountListSTR, help='Post weather to Twitter. Specify account(s) as argument. Use "all" for all accounts. If you specify multiple accounts, separate by a space NOT a comma.')
    parser.add_argument('-c', '--custompost', nargs=2, help='Post a custom message. Specify an account then type the message. Make sure you use "" around the message. Use "all" for all accounts.')
    parser.add_argument('-l', '--list', action='store_const', const='all', help='List all accounts.')
    parser.add_argument('--version', action='version', version='%(prog)s 0.3.3')

    args = parser.parse_args()

    useArguments()
like image 585
avacariu Avatar asked Aug 11 '10 02:08

avacariu


3 Answers

You could supply a custom action for an argument by, and I quote:

passing an object that implements the Action API. The easiest way to do this is to extend argparse.Action, supplying an appropriate __call__ method. The __call__ method should accept four parameters:

  1. parser: The ArgumentParser object which contains this action.
  2. namespace: The namespace object that will be returned by parse_args(). Most actions add an attribute to this object.
  3. values: The associated command-line args, with any type-conversions applied.(Type-conversions are specified with the type keyword argument to add_argument().
  4. option_string: The option string that was used to invoke this action. The option_string argument is optional, and will be absent if the action is associated with a positional argument.
like image 164
Alex Martelli Avatar answered Oct 05 '22 14:10

Alex Martelli


See http://docs.python.org/library/argparse.html#sub-commands:

One particularly effective way of handling sub-commands is to combine the use of the add_subparsers() method with calls to set_defaults() so that each subparser knows which Python function it should execute.

In a nutshell:

parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()

weather_parser = subparsers.add_parser('get-weather')
weather_parser.add_argument('--bar')
weather_parser.set_defaults(function=get_weather)  # !

args = parser.parse_args(['get-weather', '--bar', 'quux'])
print args.function(args)

Here we create a subparser for the command get-weather and assign the function get_weather to it.

Note that the documentation says that the keyword/attribute is named func but it's definitely function as of argparse 1.1.

The resulting code is a bit too wordy so I've published a small package "argh" that makes things simpler, e.g.:

parser = argparse.ArgumentParser()
add_commands(parser, [get_weather])
print dispatch(parser, ['get-weather', '--bar', 'quux'])

"Argh" can do more but I'll let stack overflow answer that. :-)

like image 26
Andy Mikhaylenko Avatar answered Oct 05 '22 12:10

Andy Mikhaylenko


With the exception of --version, which is very commonly an option, the actions you've provided are better off treated as "subcommands".

I'm unaware of the argparse specifics, as I have yet to try Python 2.7, but you might take a look at the svn command as an example, here's some pseudocode for the command line:

myprog [--version] <command> [<command opts>...]

Where <command> in:

add|edit|getweather|post|custompost|list

And <command opts> are options specific to that command. Using optparse (which is similar), this would mean that your command would be returned in args, when calling parse_args, allowing you to do something like this:

opts, args = parser.parse_args()
if opts.version:
    ...
else:
    getattr("do_" + args[0])(*args[1:])

I find this pattern particularly useful for debugging, where I'd provide access to internal functions from the command line, and pass various arguments for testing. Adjust the selection of the command handler as appropriate for your own project.

like image 34
Matt Joiner Avatar answered Oct 05 '22 14:10

Matt Joiner