无法捕获 argparse 的异常/错误

Unable to catch exception/ error for argparse

我有以下脚本,其中我定义了一个 loggerargparse 实例化函数,然后我在 主要函数。

场景 1: 对于下面的代码,如果将非整数值传递给参数解析器,错误处理将按预期工作。

import argparse
import logging

def set_logger() -> logging.Logger:
    dtfmt = '%d-%m-%y %H:%M:%S'
    logging.basicConfig(level=logging.DEBUG, 
                        format='%(asctime)s :: [%(processName)s] :: %(levelname)s: %(message)s',
                        datefmt=dtfmt,
                        handlers=[logging.StreamHandler()])
    return logging.getLogger()

def argg() -> argparse.Namespace:
    parser = argparse.ArgumentParser(prog='ProgramName', 
                                     description="A program that does mircales when it runs. LOL!",
                                     exit_on_error=False)
    parser.add_argument('--integers', type=int)
    return parser.parse_args()

def main():
    logger = set_logger()

    try:
        print(f'Integer value: {argg().integers} supplied.')
    except (AttributeError, argparse.ArgumentError)as e:
        logger.error(e)

if __name__ == '__main__':
    main()

传递值 'a' 时,记录器生成的错误消息如下所示(预期行为):

09-09-21 00:58:42 :: [MainProcess] :: ERROR: argument --integers: invalid int value: 'a'


场景 2: 但是,如果 argg() 函数如下重新定义,即包含子解析器,则当传递无效值时错误处理将无法工作到参数解析器。

def argg():
    parser = argparse.ArgumentParser(prog='ProgramName', 
                                     description="A program that does mircales when it runs. LOL!",
                                     exit_on_error=False)
    subparsers = parser.add_subparsers(dest='mode')
    subparser1 = subparsers.add_parser('regular', help="run program in regular mode")
    subparser1.add_argument('-int', '--integers', type=int, required=True)
    return parser.parse_args()

在这种情况下传递值 'a' 会导致默认的 argparse 错误,如下所示:

ProgramName regular: error: argument -int/--integers: invalid int value: 'a'

我的问题是,为什么在场景 2(即带有子参数的 argparse)中错误处理会失败(记录器未被调用)?

没有记录器的东西,在 ipython 会话中。你的第一个案例

In [11]: def argg0(argv) -> argparse.Namespace:
    ...:     parser = argparse.ArgumentParser(prog='ProgramName',
    ...:                                      description="A program that does mircales when it runs. LOL!",
    ...:                                      exit_on_error=False)
    ...:     parser.add_argument('--integers', type=int)
    ...:     return parser.parse_args(argv)
    ...: 
In [12]: argg0(['--integers','xxx'])
Traceback (most recent call last):
  File "/home/paul/mypy/argparse310.py", line 2476, in _get_value
    result = type_func(arg_string)
ValueError: invalid literal for int() with base 10: 'xxx'

During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "<ipython-input-12-f7f84a36f9f8>", line 1, in <module>
    argg0(['--integers','xxx'])
  File "<ipython-input-11-0f7c85331b7b>", line 6, in argg0
    return parser.parse_args(argv)
  File "/home/paul/mypy/argparse310.py", line 1818, in parse_args
    args, argv = self.parse_known_args(args, namespace)
  File "/home/paul/mypy/argparse310.py", line 1856, in parse_known_args
    namespace, args = self._parse_known_args(args, namespace)
  File "/home/paul/mypy/argparse310.py", line 2060, in _parse_known_args
    start_index = consume_optional(start_index)
  File "/home/paul/mypy/argparse310.py", line 2000, in consume_optional
    take_action(action, args, option_string)
  File "/home/paul/mypy/argparse310.py", line 1912, in take_action
    argument_values = self._get_values(action, argument_strings)
  File "/home/paul/mypy/argparse310.py", line 2443, in _get_values
    value = self._get_value(action, arg_string)
  File "/home/paul/mypy/argparse310.py", line 2489, in _get_value
    raise ArgumentError(action, msg % args)
ArgumentError: argument --integers: invalid int value: 'xxx'

和子解析器案例:

In [13]: def argg1(argv):
    ...:     parser = argparse.ArgumentParser(prog='ProgramName',
    ...:                                      description="A program that does mircales when it runs. LOL!",
    ...:                                      exit_on_error=False)
    ...:     subparsers = parser.add_subparsers(dest='mode')
    ...:     subparser1 = subparsers.add_parser('regular', help="run program in regular mode")
    ...:     subparser1.add_argument('-int', '--integers', type=int, required=True)
    ...:     return parser.parse_args(argv)
    ...: 
In [15]: argg1(['regular','--integers','xxx'])
usage: ProgramName regular [-h] -int INTEGERS
ProgramName regular: error: argument -int/--integers: invalid int value: 'xxx'

ipython 错误回溯添加:

An exception has occurred, use %tb to see the full traceback.
Traceback (most recent call last):
  File "/home/paul/mypy/argparse310.py", line 2476, in _get_value
    result = type_func(arg_string)
ValueError: invalid literal for int() with base 10: 'xxx'
...
ArgumentError: argument -int/--integers: invalid int value: 'xxx'

因此 exit_on_error=False 在子解析器的情况下不起作用。

编辑

我刚刚意识到 exit_on_error 确实有效 - 但它必须是 subparser 定义的一部分:

subparser1 = subparsers.add_parser('regular', help="run program in regular mode",exit_on_error=False)

子解析器是使用与常规解析器相同的 class 创建的,因此采用大部分相同的可选参数。它不继承它们;它们必须明确包含在内。我在其他情况下看到过这种情况,例如 help formatter class.