6

I'm writing a little python script to get stats from several servers or a single server, and I'm using OptionParser to parse the command line input.

#!/usr/bin/python

import sys
from optparse import OptionParser
...
parser.add_option("-s", "--server", dest="server", metavar="SERVER", type="string", 
                  help="server(s) to gather stats [default: localhost]")
...

my GOAL is to be able to do something like

#test.py -s server1 -s server2

and it would append both of those values within the options.server object in some way so that I could iterate through them, whether they have 1 value or 10.

Any thoughts / help is appreciated. Thanks.

jduncan
  • 677
  • 2
  • 12
  • 22

5 Answers5

13
import optparse

parser = optparse.OptionParser()
parser.add_option('-t', '--test', action='append')

options, args = parser.parse_args()
for i, opt in enumerate(options.test):
    print 'option %s: %s' % (i, opt)
mg.
  • 7,822
  • 1
  • 26
  • 30
  • That's what I thought would work, as well. However I get this error: AttributeError: 'str' object has no attribute 'append' when I give a string option that action. – jduncan May 03 '10 at 11:14
  • @jduncan: Are you sure? I just tried it and again it works as expected. – mg. May 03 '10 at 12:04
  • This fails for me (Python 2.6) if I don't specify any options -- options.test is None and cannot be enumerated – Marius Gedminas May 03 '10 at 15:40
  • Other than that, this code works with Python 2.4, 2.5 and 2.6 – Marius Gedminas May 03 '10 at 15:41
  • @jduncan: I can reproduce the error if I do `parser.add_option(..., default='a_string', action='append')`, but that is incorrect. In fact, if you do that and not provide an option, you'll end up iterating over all the characters. Plus, specifying anything would only append, so you could never override the default server. Don't do it; if you need a default, do `enumerate(options.test or ['default'])`. – Marius Gedminas May 03 '10 at 15:44
  • 1
    Re: iteration, setting default=[] in the add_option call makes it work with no options or multiple options. – eternicode Mar 18 '11 at 15:17
3

Yes, it can be done with optparse.

This is an example:

./test.py --categories=aaa --categories=bbb --categories ccc arg1 arg2 arg3

which prints:

arguments: ['arg1', 'arg2', 'arg3']
options: {'categories': ['aaa', 'bbb', 'ccc']}

Full working example below:

#!/usr/bin/env python

import os, sys
from optparse import OptionParser
from optparse import Option, OptionValueError

VERSION = '0.9.4'

class MultipleOption(Option):
    ACTIONS = Option.ACTIONS + ("extend",)
    STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",)
    TYPED_ACTIONS = Option.TYPED_ACTIONS + ("extend",)
    ALWAYS_TYPED_ACTIONS = Option.ALWAYS_TYPED_ACTIONS + ("extend",)

    def take_action(self, action, dest, opt, value, values, parser):
        if action == "extend":
            values.ensure_value(dest, []).append(value)
        else:
            Option.take_action(self, action, dest, opt, value, values, parser)


def main():
    PROG = os.path.basename(os.path.splitext(__file__)[0])
    long_commands = ('categories')
    short_commands = {'cat':'categories'}
    description = """Just a test"""
    parser = OptionParser(option_class=MultipleOption,
                          usage='usage: %prog [OPTIONS] COMMAND [BLOG_FILE]',
                          version='%s %s' % (PROG, VERSION),
                          description=description)
    parser.add_option('-c', '--categories', 
                      action="extend", type="string",
                      dest='categories', 
                      metavar='CATEGORIES', 
                      help='comma separated list of post categories')

    if len(sys.argv) == 1:
        parser.parse_args(['--help'])

    OPTIONS, args = parser.parse_args()
    print "arguments:", args
    print "options:", OPTIONS

if __name__ == '__main__':
    main()

More information at http://docs.python.org/library/optparse.html#adding-new-actions

Richard Gomes
  • 5,675
  • 2
  • 44
  • 50
2

You could try to check out argparse. It provides "nargs" parameter meaning you would be able to do something along

#test.py -s server1 server2

I know that's not exactly what requested but it might be a decent compromise without too much hassle. :)

Juho Vepsäläinen
  • 26,573
  • 12
  • 79
  • 105
2

Could be easier to accept a comma-separated list of servers:

# test.py -s server1,server2

and split the value within your script.

harto
  • 89,823
  • 9
  • 47
  • 61
1

Here is a known working example (from http://docs.python.org/library/email-examples.html):

parser.add_option('-r', '--recipient',
                  type='string', action='append', metavar='RECIPIENT',
                  default=[], dest='recipients',
                  help='A To: header value (at least one required)')

I think the usages is something like:

python script.py -r some@where.com -r no@where.com -r any@where.com
jGc
  • 29
  • 2