Кто / Как получить контроль над программой после возникновения исключения - PullRequest
0 голосов
/ 07 декабря 2018

Мне всегда было интересно, кто берет на себя управление программой после исключения.Я искал четкого ответа, но не нашел никакого.У меня описаны следующие функции, каждая из которых выполняет вызов API, который включает в себя сетевой запрос, поэтому мне нужно обрабатывать любые возможные ошибки с помощью блока try / исключением и, возможно, еще (ответы 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 повторяет попытку самостоятельно N раз, когда я вызываю метод get, где N - константа в исходном коде,равно 100. Но когда число повторных попыток достигнет 0, будет выдано сообщение об ошибке, которое мне нужно перехватить.

Где мне декодировать ответ JSON?Внутри каждой функции и обернуты другим блоком try / catch / else?или в основном блоке?Как я могу восстановиться после исключения и продолжать примерять функцию, в которой оно вышло из строя?

Любой совет будет благодарен.

Ответы [ 3 ]

0 голосов
/ 07 декабря 2018

Где я должен декодировать ответ 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)
0 голосов
/ 07 декабря 2018

Вы можете держать их в бесконечном цикле (чтобы избежать рекурсии), и как только вы получите ожидаемый ответ, просто верните:

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) в противном случае он будет пытаться снова и снова.

0 голосов
/ 07 декабря 2018

Вы можете сделать следующее

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()
...