Search code examples
pythonoptparse

Optparse callback not consuming argument


I'm trying to get to know optparse a bit better, but I'm struggling to understand why the following code behaves the way it does. Am I doing something stupid?

import optparse

def store_test(option, opt_str, value, parser, args=None, kwargs=None):
    print 'opt_str:', opt_str
    print 'value:', value

op = optparse.OptionParser()
op.add_option('-t', '--test', action='callback', callback=store_test, default='test', 
    dest='test', help='test!')

(opts, args) = op.parse_args(['test.py', '-t', 'foo'])

print
print 'opts:'
print opts
print 'args:'
print args

Output:

opt_str: -t
value: None

opts:
{'test': 'test'}
args:
['foo']

Why is 'foo' not being passed to store_test() and instead being interpreted as an extra argument? Is there something wrong with op.parse_args(['-t', 'foo'])?

http://codepad.org/vq3cvE13

Edit:

Here's the example from the docs:

def store_value(option, opt_str, value, parser):
    setattr(parser.values, option.dest, value)
[...]
parser.add_option("--foo",
                  action="callback", callback=store_value,
                  type="int", nargs=3, dest="foo")

Solution

  • You're missing a "type" or "nargs" option attribute:

    op.add_option('-t', '--test', action='callback', callback=store_test, default='test',
        dest='test', help='test!', type='str')
    

    This option will cause it to consume the next argument.

    Reference: http://docs.python.org/library/optparse.html#optparse-option-callbacks

    type
    has its usual meaning: as with the "store" or "append" actions, it instructs optparse to consume one argument and convert it to type. Rather than storing the converted value(s) anywhere, though, optparse passes it to your callback function.

    nargs
    also has its usual meaning: if it is supplied and > 1, optparse will consume nargs arguments, each of which must be convertible to type. It then passes a tuple of converted values to your callback.

    This seems to be the relevant code from optparse.py:

    def takes_value(self):
        return self.type is not None
    
    def _process_short_opts(self, rargs, values):
        [...]
            if option.takes_value():
                # Any characters left in arg?  Pretend they're the
                # next arg, and stop consuming characters of arg.
                if i < len(arg):
                    rargs.insert(0, arg[i:])
                    stop = True
    
                nargs = option.nargs
                if len(rargs) < nargs:
                    if nargs == 1:
                        self.error(_("%s option requires an argument") % opt)
                    else:
                        self.error(_("%s option requires %d arguments")
                                   % (opt, nargs))
                elif nargs == 1:
                    value = rargs.pop(0)
                else:
                    value = tuple(rargs[0:nargs])
                    del rargs[0:nargs]
    
            else:                       # option doesn't take a value
                value = None
    
            option.process(opt, value, values, self)