Back to index

python-cliapp  1.20120630
genman.py
Go to the documentation of this file.
00001 # Copyright (C) 2011  Lars Wirzenius
00002 # 
00003 # This program is free software; you can redistribute it and/or modify
00004 # it under the terms of the GNU General Public License as published by
00005 # the Free Software Foundation; either version 2 of the License, or
00006 # (at your option) any later version.
00007 # 
00008 # This program is distributed in the hope that it will be useful,
00009 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00010 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011 # GNU General Public License for more details.
00012 # 
00013 # You should have received a copy of the GNU General Public License along
00014 # with this program; if not, write to the Free Software Foundation, Inc.,
00015 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00016 
00017 
00018 import optparse
00019 import re
00020 
00021 
00022 class ManpageGenerator(object):
00023 
00024     '''Fill in a manual page template from an OptionParser instance.'''
00025     
00026     def __init__(self, template, parser, arg_synopsis, cmd_synopsis):
00027         self.template = template
00028         self.parser = parser
00029         self.arg_synopsis = arg_synopsis
00030         self.cmd_synopsis = cmd_synopsis
00031         
00032     @property
00033     def options(self):
00034         return sorted(self.parser.option_list,
00035                       key=lambda o: (o._long_opts + o._short_opts)[0])
00036                       
00037     def format_template(self):
00038         sections = (('SYNOPSIS', self.format_synopsis()),
00039                     ('OPTIONS', self.format_options()))
00040         text = self.template
00041         for section, contents in sections:
00042             pattern = '\n.SH %s\n' % section
00043             text = text.replace(pattern, pattern + contents)
00044         return text
00045                       
00046     def format_synopsis(self):
00047         lines = []
00048         lines += ['.nh']
00049         lines += ['.B %s' % self.esc_dashes(self.parser.prog)]
00050         
00051         for option in self.options:
00052             for spec in self.format_option_for_synopsis(option):
00053                 lines += ['.RB [ %s ]' % spec]
00054 
00055         if self.cmd_synopsis:
00056             lines += ['.PP']
00057             for cmd in sorted(self.cmd_synopsis):
00058                 lines += ['.br',
00059                           '.B %s' % self.esc_dashes(self.parser.prog),
00060                           '.RI [ options ]',
00061                           self.esc_dashes(cmd)]
00062                 lines += self.format_argspec(self.cmd_synopsis[cmd])
00063         elif self.arg_synopsis:
00064             lines += self.format_argspec(self.arg_synopsis)
00065 
00066         lines += ['.hy']
00067         return ''.join('%s\n' % line for line in lines)
00068                       
00069     def format_option_for_synopsis(self, option):
00070         if option.metavar:
00071             suffix = '\\fR=\\fI%s' % self.esc_dashes(option.metavar)
00072         else:
00073             suffix = ''
00074         for name in option._short_opts + option._long_opts:
00075             yield '%s%s' % (self.esc_dashes(name), suffix)
00076 
00077     def format_options(self):
00078         return ''.join(self.format_option_for_options(option)
00079                        for option in self.options)
00080 
00081     def format_option_for_options(self, option):
00082         lines = []
00083         lines += ['.TP']
00084         shorts = [self.esc_dashes(x) for x in option._short_opts]
00085         if option.metavar:
00086             longs = ['%s =\\fI%s' % (self.esc_dashes(x), option.metavar)
00087                      for x in option._long_opts]
00088         else:
00089             longs = ['%s' % self.esc_dashes(x)
00090                      for x in option._long_opts]
00091         lines += ['.BR ' + ' ", " '.join(shorts + longs)]
00092         lines += [self.esc_dots(self.expand_default(option).strip())]
00093         return ''.join('%s\n' % line for line in lines)
00094         
00095     def expand_default(self, option):
00096         default = self.parser.defaults.get(option.dest)
00097         if default is optparse.NO_DEFAULT or default is None:
00098             default = 'none'
00099         else:
00100             default = str(default)
00101         return option.help.replace('%default', default)
00102         
00103     def esc_dashes(self, optname):
00104         return '\\-'.join(optname.split('-'))
00105     
00106     def esc_dots(self, line):
00107         if line.startswith('.'):
00108             return '\\' + line
00109         else:
00110             return line
00111             
00112     def format_argspec(self, argspec):
00113         roman = re.compile(r'[^A-Z]+')
00114         italic = re.compile(r'[A-Z]+')
00115         words = ['.RI']
00116         while argspec:
00117             m = roman.match(argspec)
00118             if m:
00119                 words += [self.esc_dashes(m.group(0))]
00120                 argspec = argspec[m.end():]
00121             else:
00122                 words += ['""']
00123             m = italic.match(argspec)
00124             if m:
00125                 words += [self.esc_dashes(m.group(0))]
00126                 argspec = argspec[m.end():]
00127             else:
00128                 words += ['""']
00129         return [' '.join(words)]
00130