Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 389 – argparse - 新命令行解析模块

作者:
Steven Bethard <steven.bethard at gmail.com>
状态:
最终版
类型:
标准跟踪
创建日期:
2009年9月25日
Python 版本:
2.7, 3.2
发布历史:
2009年9月27日,2009年10月24日

目录

接受

本 PEP 于 2010 年 2 月 21 日在 python-dev 上获得 Guido 的批准 [17]

摘要

本 PEP 提议将 argparse [1] 模块包含在 Python 2.7 和 3.2 的 Python 标准库中。

动机

argparse 模块是一个命令行解析库,它比标准库中现有的命令行解析模块 getopt [2] 和 optparse [3] 提供更多功能。它支持位置参数(不仅仅是选项)、子命令、必需选项、像“/f”和“+rgb”这样的选项语法、零个或多个以及一个或多个样式参数,以及其他两个模块所缺乏的许多其他功能。

argparse 模块也已经是这些模块的流行第三方替代品。它用于 IPython (Scipy Python shell) [4] 等项目,包含在 Debian testing 和 unstable [5] 中,自 2007 年以来,对其包含在标准库中的请求不断 [6] [7] [8]。这种流行表明它可能是 Python 库的一个有价值的补充。

为什么 getopt 和 optparse 不够用?

反对添加 argparse 的一个论点是“标准库中已经有两个不同的选项解析模块” [9]。以下是 argparse 提供但 getopt 或 optparse 中不存在的功能列表

  • 虽然确实有两个 *选项* 解析库,但没有完整的命令行解析库——getopt 和 optparse 都只支持选项,不支持位置参数。argparse 模块同时处理两者,因此能够生成更好的帮助消息,避免了 optparse 通常需要的 usage= 字符串等冗余。
  • argparse 模块重视实用性而非纯粹性。因此,argparse 允许必需选项和自定义用于标识选项的字符,而 optparse 明确指出“‘必需选项’一词是自相矛盾的”,并且选项语法 -pf-file+f+rgb/f/file “optparse 不支持,也永远不会支持”。
  • argparse 模块允许选项使用 nargs='?'nargs='*'nargs='+' 接受可变数量的参数。optparse 模块为此功能的一部分提供了未经测试的配方 [10],但承认“当您希望选项接受可变数量的参数时,事情会变得棘手。”
  • argparse 模块支持子命令,其中主命令行解析器根据命令行参数调度到其他命令行解析器。这是命令行界面中的常见模式,例如 svn cosvn up

为什么不直接将功能添加到 optparse 中?

显然,上述所有功能都比 optparse 提供的功能有所改进。那么一个合理的问题是,为什么不简单地将这些功能作为 optparse 的补丁提供,而不是引入一个全新的模块。事实上,argparse 最初的开发就是打算这样做,但由于 optparse 的各种相当限制性的设计决策,这实际上是不可能的。一些问题包括

  • optparse 模块暴露了其解析算法的内部结构。特别是,parser.largsparser.rargs 保证可用于回调 [11]。这使得改进解析算法变得极其困难,而 argparse 需要改进解析算法才能正确处理位置参数和可变长度参数。例如,argparse 中的 nargs='+' 是使用正则表达式匹配的,因此没有 parser.largs 之类的概念。
  • optparse 扩展 API 非常复杂。例如,仅仅使用一个简单的自定义字符串到对象转换函数,您就必须子类化 Option,修改类属性,然后将您的自定义选项类型指定给解析器,如下所示
    class MyOption(Option):
        TYPES = Option.TYPES + ("mytype",)
        TYPE_CHECKER = copy(Option.TYPE_CHECKER)
        TYPE_CHECKER["mytype"] = check_mytype
    parser = optparse.OptionParser(option_class=MyOption)
    parser.add_option("-m", type="mytype")
    

    相比之下,argparse 简单地允许直接将转换函数用作 type= 参数,例如

    parser = argparse.ArgumentParser()
    parser.add_option("-m", type=check_mytype)
    

    但是鉴于 optparse 华而不实的自定义 API,尚不清楚这样的功能应如何与这些 API 交互,并且引入简单的 argparse API 很有可能会破坏现有的自定义 Option 代码。

  • optparse 和 argparse 都解析命令行参数并将它们作为属性分配给 parse_args 返回的对象。但是,optparse 模块保证自定义操作的 take_action 方法将始终传递一个提供 ensure_value 方法的 values 对象 [12],而 argparse 模块允许将属性分配给任何对象,例如
    foo_object = ...
    parser.parse_args(namespace=foo_object)
    foo_object.some_attribute_parsed_from_command_line
    

    修改 optparse 以允许传入任何对象将很困难,因为简单地传递 foo_object 而不是 Values 实例会破坏依赖于 ensure_value 方法的现有自定义操作。

