Optparse and help formatting?
Tim Chase
python.list at tim.thechases.com
Sun Sep 30 08:37:10 EDT 2007
>> I've been learning the ropes of the optparse module and have been
>> having some trouble getting the help to format the way I want.
>
> A quick perusal of the 'optparse.py' code shows me this:
>
> ========
> [...]
> class OptionParser([...]):
> def __init__([...],
> formatter=None,
> [...]):
> [...]
> if formatter is None:
> formatter = IndentedHelpFormatter()
> [...]
> ========
>
> So, the OptionParser init method accepts the help formatter as the
> 'formatter' argument, defaulting to a new instance of
> IndentedHelpFormatter.
>
> Presumably, it's a matter of subclassing 'optparse.HelpFormatter' and
> overriding the behaviour you want to change, then passing an instance
> of your new class as the 'formatter' argument to the 'OptionParser()'
> invocation.
Ben, thanks for pointing me in the right direction. Digging in
this code, it looks like textwrap.[fill|wrap] method calls were
what was eating my NL and munging my tabs.
While not a terribly elegant solution, as there's no easy way to
intercept the two bits I want without copying and pasting a large
bit of the format_description and format_option methods just to
change the behavior of one call to textwrap.*, I did manage to
get it working as desired.
In the event that anybody else needs the solution I hacked
together, I've pasted it below which, as Ben suggests, can be
used with
parser = OptionParser(...
formatter=IndentedHelpFormatterWithNL()
)
Hope this helps somebody else too.
-tim
class IndentedHelpFormatterWithNL(IndentedHelpFormatter):
def format_description(self, description):
if not description: return ""
desc_width = self.width - self.current_indent
indent = " "*self.current_indent
# the above is still the same
bits = description.split('\n')
formatted_bits = [
textwrap.fill(bit,
desc_width,
initial_indent=indent,
subsequent_indent=indent)
for bit in bits]
result = "\n".join(formatted_bits) + "\n"
return result
def format_option(self, option):
# The help for each option consists of two parts:
# * the opt strings and metavars
# eg. ("-x", or "-fFILENAME, --file=FILENAME")
# * the user-supplied help string
# eg. ("turn on expert mode", "read data from FILENAME")
#
# If possible, we write both of these on the same line:
# -x turn on expert mode
#
# But if the opt string list is too long, we put the help
# string on a second line, indented to the same column it would
# start in if it fit on the first line.
# -fFILENAME, --file=FILENAME
# read data from FILENAME
result = []
opts = self.option_strings[option]
opt_width = self.help_position - self.current_indent - 2
if len(opts) > opt_width:
opts = "%*s%s\n" % (self.current_indent, "", opts)
indent_first = self.help_position
else: # start help on same line as opts
opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts)
indent_first = 0
result.append(opts)
if option.help:
help_text = self.expand_default(option)
# Everything is the same up through here
help_lines = []
for para in help_text.split("\n"):
help_lines.extend(textwrap.wrap(para, self.help_width))
# Everything is the same after here
result.append("%*s%s\n" % (
indent_first, "", help_lines[0]))
result.extend(["%*s%s\n" % (self.help_position, "", line)
for line in help_lines[1:]])
elif opts[-1] != "\n":
result.append("\n")
return "".join(result)
More information about the Python-list
mailing list