【问题标题】:How to properly propagate error messages using Python如何使用 Python 正确传播错误消息
【发布时间】:2022-12-05 17:28:00
【问题描述】:

我对正确的 Python 错误处理有点陌生,我很难找到如何处理几种方法链中错误的野兽方法。

我有 3 种方法 - abca正在呼叫bb正在呼叫c。如何将错误从方法 c 传播回方法 a 这样我就可以,例如存储在某个地方还是在 API 响应期间返回?

示例代码:

def c(x, y):
  try:
    return int(x), int(y)
  except Exception:
    print("x or y is probably not a number")

def b(x, y):
  try:
    x, y = c(x, y)
    return x + y
  except Exception:
    print("issue during sum of x and y")

def a(x, y):
  try:
    return b(x, y)
  except Exception:
    print("some unknown error occured")

 
result = a(4, 5)
result = a('test', 10)

上面的代码在某些情况下会打印出一些错误。很明显,您可以在控制台中看到这些错误,但是以后如何处理这些“错误”消息呢?例如,如果从另一个模块调用方法a,我想返回它们并存储。现在 result 的值为 None,以防发生错误。

换句话说,我想“以某种方式”从方法c 直接跳回到方法a 并显示响应。

像这样返回错误消息本身是正确的方法吗?

def c(x, y):
  try:
    return int(x), int(y)
  except Exception:
    return "x or y is probably not a number"

【问题讨论】:

  • traceback 模块具有获取当前异常信息的函数,docs.python.org/3/library/traceback.html
  • @Filip_Niko 感谢 anton 的评论,我已经使用回溯更新了答案。现在,如果函数失败,装饰器将回溯作为字符串返回

标签: python exception error-handling


【解决方案1】:

错误传播

如果你想传播异常,你可以使用 use from 子句

所有信息都可以在这里查看https://docs.python.org/3/reference/simple_stmts.html#grammar-token-raise-stmt

def c(x, y):
    try:
        return int(x), int(y)
    except Exception as e:   
        msg = "x or y is probably not a number"
        raise Exception(msg) from e   

def b(x, y):
    try:
        x, y = c(x, y)
        return x + y 
    except Exception as e:
        msg = "issue during sum of x and y"
        raise Exception(msg) from e   

def a(x, y):
    try:
        return b(x, y)
    except Exception as e:
        msg = "some unknown error occured"
        raise Exception(msg) from e    

result = a('test', 10)

这样您就可以一直追踪所有错误及其来源

这是输出: https://i.stack.imgur.com/2ho5K.png

回溯抑制

如果你想抑制你需要使用的错误的所有回溯 from None

def a(x, y):
    try:
        return b(x, y)
    except Exception as e:
        msg = "some unknown error occured"
        raise Exception(msg) from None   

现在运行代码将抑制所有以前的错误,这是输出: https://i.stack.imgur.com/loFis.png

将错误恢复为字符串

到目前为止,捕获我发现的错误的最佳方法,感谢@anton 的 cmets,他建议使用 traceback 是使用装饰器

from functools import wraps, partial
import traceback  
import sys


def error_handler(func=None, *, raise_error=False):
    if not func:
        return partial(error_handler, raise_error=raise_error)
    
    @wraps(func)
    def func_exectutor(*args, **kwargs):
        try:
            out = func(*args, **kwargs)
            exception = False
        except Exception as error:
            exception = True
            string = ''.join(traceback.format_exception(None, error, error.__traceback__))
            out = string, error
        finally:
            if not exception:
                return out
            else:
                if raise_error:
                    raise out[1]
                else:
                    return out[0]
    return func_exectutor

装饰功能后:

@error_handler(raise_error=False)
def a(x, y):
    try:
        return b(x, y)
    except Exception as e:
        msg = "some unknown error occured"
        raise Exception(msg) from e   

如果失败,该函数的输出是一个字符串,其中包含所有可能发生的错误的回溯。如果不是,则输出是预期的

a(4, 10)
>>> 14

a('test', 10)
>>> 'Traceback (most recent call last):
  File "/tmp/ipykernel_60630/2959315277.py", line 3, in c
    return int(x), int(y)
ValueError: invalid literal for int() with base 10: 'test'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/tmp/ipykernel_60630/2959315277.py", line 10, in b
    x, y = c(x, y)
  File "/tmp/ipykernel_60630/2959315277.py", line 6, in c
    raise Exception(msg) from e
Exception: x or y is probably not a number

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/tmp/ipykernel_60630/2959315277.py", line 19, in a
    return b(x, y)
  File "/tmp/ipykernel_60630/2959315277.py", line 14, in b
    raise Exception(msg) from e
Exception: issue during sum of x and y

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/tmp/ipykernel_60630/879165569.py", line 13, in func_exectutor
    out = func(*args, **kwargs)
  File "/tmp/ipykernel_60630/2959315277.py", line 22, in a
    raise Exception(msg) from e
Exception: some unknown error occured
'

【讨论】:

  • 感谢您的评论。它可以工作,除非我需要稍后处理错误消息(例如,将其作为 API 响应发送),对吗?我知道加薪,但据我所知,谷歌似乎只是核心会崩溃,你可以在控制台中看到错误。这不是我的情况。让我知道这是否不清楚,我需要改进我的问题。谢谢
  • 哦,我完全想念这个问题的概念。对不起,让我想想办法
  • @antont 感谢您的评论,我已经使用回溯更新了答案。现在,如果函数失败,装饰器将回溯作为字符串返回
  • 伙计们,谢谢!这看起来很有希望。我将尝试将其实现到我的代码库中。 PS:日志记录不是一个选项,因为我需要将错误消息作为 API 请求的一部分返回(所有事件仍被记录)。
  • @antont,装饰器的使用使您可以避免在您可能认为会失败并处理错误的每个函数中使用 try 和 except 子句,因为装饰器会自行处理异常。一旦将装饰器放在任何函数之上(包含或不包含 try except 子句),如果函数失败,它将以字符串形式返回错误或引发此错误(raise_error 参数控制不同的情况)。如果该函数工作正常,它将返回正常输出。
猜你喜欢
  • 2018-10-22
  • 1970-01-01
  • 2022-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-22
相关资源
最近更新 更多