Optparse object containing generators: only seem to work if given particular names?

Peter Otten __peter__ at web.de
Fri Oct 31 09:39:23 CET 2008


John O'Hagan wrote:

> Here's a strange one for you:
> 
> I have a generator function which produces lists of numbers and takes
> options which influence the output. The generator contains a loop, and to
> enable the options to have a different value on each iteration, the
> options may themselves be instances of the same generator, in the form of
> attributes of an optparse object. In the body of the loop, the next()
> method is called on each generator to get the new value each time.
> 
> This enables me to either:
> 
> - pass an option either a single value as  and have it yielded repeatedly
> by a dummy generator,
> 
> - or, by using the (arbitrarily chosen) word "CTRL:" after the option,
> followed by another set of options - in quotes - suitable for the main
> generator, to pass a changing value.
> 
> The problem: whether it works or not depends on what name is given to the
> destination in parser.values()! Strange but true.
> 
> I haven't yet been able to figure out if there's any pattern to this, but
> names that work include: "add", "A", "rand", and "updown". Some that don't
> are "descend", "random", and "Z".
> 
> When it doesn't work, the error is:
> 
> TypeError: unsupported operand type(s) for +: 'int' and 'generator'
> 
> So apparently when given one of the "wrong" names, calling options.
> [wrongname].next() yields another generator object, whereas with the
> ["right"
> names, it yields the expected value.
> 
>  Below is a working "sandbox" version of the relevant part of the actual
> program, but with a hard-coded value for the number list and only a single
> option, --add, which either adds the number given next on the command line
> to each element of the list, or, if followed by CTRL: '' " (that's a pair
> of empty quotes) will sequentially do the same for each member of the
> list. (If the option is repeated inside the quotes, the altered list will
> be used to apply the addition, and so on.)
> 
> If you don't believe me (and I'm finding this hard to believe myself), try
> some different names in the two inline-commented places (of course, both
> have to be the same). Some will work, others won't.
> 
> I'm baffled, and hope someone here may be interested in figuring out why
> this is happening.
> 
> Thanks,
> 
> John O'Hagan
> 
> Here's the code:
> 
> import sys
> from optparse import OptionParser
> 
> def my_callback(option, opt_str, value, parser):
> 
> rargs = parser.rargs
> 
> if rargs[0] == "CTRL:" :
> setattr(parser.values, option.dest+"_ctrl", rargs[1].split())
> else:
> value = abs(int(rargs[0]))
> setattr(parser.values, option.dest, value)
> 
> def parse(option_list):
> 
> parser = OptionParser()
> 
> parser.add_option("--add", action="callback", callback=my_callback,
> dest="add")  ## The troublesome name!
> 
> (options, args) = parser.parse_args(option_list)
> return options
> 
> 
> def numerical_ctrl(generator):
> 
> for number_list in generator:
> for number in number_list:
> yield number
> 
> def dummy_ctrl(value):
> while 1:
> yield value
> 
> #This next bit should ideally be incorporated into the callback function
> #above (later!)
> #It converts the attributes of the "options" object to generators.
> 
> def iterizer( options ):
> 
> for opt in vars( options )  :
> 
> if "_ctrl" in opt:
> ctrl_opts_list = getattr( options, opt )
> setattr(options, opt, dummy_ctrl( getattr( options, opt ) ) )
> opt = opt.replace( "_ctrl", "" )
> ctrl_opts = parse( ctrl_opts_list )
> ctrl_opts = iterizer( ctrl_opts )
> generator = phrase_engine( ctrl_opts )
> generator = numerical_ctrl( generator )
> else:
> generator = dummy_ctrl( getattr( options, opt ) )
> 
> setattr (options, opt , generator)
> 
> return options
> 
> #The number list generator:
> 
> def phrase_engine(options):
> 
> counter = 0
> while counter < 10:
> 
> sequence = [1, 2, 3]
> 
> add = options.add.next() ##Here is the troublesome name again.
> 
> if add :
> sequence = [ i + add for i in sequence ]
> 
> yield sequence
> 
> counter += 1
> 
> options = parse( sys.argv[ 1: ] )
> options = iterizer(options)
> 
> for i in phrase_engine(options):
> print  i

Just a quick note: Your iterizer() implementation seems to depend on xxx
occuring before xxx_ctrl. That's only the case for some values:

>>> dict(add_ctrl=1, add=2)
{'add': 2, 'add_ctrl': 1}
>>> dict(random_ctrl=1, random=2)
{'random_ctrl': 1, 'random': 2}

While you can enforce that with sorted(vars(options)) I suggest that you try
to simplify your code a bit -- you know, debugging being twice as hard as
coding...

Peter



More information about the Python-list mailing list