【问题标题】:Who/How to get the control of the program after an exception has ocurred发生异常后谁/如何获得程序的控制权
【发布时间】:2018-12-07 14:31:22
【问题描述】:

我一直想知道抛出异常后谁来控制程序。我正在寻找一个明确的答案,但没有找到任何答案。我描述了以下函数,每个函数都执行一个涉及网络请求的 API 调用,因此我需要通过 try/except 和可能的 else 块处理任何可能的错误(JSON 响应也必须被解析/解码):

# This function runs first, if this fails, none of the other functions will run. Should return a JSON.
def get_summary():
    pass

# Gets executed after get_summary. Should return a string.
def get_block_hash():
    pass

# Gets executed after get_block_hash. Should return a JSON.
def get_block():
    pass

# Gets executed after get_block. Should return a JSON.
def get_raw_transaction():
    pass

我希望在每个函数上实现一种重试功能,所以如果由于超时错误、连接错误、JSON解码错误等原因而失败,它会在不影响程序流程的情况下继续重试:

def get_summary():
    try:
        response = request.get(API_URL_SUMMARY)
    except requests.exceptions.RequestException as error:
        logging.warning("...")
        #
    else:
        # Once response has been received, JSON should be 
        # decoded here wrapped in a try/catch/else
        # or outside of this block?
        return response.text

def get_block_hash():
    try:
        response = request.get(API_URL + "...")
    except requests.exceptions.RequestException as error:
        logging.warning("...")
        #
    else:
        return response.text

def get_block():
    try:
        response = request.get(API_URL + "...")
    except requests.exceptions.RequestException as error:
        logging.warning("...")
        #
    else:
        #
        #
        #
        return response.text

def get_raw_transaction():
    try:
        response = request.get(API_URL + "...")
    except requests.exceptions.RequestException as error:
        logging.warning("...")
        #
    else:
        #
        #
        #
        return response.text

if __name__ == "__main__":
    # summary = get_summary()
    # block_hash = get_block_hash()
    # block = get_block()
    # raw_transaction = get_raw_transaction()
    # ...

我想在它的最外层保留干净的代码(if __name__ == "__main__": 之后的块),我的意思是,我不想用混乱的 try/catch 块、日志记录等填充它。

当任何这些函数发生异常时,我尝试调用函数本身,但后来我读到堆栈限制并认为这是一个坏主意,应该有更好的方法来处理这个问题。

request在我调用get方法的时候已经自己重试了N次,其中N在源码中是一个常数,是100。但是当重试次数达到0时会抛出错误I需要抓住。

我应该在哪里解码 JSON 响应?在每个函数内部并被另一个 try/catch/else 块包裹?还是在主街区?如何从异常中恢复并继续尝试失败的功能?

任何建议将不胜感激。

【问题讨论】:

  • 我不确定您所说的“谁”是什么意思?您对这个问题有哪些设想?
  • @roganjosh 它指的是主执行块(在if __name__... 之后)或通过再次调用自身来抛出异常的同一函数。

标签: python exception python-requests


【解决方案1】:

您可以将它们保持在无限循环中(以避免递归),一旦获得预期的响应,就返回:

def get_summary():
    while True:
        try:
            response = request.get(API_URL_SUMMARY)
        except requests.exceptions.RequestException as error:
            logging.warning("...")
            #
        else:
            # As winklerrr points out, try to return the transformed data as soon 
            # as possible, so you should be decoding JSON response here.
            try:
                json_response = json.loads(response)
            except ValueError as error: # ValueError will catch any error when decoding response
                logging.warning(error)
            else:
                return json_response

这个函数一直执行,直到它收到预期的结果(达到return json_response),否则它将一次又一次地尝试。

【讨论】:

    【解决方案2】:

    您可以执行以下操作

    def my_function(iteration_number=1):
    
        try:
            response = request.get(API_URL_SUMMARY)
        except requests.exceptions.RequestException: 
            if iteration_number < iteration_threshold:
                my_function(iteration_number+1)
            else:
                raise
        except Exception: # for all other exceptions, raise
            raise
    
        return json.loads(resonse.text)
    
    
    my_function()
    

    【讨论】:

      【解决方案3】:

      我应该在哪里解码 JSON 响应? 在每个函数内部并由另一个 try/catch/else 块或在主块中包装?

      一般来说:尝试尽快将数据转换为您想要的格式。如果您不必一直从响应对象中再次提取所有内容,它会使您的其余代码更容易。因此,只需以最简单的格式返回您需要的数据。

      在您的场景中:您在每个函数中调用该 API,并使用对 requests.get() 的相同调用。通常,来自 API 的所有响应都具有相同的格式。所以这意味着,您可以编写一个额外的函数来调用 API 并将响应直接加载到适当的 JSON 对象中。

      提示:要使用 JSON,请使用 import json 的标准库

      示例:

      import json
      
      def call_api(api_sub_path):
          repsonse = requests.get(API_BASE_URL + api_sub_path)
          json_repsonse = json.loads(repsonse.text) 
      
          # you could verify your result here already, e.g.
          if json_response["result_status"] == "successful":
              return json_response["result"]
      
          # or maybe throw an exception here, depends on your use case        
          return json_response["some_other_value"] 
      

      如何从异常中恢复并继续尝试失败的功能?

      您可以为此使用 while 循环:

      def main(retries=100): # default value if no value is given
          result = functions_that_could_fail(retries)
      
          if result:
              logging.info("Finished successfully")
              functions_that_depend_on_result_from_before(result)
          else:
              logging.info("Finished without result")
      
      def functions_that_could_fail(retry): 
          while(retry): # is True as long as retry is bigger than 0
              try: 
                  # call all functions here so you just have to write one try-except block
                  summary = get_summary()
                  block_hash = get_block_hash()
                  block = get_block()
                  raw_transaction = get_raw_transaction()
              except Exception:
                  retry -= 1
                  if retry:
                      logging.warning("Failed, but trying again...")
              else: 
                  # else gets only executed when no exception was raised in the try block
                  logging.info("Success")
                  return summary, block_hash, block, raw_transaction
      
          logging.error("Failed - won't try again.")
          result = None
      
      def functions_that_depend_on_result_from_before(result):
          [use result here ...]
      

      因此,使用上面的代码(可能还有其他一些使用您的代码的人)可以使用以下代码启动您的程序:

      if __name__ == "__main__":
          main()
      
          # or when you want to change the number of retries
          main(retries=50)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-03-04
        • 1970-01-01
        • 2019-01-28
        • 1970-01-01
        • 2014-10-22
        • 1970-01-01
        • 1970-01-01
        • 2021-07-25
        相关资源
        最近更新 更多