123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- # -*- coding: utf-8 -*-
- # manpage/argparse_help.py
- # Part of ‘manpage’, a Python library for making Unix manual documents.
- #
- # Copyright © 2016 Ben Finney <ben+python@benfinney.id.au>
- #
- # This is free software: see the grant of license at end of this file.
- """ ArgumentParser help integration to build Unix “man page” documents. """
- import argparse
- import textwrap
- from . import document
- class CommandManPageMaker(document.ManPageMaker):
- """ Maker for a command manual page document.
- Data attributes:
- * `metadata`: A `document.MetaData` instance specifying the
- document metadata for the manual page document.
- * `parser`: The `argparse.ArgumentParser` instance for the
- command to be documented.
- * `seealso`: A collection of `document.Reference` instances.
- If not ``None``, this is used to populate the “SEE ALSO”
- section of the document.
- """
- document_class = document.CommandDocument
- def __init__(self, metadata):
- super().__init__(metadata)
- self.parser = None
- def set_parser(self, parser):
- """ Set the parser to use for generating help.
- :param parser: The instance of `argparse.ArgumentParser`
- from which to derive help text for the command.
- :return: None.
- """
- parser.formatter_class = ManPageHelpFormatter
- parser.prog = self.metadata.name
- self.parser = parser
- def make_manpage(self):
- """ Make a manual page document from the known metadata. """
- manpage = super().make_manpage()
- manpage.content_sections.update({
- "OPTIONS": self.make_options_section(),
- })
- return manpage
- def make_synopsis_section(self, text=None):
- """ Make the “SYNOPSIS” section of the document. """
- if text is None:
- text = " ".join(
- line.strip()
- for line in self.parser.format_usage().splitlines())
- section = super().make_synopsis_section(text)
- return section
- def make_description_section(self, text=None):
- """ Make the “DESCRIPTION” section of the document. """
- if text is None:
- text = self.parser.description
- section = super().make_description_section(text)
- return section
- def make_options_section(self):
- """ Make the “OPTIONS” section of the document. """
- section = document.DocumentSection("OPTIONS")
- options_markup = self.parser.formatter_class.format_options(
- self.parser)
- section.body = options_markup
- return section
- def make_distribution_section(self, distribution):
- """ Make the “DISTRIBUTION” section of the document. """
- section = document.DocumentSection("DISTRIBUTION")
- name_markup = document.GroffMarkup.escapetext(
- self.metadata.name, hyphen=document.GroffMarkup.glyph.minus)
- distribution_markup = document.GroffMarkup.escapetext(
- distribution.get_name())
- homepage = distribution.get_url()
- homepage_markup = document.GroffMarkup.escapetext(homepage)
- section.body = textwrap.dedent("""\
- The command
- {macro.bold} {name}
- is part of the Python distribution
- {glyph.dquote_left}{dist}{glyph.dquote_right}.
- The home page for {dist} is at
- {macro.url_begin} {homepage}
- {macro.url_end} .
- """).format(
- macro=document.GroffMarkup.macro,
- glyph=document.GroffMarkup.glyph,
- name=name_markup,
- dist=distribution_markup, homepage=homepage_markup)
- return section
- class ManPageHelpFormatter(argparse.RawTextHelpFormatter):
- """ ArgumentParser help formatter to generate a Unix manual page. """
- def __init__(
- self, prog,
- indent_increment=2,
- max_help_position=8,
- width=float("inf"),
- ):
- prog_markup = self._format_literal_text(prog)
- super().__init__(
- prog_markup, indent_increment, max_help_position, width)
- def _indent(self):
- pass
- def _dedent(self):
- pass
- def _split_lines(self, text, width):
- lines = [text]
- return lines
- @staticmethod
- def _format_literal_text(text):
- """ Convert `text` to Groff markup for literal command text. """
- text_markup = document.GroffMarkup.escapetext(
- text, hyphen=document.GroffMarkup.glyph.minus)
- result = "{font.bold}{text}{font.previous}".format(
- font=document.GroffMarkup.font, text=text_markup)
- return result
- @staticmethod
- def _format_variable_text(text):
- """ Convert `text` to Groff markup for variable parameter text. """
- text_markup = document.GroffMarkup.escapetext(
- text, hyphen=document.GroffMarkup.glyph.minus)
- result = "{font.italic}{text}{font.previous}".format(
- font=document.GroffMarkup.font, text=text_markup)
- return result
- @staticmethod
- def _format_option_text(text):
- """ Convert `text` to Groff markup for a command option. """
- result = document.GroffMarkup.escapetext(
- text, hyphen=document.GroffMarkup.glyph.minus)
- return result
- @staticmethod
- def format_options(parser):
- """ Format the detailed options help.
- :param parser: The `ArgumentParser` instance to document.
- :return: The text of the detailed options help.
- """
- formatter = parser._get_formatter()
- for action_group in parser._action_groups:
- formatter.start_section(action_group.title)
- formatter.add_arguments(action_group._group_actions)
- formatter.end_section()
- formatter.add_text(parser.epilog)
- return formatter.format_help()
- def start_section(self, heading):
- """ Start a new subsection of the detailed actions help.
- :param heading: Text of the section heading.
- :return: None.
- """
- heading_markup = textwrap.dedent("""\
- {control.empty}
- {macro.subsection} {title}""").format(
- control=document.GroffMarkup.control,
- macro=document.GroffMarkup.macro,
- title=heading.title())
- section = self._Section(self, self._current_section, heading_markup)
- self._add_item(section.format_help, [])
- self._current_section = section
- def _format_usage(self, usage, actions, groups, prefix):
- """ Make text for the usage message.
- :param usage: Text to use for the message. If not
- specified, construct the message from the remaining
- arguments.
- :param actions: Collection of `Action` instances to
- document.
- :param groups: Collection of `_ArgumentGroup` instances
- grouping the actions.
- :param prefix: Text to prefix the usage message.
- :return: Text of the usage message.
- """
- prefix = ""
- result = super()._format_usage(usage, actions, groups, prefix)
- return result
- @classmethod
- def _action_with_literal_text_markup(cls, action):
- """ Make a copy of `action`, with literal text formatted.
- :param action: The `Action` instance to document.
- :return: An `Action` instance copied from `action`, with
- literal text marked up for help output.
- """
- action_with_markup = argparse.Action(
- list(action.option_strings),
- action.dest,
- nargs=action.nargs,
- const=action.const,
- default=action.default,
- type=action.type,
- choices=action.choices,
- required=action.required,
- help=action.help,
- metavar=action.metavar)
- action_with_markup.option_strings = [
- cls._format_literal_text(item)
- for item in action.option_strings]
- if action.choices is not None:
- action_with_markup.choices = [
- cls._format_literal_text(item)
- for item in action.choices]
- return action_with_markup
- def _metavar_formatter(self, action, default_metavar):
- """ Provide a formatter function of the action meta-variable.
- :param action: The `Action` instance to document.
- :param default_metavar: Default “meta variable”
- (replaceable parameter) name in the arguments.
- :return: A function which takes a parameter `tuple_size`,
- a positive integer; and returns a value for formatting.
- The formatter function returns either the marked-up
- meta-variable text (if `tuple_size` is 1), or a tuple of
- `tuple_size` copies of the marked-up text.
- This method is used in the base `argparse.ArgumentParser`
- for some formatting operations.
- """
- action_with_markup = self._action_with_literal_text_markup(action)
- if action.metavar is not None:
- action_with_markup.metavar = self._format_variable_text(
- action.metavar)
- default_metavar_with_markup = self._format_variable_text(
- default_metavar)
- result = super()._metavar_formatter(
- action_with_markup, default_metavar_with_markup)
- return result
- def _format_actions_usage(self, actions, groups):
- """ Make text documenting `actions` in the usage message.
- :param actions: Collection of `Action` instances to
- document.
- :param groups: Collection of `_ArgumentGroup` instances
- grouping the actions.
- :return: Text documenting the `actions` for the usage
- message.
- """
- marked_up_actions = [
- self._action_with_literal_text_markup(action)
- for action in actions]
- result = super()._format_actions_usage(marked_up_actions, groups)
- return result
- def _format_action_invocation(self, action):
- """ Make text detailing the invocation of `action`.
- :param action: The `Action` instance to document.
- :return: The text documenting the invocation of `action`.
- """
- default_metavar = action.dest.upper()
- action_with_markup = self._action_with_literal_text_markup(action)
- args_markup = self._format_args(action_with_markup, default_metavar)
- parts = []
- if not action_with_markup.option_strings:
- # Action is a positional parameter.
- metavar_markup = self._metavar_formatter(
- action, default_metavar)(1)[0]
- parts.append(metavar_markup)
- else:
- # Action is a non-positional option.
- if action_with_markup.nargs == 0:
- option_template = "{option}"
- else:
- option_template = "{option} {args}"
- options_markup = ",\n".join(
- option_template.format(
- option=option_markup, args=args_markup)
- for option_markup in action_with_markup.option_strings)
- parts.append(options_markup)
- result = "\n" + self._join_parts(parts)
- return result
- # This is free software: you may copy, modify, and/or distribute this work
- # under the terms of the GNU General Public License as published by the
- # Free Software Foundation; version 3 of that license or any later version.
- #
- # No warranty expressed or implied. See the file ‘LICENSE.GPL-3’ for details,
- # or view it online at <URL:https://www.gnu.org/licenses/gpl-3.0.html>.
- # Local variables:
- # coding: utf-8
- # mode: python
- # End:
- # vim: fileencoding=utf-8 filetype=python :
|