Python 自定义 argparse 帮助消息
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/35847084/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Customize argparse help message
提问by Lone Learner
I have written the following sample code to demonstrate my issue.
我编写了以下示例代码来演示我的问题。
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-v', '--version', action='version',
version='%(prog)s 1.0')
parser.parse_args()
This produces the following help message.
这会产生以下帮助消息。
$ python foo.py --help
usage: foo.py [-h] [-v]
optional arguments:
-h, --help show this help message and exit
-v, --version show program's version number and exit
I want to customize this help output such that it capitalizes all phrases and sentences, and puts period after sentences. In other words, I want the help message to be generated like this.
我想自定义此帮助输出,使其大写所有短语和句子,并在句子后放置句号。换句话说,我希望像这样生成帮助消息。
$ python foo.py --help
Usage: foo.py [-h] [-v]
Optional arguments:
-h, --help Show this help message and exit.
-v, --version Show program's version number and exit.
Is this something that I can control using the argparse API. If so, how? Could you please provide a small example that shows how this can be done?
这是我可以使用 argparse API 控制的东西吗?如果是这样,如何?你能否提供一个小例子来说明如何做到这一点?
回答by Martijn Pieters
First of all: capitalising those phrases flies in the face of convention, and argparse
isn't really tooled to help you change these strings easily. You have three different classes of strings here: boilerplate text from the help formatter, section titles, and help text per specific option. All these strings are localisable; you couldjust provide a 'capitalised' translation for all of these strings via the gettext()
module support. That said, you can reach in and replace all these strings if you are determined enough and read the source code a little.
首先:大写这些短语与惯例argparse
背道而驰,并没有真正帮助您轻松更改这些字符串。这里有三类不同的字符串:来自帮助格式化程序的样板文本、章节标题和每个特定选项的帮助文本。所有这些字符串都是可本地化的;您可以通过gettext()
模块支持为所有这些字符串提供“大写”翻译。也就是说,如果您有足够的决心并稍微阅读源代码,则可以访问并替换所有这些字符串。
The version
action includes a default help
text, but you can supply your own by setting the help
argument. The same applies to the help
action; if you set the add_help
argumentto False
you can add that action manually:
该version
操作包括默认help
文本,但您可以通过设置help
参数来提供您自己的文本。这同样适用于help
行动;如果您将add_help
参数设置为False
您可以手动添加该操作:
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument('-v', '--version', action='version',
version='%(prog)s 1.0', help="Show program's version number and exit.")
parser.add_argument('-h', '--help', action='help', default=argparse.SUPPRESS,
help='Show this help message and exit.')
Next, the optional arguments
message is a group title; each parser has two default groups, one for positional arguments, the other for optional. You can reach these by the attributes _positionals
and _optionals
, both of which have a title
attribute:
接下来,optional arguments
消息是一个组标题;每个解析器都有两个默认组,一个用于位置参数,另一个用于可选。您可以通过属性_positionals
和来访问它们_optionals
,这两个title
属性都有一个属性:
parser._positionals.title = 'Positional arguments'
parser._optionals.title = 'Optional arguments'
Be warned, by accessing names starting with an underscore you are venturing into the undocumented private API of the module, and your code may break in future updates.
请注意,通过访问以下划线开头的名称,您正在冒险进入模块的未记录的私有 API,并且您的代码可能会在未来的更新中中断。
Finally, to change the usage
string, you'll have to subclass the help formatter; pass the subclass in as the formatter_class
argument:
最后,要更改usage
字符串,您必须继承帮助格式化程序;将子类作为formatter_class
参数传递:
class CapitalisedHelpFormatter(argparse.HelpFormatter):
def add_usage(self, usage, actions, groups, prefix=None):
if prefix is None:
prefix = 'Usage: '
return super(CapitalisedHelpFormatter, self).add_usage(
usage, actions, groups, prefix)
parser = argparse.ArgumentParser(formatter_class=CapitalisedHelpFormatter)
Demo, putting these all together:
演示,将所有这些放在一起:
>>> import argparse
>>> class CapitalisedHelpFormatter(argparse.HelpFormatter):
... def add_usage(self, usage, actions, groups, prefix=None):
... if prefix is None:
... prefix = 'Usage: '
... return super(CapitalisedHelpFormatter, self).add_usage(
... usage, actions, groups, prefix)
...
>>> parser = argparse.ArgumentParser(add_help=False, formatter_class=CapitalisedHelpFormatter)
>>> parser._positionals.title = 'Positional arguments'
>>> parser._optionals.title = 'Optional arguments'
>>> parser.add_argument('-v', '--version', action='version',
... version='%(prog)s 1.0', help="Show program's version number and exit.")
_VersionAction(option_strings=['-v', '--version'], dest='version', nargs=0, const=None, default='==SUPPRESS==', type=None, choices=None, help="Show program's version number and exit.", metavar=None)
>>> parser.add_argument('-h', '--help', action='help', default=argparse.SUPPRESS,
... help='Show this help message and exit.')
_HelpAction(option_strings=['-h', '--help'], dest='help', nargs=0, const=None, default='==SUPPRESS==', type=None, choices=None, help='Show this help message and exit.', metavar=None)
>>> print(parser.format_help())
Usage: [-v] [-h]
Optional arguments:
-v, --version Show program's version number and exit.
-h, --help Show this help message and exit.
回答by hpaulj
Martijn has give a couple of the fixes that came to mind - the providing the help
parameter, and a custom Formatter class.
Martijn 给出了几个想到的修复 - 提供help
参数和自定义 Formatter 类。
One other partial fix is to modify the help string after the argument is created. add_argument
creates and returns an Action
object that contains the parameters and defaults. You can save a link to this, and modify the Action
. You can also get a list of those actions, and act on that.
另一种部分修复是在创建参数后修改帮助字符串。 add_argument
创建并返回一个Action
包含参数和默认值的对象。您可以保存指向此的链接,并修改Action
. 您还可以获得这些操作的列表,并据此采取行动。
Let me illustrate, for a simple parser with the default help and one other argument, the action list is:
让我举例说明,对于带有默认帮助和其他参数的简单解析器,操作列表是:
In [1064]: parser._actions
Out[1064]:
[_HelpAction(option_strings=['-h', '--help'], dest='help', nargs=0, const=None, default='==SUPPRESS==', type=None, choices=None, help='show this help message and exit', metavar=None),
_StoreAction(option_strings=['-f', '--foo'], dest='foo', nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)]
I can view and modify the help
attribute of any of these:
我可以查看和修改其中help
任何一个的属性:
In [1065]: parser._actions[0].help
Out[1065]: 'show this help message and exit'
In [1066]: parser._actions[0].help='Show this help message and exit.'
producing this help:
产生这个帮助:
In [1067]: parser.parse_args(['-h'])
usage: ipython3 [-h] [-f FOO]
optional arguments:
-h, --help Show this help message and exit.
-f FOO, --foo FOO
Using the parser._actions
list uses a 'private' attribute, which some people consider unwise. But in Python that public/private distinction is not tight, and can be broken with care. Martijn is doing that by accessing the parser._positionals.title
.
使用parser._actions
列表会使用“私有”属性,有些人认为这是不明智的。但是在 Python 中,公共/私有的区别并不严格,可以小心地打破。Martijn 通过访问parser._positionals.title
.
Another way to change that group title is with custom argument groups
更改该组标题的另一种方法是使用自定义参数组
ogroup=parser.add_argument_group('Correct Optionals Title')
ogroup.add_argument('-v',...)
ogroup.add_argument('-h',...)
回答by Maxxim
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:
与其依赖于内部 API(如有更改,恕不另行通知),这里提供了一种仅使用公共 API 的替代方法。它可以说更复杂,但反过来又可以让您最大程度地控制打印的内容:
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
- 进入构造函数
argparse.ArgumentParser
以捕获和存储程序信息(例如描述、用法) - 点击
argparse.ArgumentParser.add_argument()
以捕获和存储添加的参数(例如标志、帮助、默认值) - 重新定义
argparse.ArgumentParser.print_help()
并使用以前存储的程序信息/参数来生成帮助文本
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