【问题标题】:Use argparse with Setuptools entry_points将 argparse 与 Setuptools entry_points 一起使用
【发布时间】:2014-04-07 10:47:14
【问题描述】:

我正在编写一个我想使用 Setuptools 分发的脚本。我已将此脚本添加到我的setup.py 中的entry_points 部分。

来自 setuptools 文档:

您指定的函数在没有参数的情况下被调用,它们的返回值被传递给 sys.exit(),因此您可以返回错误级别或消息以打印到 stderr

由于该方法将返回而不是退出,它变得更加可测试。出于可测试性的目的,我接受默认为sys.argv 的方法中的参数。到目前为止一切顺利。

将 argparse 添加到混合中时会出现问题。当 argparse 无法解析 args 时,它会调用 sys.exit。现在我真的希望 argparse 不这样做,因为这是由 setuptools 包装器处理的。我能想到解决这个问题的第一件事是覆盖argparse.ArgumentParser,但后来我看到了:

# ===============
# Exiting methods
# ===============
def exit(self, status=0, message=None):
    if message:
        self._print_message(message, _sys.stderr)
    _sys.exit(status)

def error(self, message):
    """error(message: string)

    Prints a usage message incorporating the message to stderr and
    exits.

    If you override this in a subclass, it should not return -- it
    should either exit or raise an exception.
    """
    self.print_usage(_sys.stderr)
    self.exit(2, _('%s: error: %s\n') % (self.prog, message))

所以文档字符串声明我不应该返回并坚持引发异常。我该如何解决这个问题?

如果我解释得不够透彻的主要方法:

def main(args=sys.argv):
    parser = ArgumentParser(prog='spam')

    # parser is configured here

    parsed = parser.parse_args(args)

    # Parsed args are used here

【问题讨论】:

    标签: python python-2.7 setuptools argparse


    【解决方案1】:

    您不想从errorreturn 的原因是解析器会继续解析。一些错误在结尾附近出现(例如关于未解析的字符串),但其他错误可能会提前发生(例如第一个参数字符串的类型错误)。如果您从错误方法返回,parse_args 的行为是不可预测的。通常你希望解析器退出并返回控制你的代码。

    您要做的是将parse_args() 调用包装在try: except SystemExit: 块中。我经常使用这样的测试脚本:

    for test in ['-o FILE',
        ...
             ]:
        print(test)
        try:
            print(parser.parse_args(test.split()))
        except SystemExit:
            pass
    

    您可以使用error 和/或exit 返回其他类型的异常。他们还可以绕过使用信息。但是,您需要以某种方式在包装器中捕获异常。

    【讨论】:

    • 捕捉SystemError 不会有任何好处,因为SystemExit 不是它的子类。我认为这是一个错字。 ;-) 我决定在我的 main 方法中捕获 SystemExit,这反过来又返回状态码。这并没有摆脱我在测试中遇到的麻烦,所以我必须继续修补sys.stdout 和朋友们看看写了什么。扩展argparse 是一件痛苦的事情,因为它没有大量文档并且应用了工厂模式。提出异常的意义非常明显,我不应该问这个。感谢您的回答。 ;-)
    • 是的,SysExit 是正确的。 test_argparse.py 在其 ErrorRaisingArgumentParser 类中重定向标准输出和标准错误。
    【解决方案2】:

    如果您开始一个新项目或有时间进行一些重构,那么您可以考虑使用Click 库。 Click 具有 setuptools integration 和 'testability' 作为功能,以及其他注意事项。

    这是来自文档的示例/test-sn-p,它都创建了一个迷你命令行界面,然后立即对其进行测试:

    import click
    from click.testing import CliRunner
    
    def test_hello_world():
        @click.command()
        @click.argument('name')
        def hello(name):
            click.echo('Hello %s!' % name)
    
        runner = CliRunner()
        result = runner.invoke(hello, ['Peter'])
        assert result.exit_code == 0
        assert result.output == 'Hello Peter!\n'
    

    【讨论】:

    • 点击非常好。我个人认为文档不太好,但只要稍加坚持,就能找到大部分答案。其余的可以通过堆栈溢出找到;-)
    猜你喜欢
    • 2015-03-11
    • 1970-01-01
    • 2020-11-05
    • 2014-11-25
    • 2015-02-20
    • 2011-02-20
    • 1970-01-01
    • 2011-12-06
    • 2021-07-20
    相关资源
    最近更新 更多