summaryrefslogtreecommitdiff
path: root/lib/spack/llnl/util/argparsewriter.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/spack/llnl/util/argparsewriter.py')
-rw-r--r--lib/spack/llnl/util/argparsewriter.py203
1 files changed, 203 insertions, 0 deletions
diff --git a/lib/spack/llnl/util/argparsewriter.py b/lib/spack/llnl/util/argparsewriter.py
new file mode 100644
index 0000000000..7ae89b81a8
--- /dev/null
+++ b/lib/spack/llnl/util/argparsewriter.py
@@ -0,0 +1,203 @@
+# Copyright 2013-2018 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+
+from __future__ import print_function
+import re
+import argparse
+import errno
+import sys
+
+
+class ArgparseWriter(object):
+ """Analyzes an argparse ArgumentParser for easy generation of help."""
+ def __init__(self):
+ self.level = 0
+
+ def _write(self, parser, root=True, level=0):
+ self.parser = parser
+ self.level = level
+ actions = parser._actions
+
+ # allow root level to be flattened with rest of commands
+ if type(root) == int:
+ self.level = root
+ root = True
+
+ # go through actions and split them into optionals, positionals,
+ # and subcommands
+ optionals = []
+ positionals = []
+ subcommands = []
+ for action in actions:
+ if action.option_strings:
+ optionals.append(action)
+ elif isinstance(action, argparse._SubParsersAction):
+ for subaction in action._choices_actions:
+ subparser = action._name_parser_map[subaction.dest]
+ subcommands.append(subparser)
+ else:
+ positionals.append(action)
+
+ groups = parser._mutually_exclusive_groups
+ fmt = parser._get_formatter()
+ description = parser.description
+
+ def action_group(function, actions):
+ for action in actions:
+ arg = fmt._format_action_invocation(action)
+ help = action.help if action.help else ''
+ function(arg, re.sub('\n', ' ', help))
+
+ if root:
+ self.begin_command(parser.prog)
+
+ if description:
+ self.description(parser.description)
+
+ usage = fmt._format_usage(None, actions, groups, '').strip()
+ self.usage(usage)
+
+ if positionals:
+ self.begin_positionals()
+ action_group(self.positional, positionals)
+ self.end_positionals()
+
+ if optionals:
+ self.begin_optionals()
+ action_group(self.optional, optionals)
+ self.end_optionals()
+
+ if subcommands:
+ self.begin_subcommands(subcommands)
+ for subparser in subcommands:
+ self._write(subparser, root=True, level=level + 1)
+ self.end_subcommands(subcommands)
+
+ if root:
+ self.end_command(parser.prog)
+
+ def write(self, parser, root=True):
+ """Write out details about an ArgumentParser.
+
+ Args:
+ parser (ArgumentParser): an ``argparse`` parser
+ root (bool or int): if bool, whether to include the root parser;
+ or ``1`` to flatten the root parser with first-level
+ subcommands
+ """
+ try:
+ self._write(parser, root, level=0)
+ except IOError as e:
+ # swallow pipe errors
+ if e.errno != errno.EPIPE:
+ raise
+
+ def begin_command(self, prog):
+ pass
+
+ def end_command(self, prog):
+ pass
+
+ def description(self, description):
+ pass
+
+ def usage(self, usage):
+ pass
+
+ def begin_positionals(self):
+ pass
+
+ def positional(self, name, help):
+ pass
+
+ def end_positionals(self):
+ pass
+
+ def begin_optionals(self):
+ pass
+
+ def optional(self, option, help):
+ pass
+
+ def end_optionals(self):
+ pass
+
+ def begin_subcommands(self, subcommands):
+ pass
+
+ def end_subcommands(self, subcommands):
+ pass
+
+
+_rst_levels = ['=', '-', '^', '~', ':', '`']
+
+
+class ArgparseRstWriter(ArgparseWriter):
+ """Write argparse output as rst sections."""
+
+ def __init__(self, out=sys.stdout, rst_levels=_rst_levels,
+ strip_root_prog=True):
+ """Create a new ArgparseRstWriter.
+
+ Args:
+ out (file object): file to write to
+ rst_levels (list of str): list of characters
+ for rst section headings
+ strip_root_prog (bool): if ``True``, strip the base command name
+ from subcommands in output
+ """
+ super(ArgparseWriter, self).__init__()
+ self.out = out
+ self.rst_levels = rst_levels
+ self.strip_root_prog = strip_root_prog
+
+ def line(self, string=''):
+ self.out.write('%s\n' % string)
+
+ def begin_command(self, prog):
+ self.line()
+ self.line('----')
+ self.line()
+ self.line('.. _%s:\n' % prog.replace(' ', '-'))
+ self.line('%s' % prog)
+ self.line(self.rst_levels[self.level] * len(prog) + '\n')
+
+ def description(self, description):
+ self.line('%s\n' % description)
+
+ def usage(self, usage):
+ self.line('.. code-block:: console\n')
+ self.line(' %s\n' % usage)
+
+ def begin_positionals(self):
+ self.line()
+ self.line('**Positional arguments**\n')
+
+ def positional(self, name, help):
+ self.line(name)
+ self.line(' %s\n' % help)
+
+ def begin_optionals(self):
+ self.line()
+ self.line('**Optional arguments**\n')
+
+ def optional(self, opts, help):
+ self.line('``%s``' % opts)
+ self.line(' %s\n' % help)
+
+ def begin_subcommands(self, subcommands):
+ self.line()
+ self.line('**Subcommands**\n')
+ self.line('.. hlist::')
+ self.line(' :columns: 4\n')
+
+ for cmd in subcommands:
+ prog = cmd.prog
+ if self.strip_root_prog:
+ prog = re.sub(r'^[^ ]* ', '', prog)
+
+ self.line(' * :ref:`%s <%s>`'
+ % (prog, cmd.prog.replace(' ', '-')))
+ self.line()