【问题标题】:How to set custom output handlers for argparse in Python?如何在 Python 中为 argparse 设置自定义输出处理程序?
【发布时间】:2015-07-08 05:45:51
【问题描述】:

我已将logger 配置为在终端stdout 和文件上打印,这样我就可以拥有可以参考的日志消息存档。

这很容易通过将FileHandler 添加到您的logging 对象来实现。轻松愉快。

我现在想要完成的是让argparse 在遇到解析错误时也将日志记录到同一个文件中,并将日志记录到标准输出。到目前为止,它只打印到stdout。我查看了argparsedocumentation,但找不到任何关于为argparse 设置不同的输出流或管道的信息。

有可能吗?怎么样?

【问题讨论】:

  • @wim 的评论去哪了?提示是通过覆盖argparse._sys.stderr 的值来覆盖argparse 模块的标准错误。这实际上有效,但现在它只写入文件而不是标准输出。
  • 嗨。我删除了该评论,因为@James Mills 发布了一个更出色的解​​决方案。
  • @radj 查看我的回复,了解如何重定向stdoutstderr 流,以确保您捕获来自argparser.ArgumentParser 的所有输出

标签: python python-2.7 logging argparse


【解决方案1】:

查看argparse.py source code 似乎没有办法配置此行为。

我的建议是:

  • 提交带有补丁的错误报告 :)

覆盖/补丁:

  • print_* 方法
  • error 方法。

print_* 方法似乎采用可选的 file 参数,默认为 _sys.stdout

更新: 或者,您可以这样做,在解析参数时临时重定向sys.stdout

from contextlib import contextmanager

@contextmanager
def redirect_stdout_stderr(stream):
    old_stdout = sys.stdout
    old_stderr = sys.stderr
    sys.stdout = stream
    sys.stderr = stream
    try:
        yield
    finally:
        sys.stdout = old_stdout
        sys.stderr = old_stderr


with redirct_stdout_stderr(logstream):
    args = parser.parse_args()

【讨论】:

    【解决方案2】:

    似乎没有办法通过 API 做到这一点。

    但是,您可以执行以下操作:

    class LoggingArgumentParser(argparse.ArgumentParser):
        """Custom ArgumentPaarser that overrides _print_message"""
    
        def _print_message(self, message, file=None):
            if message:
                logger.write(message)
    

    【讨论】:

    • 可惜还有print_usage等人追赶
    • @wim 不,那些在内部使用_print_message
    • 哦,你是对的。不过,覆盖_underscore 方法可能不符合规定吗?您的代码可以在没有任何警告的情况下中断,因为它不是公共接口
    • @wim 绝对不是 kosher :)
    • 为什么你在派生类中定义了__init__,而不是将args和kwargs传递给超类?
    【解决方案3】:

    虽然@James Mills 给出的答案很好并且解决了问题,但在这种情况下不需要生成器。因此,yield 是多余的。另一种实现相同的方法(不使用生成器)是编写自己的 context manager 而不使用内置的 contextlib.contextmanager 装饰器。如下所示。

    class redirect_stdout_stderr(object):
        def __init__(self, stream):
            # Save the old std streams
            self.old_stream = sys.stdout
            self.old_error_stream = sys.stderr
            self.fstream = stream
    
        def __enter__(self):
            # Change the std streams to your streams when entering
            sys.stdout = self.fstream
            sys.stderr = self.fstream
    
        def __exit__(self, exc_type, exc_value, exc_traceback):
            # Change the std streams back to the original streams while exiting
            sys.stdout = self.old_stream
            sys.stderr = self.old_error_stream
    

    在您的情况下,您可以执行以下操作。

    with redirect_stdout_stderr(logstream):
        # __enter__() is executed
        args = parser.parse_args()
        # __exit__() is executed
    

    希望这会有所帮助!

    【讨论】:

      猜你喜欢
      • 2016-08-03
      • 2022-11-11
      • 1970-01-01
      • 2015-03-19
      • 1970-01-01
      • 1970-01-01
      • 2012-12-26
      • 2020-01-24
      • 2014-07-14
      相关资源
      最近更新 更多