From 4d16eec8207908d94df26511a8a617c8f7cf7234 Mon Sep 17 00:00:00 2001 From: Tzafrir Cohen Date: Tue, 27 Feb 2018 19:37:27 +0200 Subject: Initial commit --- Makefile | 9 +++ argparse-html.py | 186 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 Makefile create mode 100755 argparse-html.py diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..53c08fa --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +script = argparse-html.py + +all: README README.html + +README: $(script) + python $(script) --help >$@ + +README.html: $(script) + python $(script) --html-usage --html-usage-raw >$@ diff --git a/argparse-html.py b/argparse-html.py new file mode 100755 index 0000000..53082a7 --- /dev/null +++ b/argparse-html.py @@ -0,0 +1,186 @@ +""" Argparse usage to HTML output + +A class and an example script that uses it + +Copyright 2018, Tzafrir Cohen + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +from __future__ import print_function +import argparse +import re + +# Used by the example script and not by the class itself. +import sys +import time + + +class HTMLFormatter(argparse.RawTextHelpFormatter): + """ Format argparse help as an HTML snippet. + + Need to inherit from RawTextHelpFormatter rather than HelpFormatter + in order to keep description / epilog text split to lines (paragraps). + """ + def _format_action(self, action): + """ Wrap an action line in a LI tag """ + return "
  • {}
  • ".format( + super(HTMLFormatter, self)._format_action(action)) + + def _format_usage(self, usage, actions, groups, prefix): + """ Wrap usage text in a P tag """ + return "

    {}

    ".format( + super(HTMLFormatter, self)._format_usage(usage, actions, + groups, prefix)) + + def _format_text(self, text): + """ Format a single section. Split to paragraphs """ + text = super(HTMLFormatter, self)._format_text(text) + text = text.strip() + paragrapgs = re.split(r'^\s*$\n', text, flags=re.M) + html_pars = [] + for p in paragrapgs: + if p in ['']: + html_pars.append(p) + else: + html_pars.append("

    {}

    ".format(p)) + return "\n".join(html_pars) + + def start_section(self, heading): + """ A title and beginning of group UL. + + FIXME: needs extra cleanup of the ':' after the ') + + +def parse_cmd_line(args=sys.argv[1:]): + """ Parse command-line arguments and returns configuration """ + parser = argparse.ArgumentParser( + formatter_class=argparse.RawTextHelpFormatter) + parser.description = """ + This script demonstrates how to automatically convert argparse + output to HTML. See also the class HTMLFormatter. + + It's a hack. It does not produce a very clean HTML (there's an + extra ', for instance). But it's good enough for me. An empty + (or spaces-only) line is considered as a paragraph separator. + + I needed to produce HTML as a single line and thus this is the + default output. This is not part of the class and can be easily + changed in the wrapper function html_usage(). + + """ + + parser.epilog = """ + Note that we use RawTextHelpFormatter as the parser, rather than + HelpFormatter itself. Otherwise splitting to paragarphs with empty + lines won't show up in the standard --help output. + + If you have no problem with that, you can remove the formatter_class + parameter above. + """ + + parser.add_argument("-a", "--arg-a", action="store", type=float, + default=1.1, + help="Set value of A. Default: %(default)s.") + parser.add_argument("-b", "--param-b", action="store", type=int, + default=2, + help="Set value of B. Default: %(default)s.") + parser.add_argument("-v", "--verbose", action="store_true", + help="Be verbose") + opt_sh = parser.add_argument_group("For Interactive Use") + opt_sh.add_argument("-d", "--no-act", action="store_true", + help="Do nothing. Just print what is to be run") + opt_sh.add_argument("-c", "--config", action="store_true", + help="Do nothing. Just show configuration") + opt_sh.add_argument("--html-usage", action="store_true", + help="Do nothing. Format help as HTML (a single line)") + opt_sh.add_argument("--html-usage-raw", action="store_true", + help="If html-usage: don't squash HTML to a single line") + cfg = parser.parse_args(args) + + if cfg.html_usage: + html_usage(parser, cfg) + sys.exit(0) + + # example sanity test: + for key in ['arg_a', 'param_b']: + val = getattr(cfg, key) + if val >= 100 or val < 0: + msg = "E: Invalid value for {}: must be between 0 and 10".format(key) + parser.error(msg) + + return cfg + + +def html_usage(parser, cfg): + """ Print usage text as an HTML snippet. + + If not html_usage_raw was not set in the configuation: produce a + single line. + + Replacing the formatter class for an existing object is not + documented anywhere, but seems to work just fine. + """ + parser.formatter_class = HTMLFormatter + help_text = parser.format_help() + help_text = re.sub('