【问题标题】:Hide traceback unless a debug flag is set除非设置了调试标志,否则隐藏回溯
【发布时间】:2015-02-24 19:07:58
【问题描述】:

除非设置了详细或调试标志,否则隐藏回溯错误的惯用 python 方法是什么?

示例代码:

their_md5 = 'c38f03d2b7160f891fc36ec776ca4685'
my_md5 = 'c64e53bbb108a1c65e31eb4d1bb8e3b7' 
if their_md5 != my_md5:
    raise ValueError('md5 sum does not match!')

现有输出,但仅在使用 foo.py --debug 调用时才需要:

Traceback (most recent call last):
  File "b:\code\apt\apt.py", line 1647, in <module>
    __main__.__dict__[command] (packages)
  File "b:\code\apt\apt.py", line 399, in md5
    raise ValueError('md5 sum does not match!')
ValueError: md5 sum does not match!

所需的正常输出:

ValueError: md5 sum does not match!

这是一个测试脚本:https://gist.github.com/maphew/e3a75c147cca98019cd8

【问题讨论】:

    标签: python error-handling user-experience


    【解决方案1】:

    简单的方法是使用sys 模块并使用这个命令:

    sys.tracebacklimit = 0
    

    使用您的标志来确定行为。

    例子:

    >>> import sys
    >>> sys.tracebacklimit=0
    >>> int('a')
    ValueError: invalid literal for int() with base 10: 'a'
    

    更好的方法是使用和exception hook:

    def exception_handler(exception_type, exception, traceback):
        # All your trace are belong to us!
        # your format
        print "%s: %s" % (exception_type.__name__, exception)
    
    sys.excepthook = exception_handler
    

    编辑:

    如果你仍然需要回退到原来的钩子的选项:

    def exception_handler(exception_type, exception, traceback, debug_hook=sys.excepthook):
        if _your_debug_flag_here:
            debug_hook(exception_type, exception, traceback)
        else:
            print "%s: %s" % (exception_type.__name__, exception)
    

    现在您可以将调试钩子传递给处理程序,但您很可能希望始终使用源自sys.excepthook 的钩子(因此在debug_hook 中不传递任何内容)。 Python 在定义时绑定默认参数一次(常见的陷阱...),这使得它在替换之前始终与相同的原始处理程序一起工作。

    【讨论】:

    • 谢谢!不过,我很难实施更好的方法。它非常适合用户友好的响应,但我无法详细地做到这一点。 print traceback 只显示对象名称,dir(traceback) 没有显示任何我知道如何处理的内容。这就是我所拥有的:gist.github.com/maphew/e3a75c147cca98019cd8
    • 漂亮的解决方案。您可以使用 lambda 将其变成单线:sys.excepthook = lambda exctype,exc,traceback : print("{}: {}".format(exctype.__name__,exc))
    • python3 警告:上面带有sys.tracebacklimit = 0 的示例不起作用。但是,已定义(且富有洞察力!)函数 exceptionHandler 工作正常,打印内容通常发生变化。
    • 您可以使用sys.__excepthook__,而不是依赖sys.excepthook在函数定义时不重新定义,它始终保持其原始定义,就像在this answer中一样。
    • 这确实应该是默认行为 - 添加回溯页面使用户不太可能查看任何错误消息,因此除非您是代码的实际开发人员,否则会适得其反。
    【解决方案2】:
    try:
        pass # Your code here
    except Exception as e:
        if debug:
            raise # re-raise the exception
                  # traceback gets printed
        else:
            print("{}: {}".format(type(e).__name__, e))
    

    【讨论】:

    • 为我生成 TypeError: exceptions must be old-style classes or derived from BaseException, not NoneType(目前为 py v2.7.4)
    • @matt:在哪一行?我没有 2.7.4,所以看不到错误。
    • 我的错误,我在except ... as e: 块之外有if debug: raiseThis 现在在 repl.it/languages/Python 中工作。 +1 表示可行,但我接受了 Reut 的回答,因为我不希望将所有内容都包含在 try:...except: 中。
    【解决方案3】:

    我会恶狠狠地回答:

    不要隐藏回溯

    Traceback 是向开发人员显示错误 - 未处理的异常。开发者没有准备的东西,没有注意到。一言以蔽之——可憎。

    但你是聪明人。您会看到在某些情况下 md5 总和不会太多,然后您应该结束您的程序。所以也许让我们这样做:

    if their_md5 != my_md5:
      sys.stderr.write('md5 sum does not match!')
      exit(-1)
    

    更重要的是 - 你的开发人员会很感激 - 他们仍然会有他们的回溯,并且永远不会尝试处理此类异常。 很快 - 如果您不想处理它,没有理由引发异常。

    但是……

    如果你真的必须提出异常(所以可能会有一些情况是可以接受的,并且你的开发人员想要处理它,例如使用 sha256,或者它只是巨大项目的一小部分)比你可以这样做:

     def do_sth_with_md5(their_md5, my_md5):
         if their_md5 != my_md5:
             raise ValueError('md5 sum does not match!')
         actual_staff_to_do(their_md5, my_md5)
        (...)
    
     ... somewhere else ...
     try:
         do_sth_with_md5(their, my)
     except ValueError:
         sys.stderr.write('md5 sum does not match!') #or sha256handling... whatever
         exit(-1)
    
         
    

    当然,这些都是简化的例子……

    【讨论】:

    • 如果您在 Spyder 中,exit(-1) 不起作用。据我所知,加薪是唯一的出路。
    • @Tunneller ,Spyder 到底是什么?你是说这个 IDE (spyder-ide.org) 吗?
    • 嗯,我想说“是”,但 Stackoverflow 似乎要我输入一个完整的句子?所以@ravenwing,“是的”。
    • 所以...我会说这是 python 的答案,而不是一些 IDE 特定的答案。我很确定它不使用它自己的python实现。所以...改变你的 IDE ;p
    【解决方案4】:

    使用日志系统来处理错误输出。

    我将使用请求连接错误作为示例用例,它会生成一个级联的 3 个异常,每个异常都有一个冗长的回溯。只有最后的错误消息才是真正重要的——被调用的服务正在拒绝,我不需要知道我的客户端应用程序的 3 页回溯!

    1. 定义自定义错误类
    # file: custom_exceptions.py
    
    class Error(Exception):
        """This class should be used where traceback should not be logged"""
        pass
    
    1. 在内部函数中,捕获异常并将其强制转换为自定义错误类
        try:
            response = requests.get(url, auth=auth, headers=headers, timeout=TIMEOUT)
        except requests.exceptions.ConnectionError as e:
            raise custom_exceptions.Error(e)
    
    1. 在调用函数中以不同方式处理自定义异常和意外异常
        except custom_exceptions.Error as e:    # any custom error based on the custom Error class
            if LOGLEVEL=logging.DEBUG:
                logger.exception(e)     # with traceback
            else:
                logger.error(e)         # just the exception and message
            exit()
        except Exception as e:
            logger.exception(e)         # with traceback
            exit()
    

    生成的日志消息 - 此错误场景所需的所有详细信息:

    2021-07-23 10:58:27,545 [ERROR] HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: /next (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7fc54f11f9d0>: Failed to establish a new connection: [Errno 111] Connection refused'))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-09-03
      • 1970-01-01
      • 2018-10-30
      • 2020-06-21
      • 2014-11-08
      • 2017-12-28
      • 2014-04-09
      • 1970-01-01
      相关资源
      最近更新 更多