由于这些问题,argparse 难以与 optparse API 保持兼容,因此 argparse 被开发为一个独立的模块。鉴于这些问题,将所有 argparse 功能合并到 optparse 中而不产生向后不兼容性似乎不太可能。

optparse 的弃用

由于 optparse 的所有功能都可在 argparse 中使用,因此 optparse 模块将被弃用。但是,由于 optparse 的广泛使用,弃用策略仅包含文档更改和默认情况下不可见的警告

  • Python 2.7+ 和 3.2+ – 以下注释将添加到 optparse 文档中
    optparse 模块已弃用,将不再进一步开发;开发将继续使用 argparse 模块。
  • Python 2.7+ – 如果在命令行提供 Python 3 兼容性标志 -3,则导入 optparse 将发出 DeprecationWarning。否则不会发出警告。
  • Python 3.2+ – 导入 optparse 将发出 PendingDeprecationWarning,默认情况下不显示。

请注意,没有为 optparse 提出删除日期。

getopt 文档的更新

getopt 模块不会被弃用。但是,其文档将更新以在几个地方指向 argparse。在模块顶部,将添加以下注释

getopt 模块是一个命令行选项解析器,其 API 旨在让 C getopt 函数的用户熟悉。不熟悉 C getopt 函数或希望编写更少代码并获得更好帮助和错误消息的用户应考虑使用 argparse 模块。

此外,在最终的 getopt 示例之后,将添加以下注释

请注意,使用 argparse 模块可以通过更少的代码生成等效的命令行界面
import argparse

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('-o', '--output')
    parser.add_argument('-v', dest='verbose', action='store_true')
    args = parser.parse_args()
    # ... do something with args.output ...
    # ... do something with args.verbose ..

延期:字符串格式化

argparse 模块支持从 2.3 到 3.2 的 Python 版本,因此依赖于传统的 %(foo)s 样式字符串格式化。有人建议最好使用新样式 {foo} 字符串格式化 [13]。关于如何在标准库中的模块中最好地做到这一点,有一些讨论 [14],并且有几个人正在开发用于自动将 % 格式化转换为 {} 格式化的函数 [15] [16]。当其中一个添加到标准库时,argparse 将使用它们来支持两种格式化样式。

已拒绝:getopt 兼容性方法

以前,当本 PEP 建议弃用 getopt 和 optparse 时,曾有人讨论添加一个类似以下的方法

ArgumentParser.add_getopt_arguments(options[, long_options])

但是,出于多种原因,此方法不会添加

  • getopt 模块未被弃用,因此需求较少。
  • 此方法实际上不会简化任何已经维护使用消息的 getopt 用户的过渡,因为上述 API 无法向参数添加帮助消息。
  • getopt 的一些用户认为只需一个函数调用非常重要。上述 API 不满足此要求,因为 ArgumentParser()parse_args() 也必须被调用。

超出范围:各种功能请求

在本 PEP 的讨论中,对 argparse 提出了一些功能请求

  • 支持来自环境变量的参数默认值
  • 支持来自配置文件的参数默认值
  • 支持“foo --help subcommand”以及当前支持的“foo subcommand --help”

这些都是对 argparse 模块合理的特性请求,但超出了本 PEP 的范围,并已重定向到 argparse 问题跟踪器。

讨论:sys.stderr 和 sys.exit

有人担心 argparse 默认情况下总是写入 sys.stderr 并在提供无效参数时总是调用 sys.exit。这对于绝大多数围绕简单命令行界面的 argparse 用例来说是期望的行为。但是,在某些情况下,可能需要阻止 argparse 退出,或者让它将消息写入 sys.stderr 以外的地方。这些用例可以通过子类化 ArgumentParser 并覆盖 exit_print_message 方法来支持。后者是一个未记录的实现细节,但如果这被证明是一个常见的需求,则可以正式公开。

参考资料


来源:https://github.com/python/peps/blob/main/peps/pep-0389.rst

上次修改:2025-02-01 08:59:27 GMT