build_manpage.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. # -*- coding: utf-8 -*-
  2. """build_manpage command -- Generate man page from setup()"""
  3. """python setup.py build_manpage --output=bla.txt
  4. --parser=scripts.pwman3:parser_optios
  5. """
  6. import datetime
  7. from distutils.command.build import build
  8. from distutils.core import Command
  9. from distutils.errors import DistutilsOptionError
  10. import optparse
  11. import argparse
  12. class build_manpage(Command):
  13. """-O bla.1 --parser pwman:parser_options"""
  14. description = 'Generate man page from setup().'
  15. user_options = [
  16. ('output=', 'O', 'output file'),
  17. ('parser=', None, 'module path to optparser (e.g. mymod:func'),
  18. ]
  19. def initialize_options(self):
  20. self.output = None
  21. self.parser = None
  22. def finalize_options(self):
  23. if self.output is None:
  24. raise DistutilsOptionError('\'output\' option is required')
  25. if self.parser is None:
  26. raise DistutilsOptionError('\'parser\' option is required')
  27. mod_name, func_name = self.parser.split(':')
  28. fromlist = mod_name.split('.')
  29. try:
  30. mod = __import__(mod_name, fromlist=fromlist)
  31. self._parser = getattr(mod, func_name)()
  32. except ImportError, err:
  33. raise
  34. self._parser.formatter = ManPageFormatter()
  35. self._parser.formatter.set_parser(self._parser)
  36. self.announce('Writing man page %s' % self.output)
  37. self._today = datetime.date.today()
  38. def _markup(self, txt):
  39. return txt.replace('-', '\\-')
  40. def _write_header(self):
  41. appname = self.distribution.get_name()
  42. ret = []
  43. ret.append('.TH %s 1 %s\n' % (self._markup(appname),
  44. self._today.strftime('%Y\\-%m\\-%d')))
  45. description = self.distribution.get_description()
  46. if description:
  47. name = self._markup('%s - %s' % (self._markup(appname),
  48. description.splitlines()[0]))
  49. else:
  50. name = self._markup(appname)
  51. ret.append('.SH NAME\n%s\n' % name)
  52. if isinstance(self._parser, argparse.ArgumentParser):
  53. self._parser.prog = self.distribution.get_name()
  54. synopsis = self._parser.format_usage().split(':',1)[1]
  55. else:
  56. synopsis = self._parser.get_usage()
  57. if synopsis:
  58. synopsis = synopsis.replace('%s ' % appname, '')
  59. ret.append('.SH SYNOPSIS\n.B %s\n%s\n' % (self._markup(appname),
  60. synopsis))
  61. long_desc = self.distribution.get_long_description()
  62. if long_desc:
  63. ret.append('.SH DESCRIPTION\n%s\n' % self._markup(long_desc))
  64. return ''.join(ret)
  65. def _write_options(self):
  66. ret = ['.SH OPTIONS\n']
  67. ret.append(self._parser.format_option_help())
  68. return ''.join(ret)
  69. def _write_footer(self):
  70. ret = []
  71. appname = self.distribution.get_name()
  72. author = '%s <%s>' % (self.distribution.get_author(),
  73. self.distribution.get_author_email())
  74. ret.append(('.SH AUTHORS\n.B %s\nwas written by %s.\n'
  75. % (self._markup(appname), self._markup(author))))
  76. homepage = self.distribution.get_url()
  77. ret.append(('.SH DISTRIBUTION\nThe latest version of %s may '
  78. 'be downloaded from\n'
  79. '.UR %s\n.UE\n'
  80. % (self._markup(appname), self._markup(homepage),)))
  81. return ''.join(ret)
  82. def run(self):
  83. manpage = []
  84. manpage.append(self._write_header())
  85. manpage.append(self._write_options())
  86. manpage.append(self._write_footer())
  87. stream = open(self.output, 'w')
  88. stream.write(''.join(manpage))
  89. stream.close()
  90. class ManPageArgFormatter(argparse.HelpFormatter):
  91. pass
  92. class ManPageFormatter(optparse.HelpFormatter):
  93. def __init__(self,
  94. indent_increment=2,
  95. max_help_position=24,
  96. width=None,
  97. short_first=1):
  98. # should be replace with super ?
  99. optparse.HelpFormatter.__init__(self, indent_increment,
  100. max_help_position, width, short_first)
  101. # argparse.HelpFormatter(indent_increment, max_help_position, width,)
  102. # no such option in argparse? short_first=1)
  103. def _markup(self, txt):
  104. return txt.replace('-', '\\-')
  105. def format_usage(self, usage):
  106. return self._markup(usage)
  107. def format_heading(self, heading):
  108. if self.level == 0:
  109. return ''
  110. return '.TP\n%s\n' % self._markup(heading.upper())
  111. def format_option(self, option):
  112. result = []
  113. opts = self.option_strings[option]
  114. result.append('.TP\n.B %s\n' % self._markup(opts))
  115. if option.help:
  116. help_text = '%s\n' % self._markup(self.expand_default(option))
  117. result.append(help_text)
  118. return ''.join(result)
  119. #build.sub_commands.append(('build_manpage', None))