views:

136

answers:

2

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()
+2  A: 

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.

Matt Joiner
`argparse` actually provides support for precisely this pattern. (`optparse` doesn't.) I'm still stuck on Python 2.6 myself so I don't know the specifics offhand either, but they're explained in the [documentation](http://docs.python.org/library/argparse.html#sub-commands).
David Zaslavsky
What would be the advantage of doing this instead of just removing the `--` from my arguments. I guess to the end user it'd end up looking the same, right? I guess having it like that would make more sense for the user as well b/c they're not really running the program with custom parameters but rather telling it to do something.
vlad003
@vlad003: That's right. The user of the program must select one and only one of those "options", so really they're commands to your program. Those commands however, do potentially take arguments. An alternative is to write seperate executable scripts for each command, perform arg parsing separately in each one, and call a common code base for implementation. Eg your scripts might be called: myprog-list, myprog-add, etc. However I might add, as you are using argparse from Python-2.7, that there could be very fancy argument handling already incorporated into that.
Matt Joiner
+2  A: 

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.
Alex Martelli
In what situations would this be the best method? I can't see a use for all that extra code. But then again, I barely used Classes so I'm probably missing something.
vlad003
@vlad, it might be used to automatically call a function when an argument is supplied, which is what you're doing will all your boilerplate -- you'd just have to make the functions be the `__call__` methods of appropriate subclasses of `argparse.Action`. But if you don't "get" object-oriented programming, that's OK, you can do it your way (though that loop and `if x ==` checks are really redundant in any case - just do one after the other the checks for what arguments are present possibly follower by the appropriate calls, there's no added value in the other boilerplate you use).
Alex Martelli
Accepted this answer because it answers my question. I *might* end up trying this to learn how it works; but it will require many changes in how my code currently works (especially the functions listed there).Thanks!
vlad003