Как подавить отображение родительского исключения (причины) для последующих исключений

Я в курсе raise ... from None и читал как я могу более легко подавлять предыдущие исключения, когда я поднимаю свое собственное исключение в ответ?.

однако, как я могу достичь того же эффекта (подавления сообщения "во время обработки вышеуказанного исключения произошло другое исключение"), не имея контроля над кодом, который выполняется из предложения except? Я так и думал!--3--> может использоваться для этого, но эта функция не существует в Python 3.

почему я спрашиваю об этом? У меня есть простой код кэширования, который выглядит как (упрощенный):

try:
    value = cache_dict[key]
except KeyError:
    value = some_api.get_the_value_via_web_service_call(key)
    cache_dict[key] = value

когда в вызове API есть исключение, вывод будет примерно таким:

Traceback (most recent call last):
  File ..., line ..., in ...
KeyError: '...'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File ..., line ..., in ...
some_api.TheInterestingException: ...

это вводит в заблуждение, так как исходный KeyError на самом деле не является ошибкой вообще. Я мог бы, конечно, избежать ситуации, изменив try / except (EAFP) в тест на наличие ключа (LBYL), но это не очень Питоническое и менее потокобезопасное (не то вышеизложенное является потокобезопасным, как есть, но это к делу не относится).

неразумно ожидать, что весь код в some_api изменит их raise X до raise X from None (и это даже не имеет смысла во всех случаях). Есть ли чистое решение, чтобы избежать нежелательной цепочки исключений в сообщении об ошибке?

(кстати, бонусный вопрос: кэш, который я использовал в Примере, в основном эквивалентен cache_dict.setdefault(key, some_api.get_the_value_via_web_service_call(key)), если бы только второй аргумент setdefault мог быть вызываемым, что бы вызывается только тогда, когда необходимо задать значение. Разве нет лучшего / канонического способа сделать это?)

2 ответов


у вас есть несколько вариантов здесь.

во-первых, более чистая версия того, что предложил orlp:

try:
    value = cache_dict[key]
except KeyError:
    try:
        value = some_api.get_the_value(key)
    except Exception as e:
        raise e from None
    cache_dict[key] = value

для второго варианта, я предполагаю, что есть return value прячется там где-то, что вы не показываете:

try:
    return cache_dict[key]
except KeyError:
    pass
value = cache_dict[key] = some_api.get_the_value(key)
return value

третий вариант, LBYL:

if key not in cache_dict:
    cache_dict[key] = some_api.get_the_value(key)
return cache_dict[key]

для Бонусного вопроса определите свой собственный подкласс dict, который определяет __missing__:

class MyCacheDict(dict):

    def __missing__(self, key):
        value = self[key] = some_api.get_the_value(key)
        return value

надеюсь, что это помогает!


вы можете попробовать подавить контекст самостоятельно:

try:
    value = cache_dict[key]
except KeyError:
    try:
        value = some_api.get_the_value_via_web_service_call(key)
    except Exception as e:
        e.__context__ = None
        raise

    cache_dict[key] = value