Python try/finally 与 sys.exc_info()

Python try/finally wtih sys.exc_info()

我正在尝试编写一个 python 作业,我将在所有其他作业中使用它来捕获作业开始时间,当程序成功完成或出现一些问题时,我捕获错误详细信息并将详细信息加载到 table.

job_status

def srt(jobname):
   sts = 'running'
   df_srt = select(f'''select /'{job_name}/' as job_name, /'{strt_time}/' as strt_time, /'{sts}/' as sts''')
   df_srt.write.to_csv(/path_name)

def end(jobname):
   df_end = select(f'''select /'{job_name}/' as job_name, /'{strt_time}/' as strt_time, /'{end_time}/', /'{log}/' as log, /'{sts}/' as sts ''')
   df_end.write.to_csv(/path_name)

我正在使用作业的程序

from job_status import *

    def main_program:
       try:
          # some operation
          print(1/0)
       except:
         pass
       finally:
          if sys.exc_info()[0] is not None:
                status = 'Failed'
                log = concat(sys.exc_info()[0],' , ', sys.exc_info()[1], ' , ', sys.exc_info()[2])
                end_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                end(job_name=__name__, strt_time=start_time, end_time=end_time, sts=sts, log=log)
            else:
                sts = 'Success'
                end_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                end(job_name=__name__, start_time=start_time, end_time=end_time, status=status)

每当出现问题时,我都会尝试捕获错误详细信息。但无论错误的性质如何,sys.exc_info()[0] 都会进入成功路径。任何想法,我如何实现这个?

sys.exc_info() 包含当前正在处理的异常 的信息。来自 docs(强调我的):

This function returns a tuple of three values that give information about the exception that is currently being handled. [...] If the current stack frame is not handling an exception, the information is taken from the calling stack frame, or its caller, and so on until a stack frame is found that is handling an exception. Here, “handling an exception” is defined as “executing an except clause.” For any stack frame, only information about the exception being currently handled is accessible. If no exception is being handled anywhere on the stack, a tuple containing three None values is returned.

finally 子句中,您已经过去了 处理异常的时间点。因此,您需要从 except 子句的 inside 中保存有关异常的相关信息。例如:

exc_info = None
try:
    # some operation
    print(1/0)
except:
    exc_info = sys.exc_info()
finally:
    if exc_info is not None:
        status = 'Failed'
        # ...
    else:
        status = 'Success'
        # ...

(注意:注释表明 OP 中的代码并非旨在捕获异常;相反,其目的只是记录成功或失败,并让原始异常传播。此处的代码已被修改以实现该目的。)

try命令包括三种附加子句:exceptelsefinally:

  • "except" [expression ["as" identifier]]

    如果提供表达式,则在抛出与表达式匹配的异常时执行该子句。如果未提供表达式,则在抛出任何异常时执行该子句。

  • else

    如果没有抛出异常,则执行该子句。

  • finally

    该子句在 try 块的末尾执行,在任何匹配的 exceptelse 子句之后。

(具体语法见[注1],详情见Python docs

sys.exc_info 可用于 except 子句中 (或在执行 except 子句期间调用的函数中)提取有关异常的信息。一旦 except 子句结束,sys.exc_info 就不再是 returns 有用的信息。由于 finally 子句在 except 子句完成后执行,因此您的代码就是这种情况。

但是你试图用 if exc_info is not None: 做的正是 try 语句的 else 子句所做的。因此,您可以使用 else 子句更简单地编写代码。我做了一些重构,以便能够将通用代码放在 finally 子句中。

为了传播异常,有必要用 raise 语句结束 except 子句,而不用表达式。这将导致异常继续“就像它是由 try 块引发的一样”。但是,finally 子句仍然在传播之前执行。

def main_program:
    start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    try:
        # some operation
        print(1/0)
    except:
        status = 'Failed'
        log = concat(sys.exc_info()[0],' , ', sys.exc_info()[1], ' , ', sys.exc_info()[2])
        raise
    else:
        status = 'Success'
        log = None
    finally:
        end_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        end(job_name=__name__, start_time=start_time, end_time=end_time, sts=sts, log=log)

备注:

  1. try 语句的精确语法表明您必须至少有一个 except 子句或一个 finally 子句,并且您只能有一个 else 子句,如果你有 except 子句。从文档:
    try_stmt  ::=  try1_stmt | try2_stmt
    try1_stmt ::=  "try" ":" suite
                   ("except" [expression ["as" identifier]] ":" suite)+
                   ["else" ":" suite]
                   ["finally" ":" suite]
    try2_stmt ::=  "try" ":" suite
                   "finally" ":" suite
    
  2. 我不知道 concat 应该在这里做什么,所以我就保持原样。它不能是简单的字符串连接,因为 sys.exc_info 返回的元组的组成部分不是字符串,如果那是您正在寻找的内容,', '.join(map(str, exc_info())) 就可以。如果它是一些可以很好地格式化 sys.exc_info 返回的对象的函数,那么您可以考虑只将 sys.exc_info.
  3. 返回的对象传递给它