Customize argparse help message

前端 未结 3 567
慢半拍i
慢半拍i 2020-12-13 04:16

I have written the following sample code to demonstrate my issue.

import argparse

parser = argparse.ArgumentParser()
parser.add_argument(\'-v\', \'--version         


        
3条回答
  •  清歌不尽
    2020-12-13 04:46

    EDIT: If have since extended this significantly and will continue to do so on GitHub. For the sake of consistency, I'll leave my answer here as is.


    Instead of relying on internal API, which is subject to change without notice, here's an alternative using public API only. It is arguably more complex but in turn gives you maximum control over what is printed:

    class ArgumentParser(argparse.ArgumentParser):
    
        def __init__(self, *args, **kwargs):
            super(ArgumentParser, self).__init__(*args, **kwargs)
            self.program = { key: kwargs[key] for key in kwargs }
            self.options = []
    
        def add_argument(self, *args, **kwargs):
            super(ArgumentParser, self).add_argument(*args, **kwargs)
            option = {}
            option["flags"] = [ item for item in args ]
            for key in kwargs:
                option[key] = kwargs[key]
            self.options.append(option)
    
        def print_help(self):
            # use self.program/self.options to produce custom help text
    

    How it works:

    • tap into constructor of argparse.ArgumentParser to capture and store program info (e.g. description, usage)
    • tap into argparse.ArgumentParser.add_argument() to capture and store added arguments (e.g. flags, help, defaults)
    • redefine argparse.ArgumentParser.print_help() and use previously stored program info / arguments to produce help text

    Here is a full example covering some common use cases. Note that it is by no means complete (for example, there is no support for positional arguments or options with more than one argument), but is should provide an impression of what's possible:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    import os
    import sys
    import argparse
    import textwrap
    
    class ArgumentParser(argparse.ArgumentParser):
    
        def __init__(self, *args, **kwargs):
            super(ArgumentParser, self).__init__(*args, **kwargs)
            self.program = { key: kwargs[key] for key in kwargs }
            self.options = []
    
        def add_argument(self, *args, **kwargs):
            super(ArgumentParser, self).add_argument(*args, **kwargs)
            option = {}
            option["flags"] = [ item for item in args ]
            for key in kwargs:
                option[key] = kwargs[key]
            self.options.append(option)
    
        def print_help(self):
            wrapper = textwrap.TextWrapper(width=80)
    
            # Print usage
            if "usage" in self.program:
                print("Usage: %s" % self.program["usage"])
            else:
                usage = []
                for option in self.options:
                    usage += [ "[%s %s]" % (item, option["metavar"]) if "metavar" in option else "[%s %s]" % (item, option["dest"].upper()) if "dest" in option else "[%s]" % item for item in option["flags"] ]
                wrapper.initial_indent = "Usage: %s " % os.path.basename(sys.argv[0])
                wrapper.subsequent_indent = len(wrapper.initial_indent) * " "
                output = str.join(" ", usage)
                output = wrapper.fill(output)
                print(output)
            print()
    
            # Print description
            if "description" in self.program:
                print(self.program["description"])
                print()
    
            # Print options
            print("Options:")
            maxlen = 0
            for option in self.options:
                option["flags2"] = str.join(", ", [ "%s %s" % (item, option["metavar"]) if "metavar" in option else "%s %s" % (item, option["dest"].upper()) if "dest" in option else item for item in option["flags"] ])
                if len(option["flags2"]) > maxlen:
                    maxlen = len(option["flags2"])
            for option in self.options:
                template = "  %-" + str(maxlen) + "s  "
                wrapper.initial_indent = template % option["flags2"]
                wrapper.subsequent_indent = len(wrapper.initial_indent) * " "
                if "help" in option and "default" in option:
                    output = option["help"]
                    output += " (default: '%s')" % option["default"] if isinstance(option["default"], str) else " (default: %s)" % str(option["default"])
                    output = wrapper.fill(output)
                elif "help" in option:
                    output = option["help"]
                    output = wrapper.fill(output)
                elif "default" in option:
                    output = "Default: '%s'" % option["default"] if isinstance(option["default"], str) else "Default: %s" % str(option["default"])
                    output = wrapper.fill(output)
                else:
                    output = wrapper.initial_indent
                print(output)
    
    # Main
    if (__name__ == "__main__"):
        #parser = argparse.ArgumentParser(description="Download program based on some library.", argument_default=argparse.SUPPRESS, allow_abbrev=False, add_help=False)
        #parser = argparse.ArgumentParser(usage="%s [OPTION]..." % os.path.basename(sys.argv[0]), description="Download program based on some library.", argument_default=argparse.SUPPRESS, allow_abbrev=False, add_help=False)
        #parser = ArgumentParser(usage="%s [OPTION]..." % os.path.basename(sys.argv[0]), description="Download program based on some library.", argument_default=argparse.SUPPRESS, allow_abbrev=False, add_help=False)
        parser = ArgumentParser(description="Download program based on some library.", argument_default=argparse.SUPPRESS, allow_abbrev=False, add_help=False)
    
        parser.add_argument("-c", "--config-file", action="store", dest="config_file", metavar="file", type=str, default="config.ini")
        parser.add_argument("-d", "--database-file", action="store", dest="database_file", metavar="file", type=str, help="SQLite3 database file to read/write", default="database.db")
        parser.add_argument("-l", "--log-file", action="store", dest="log_file", metavar="file", type=str, help="File to write log to", default="debug.log")
        parser.add_argument("-f", "--data-file", action="store", dest="data_file", metavar="file", type=str, help="Data file to read", default="data.bin")
        parser.add_argument("-t", "--threads", action="store", dest="threads", type=int, help="Number of threads to spawn", default=3)
        parser.add_argument("-p", "--port", action="store", dest="port", type=int, help="TCP port to listen on for access to the web interface", default="12345")
        parser.add_argument("--max-downloads", action="store", dest="max_downloads", metavar="value", type=int, help="Maximum number of concurrent downloads", default=5)
        parser.add_argument("--download-timeout", action="store", dest="download_timeout", metavar="value", type=int, help="Download timeout in seconds", default=120)
        parser.add_argument("--max-requests", action="store", dest="max_requests", metavar="value", type=int, help="Maximum number of concurrent requests", default=10)
        parser.add_argument("--request-timeout", action="store", dest="request_timeout", metavar="value", type=int, help="Request timeout in seconds", default=60)
        parser.add_argument("--main-interval", action="store", dest="main_interval", metavar="value", type=int, help="Main loop interval in seconds", default=60)
        parser.add_argument("--thread-interval", action="store", dest="thread_interval", metavar="value", type=int, help="Thread loop interval in milliseconds", default=500)
        parser.add_argument("--console-output", action="store", dest="console_output", metavar="value", type=str.lower, choices=["stdout", "stderr"], help="Output to use for console", default="stdout")
        parser.add_argument("--console-level", action="store", dest="console_level", metavar="value", type=str.lower, choices=["debug", "info", "warning", "error", "critical"], help="Log level to use for console", default="info")
        parser.add_argument("--logfile-level", action="store", dest="logfile_level", metavar="value", type=str.lower, choices=["debug", "info", "warning", "error", "critical"], help="Log level to use for log file", default="info")
        parser.add_argument("--console-color", action="store", dest="console_color", metavar="value", type=bool, help="Colorized console output", default=True)
        parser.add_argument("--logfile-color", action="store", dest="logfile_color", metavar="value", type=bool, help="Colorized log file output", default=False)
        parser.add_argument("--log-template", action="store", dest="log_template", metavar="value", type=str, help="Template to use for log lines", default="[%(created)d] [%(threadName)s] [%(levelname)s] %(message)s")
        parser.add_argument("-h", "--help", action="help", help="Display this message")
    
        args = parser.parse_args(["-h"])
    

    Produced output:

    Usage: argparse_custom_usage.py [-c file] [--config-file file] [-d file]
                                    [--database-file file] [-l file] [--log-file
                                    file] [-f file] [--data-file file] [-t THREADS]
                                    [--threads THREADS] [-p PORT] [--port PORT]
                                    [--max-downloads value] [--download-timeout
                                    value] [--max-requests value] [--request-timeout
                                    value] [--main-interval value] [--thread-
                                    interval value] [--console-output value]
                                    [--console-level value] [--logfile-level value]
                                    [--console-color value] [--logfile-color value]
                                    [--log-template value] [-h] [--help]
    
    Download program based on some library.
    
    Options:
      -c file, --config-file file    Default: 'config.ini'
      -d file, --database-file file  SQLite3 database file to read/write (default:
                                     'database.db')
      -l file, --log-file file       File to write log to (default: 'debug.log')
      -f file, --data-file file      Data file to read (default: 'data.bin')
      -t THREADS, --threads THREADS  Number of threads to spawn (default: 3)
      -p PORT, --port PORT           TCP port to listen on for access to the web
                                     interface (default: '12345')
      --max-downloads value          Maximum number of concurrent downloads
                                     (default: 5)
      --download-timeout value       Download timeout in seconds (default: 120)
      --max-requests value           Maximum number of concurrent requests (default:
                                     10)
      --request-timeout value        Request timeout in seconds (default: 60)
      --main-interval value          Main loop interval in seconds (default: 60)
      --thread-interval value        Thread loop interval in milliseconds (default:
                                     500)
      --console-output value         Output to use for console (default: 'stdout')
      --console-level value          Log level to use for console (default: 'info')
      --logfile-level value          Log level to use for log file (default: 'info')
      --console-color value          Colorized console output (default: True)
      --logfile-color value          Colorized log file output (default: False)
      --log-template value           Template to use for log lines (default:
                                     '[%(created)d] [%(threadName)s] [%(levelname)s]
                                     %(message)s')
      -h, --help                     Display this message
    

提交回复
热议问题