Надлежащая осторожность и безопасность при работе с объектами трассировки из sys.exc_info ()

Мне известно, что sys.В документации exc_info говорится, что нужно проявлять осторожность при работе с объектами трассировки, но я все еще не уверен, насколько безопасны или небезопасны некоторые случаи. Кроме того, в документации говорится: «Предупреждение: не делайте этого!», За которым сразу следует «Примечание: на самом деле, все в порядке», что еще больше меня смущает.

В любом случае, документы и « Почему необходимо явно удалять трассировку sys.exc_info () в Python? » (ответ Алекса Мартелли), похоже, подразумевают его единственные локальные переменные, которые ссылка на присвоенное им значение трассировки, вызывающее проблему.

Это оставляет мне несколько вопросов:

  1. Что именно означает «локальная переменная» в данном контексте? Я борюсь за терминологию, но: означает ли это только переменные, созданные в функции, или также переменные, созданные параметрами функции? А как насчет других переменных в области видимости, например глобальных или self?
  2. Как замыкания влияют на потенциальные циклические ссылки трассировки? Общая мысль такова: замыкание может ссылаться на все, что может выполнять его включающая функция, поэтому трассировка со ссылкой на замыкание может в конечном итоге содержать довольно много ссылок. Я изо всех сил пытаюсь придумать более конкретный пример, но какая-то комбинация: внутренней функции, кода, который возвращает sys.exc_info (), с дорогими-короткоживущими объектами где-то в области видимости.

Не стесняйтесь сказать мне, где мои выводы или предположения ошибочны, поскольку я несколько раз убеждал себя в вере или неверии в свои собственные утверждения, когда писал это :).

Хоть я и хотел бы получить ответы на свои конкретные примеры, я также прошу общих советов, знаний или военных историй о том, как безопасно справляться с трассировкой в ​​более эзотерических ситуациях (например, вы должны запустить цикл и захотеть чтобы накапливать любые возникшие исключения, вам необходимо создать новый поток и сообщить о любых возникших в нем исключениях, вам необходимо создать закрытие и обратные вызовы, а также сообщить о возбужденных исключениях и т. д.).

Пример 1: внутренняя функция, выполняющая обработку ошибок.

def DoWebRequest():
  thread, error_queue = CreateThread(ErrorRaisingFunc)
  thread.start()
  thread.join()
  if not error_queue.empty():
    # Purposefully not calling error_queue.get() for illustrative purposes
    print 'error!'

def CreateThread(func):
  error_queue = Queue.Queue()
  def Handled():
    try:
      func()
    except Exception:
      error_queue.put(sys.exc_info())
  thread = threading.Thread(target=Handled)
  return thread, error_queue

Вызывает ли закрытие Handled () какое-либо возбужденное исключение для ссылки error_queue и приводит к циклической ссылке, потому что error_queue также содержит трассировку? Достаточно ли удаления трассировки из error_queue (т.е. вызова .get () ), чтобы исключить циклическую ссылку?

Пример 2: долгоживущий объект в области exc_info, или возврат exc_info

long_lived_cache = {}

def Alpha(key):
  expensive_object = long_lived_cache.get(key)
  if not expensive_object:
    expensive_object = ComputeExpensiveObject()
    long_lived_cache[key] = expensive_object

  exc_info = AlphaSub(expensive_object)
  if exc_info:
    print 'error!', exc_info

def AlphaSub(expensive_object):
  try:
    ErrorRaisingFunc(expensive_object)
    return None
  except Exception:
    return sys.exc_info()

Имеет ли вызванное исключение AlphaSub () ссылку на own_object , и, поскольку Road_object кэширован, трассировка никогда не исчезает ? Если да, то как разорвать такой цикл?

В качестве альтернативы, exc_info содержит кадр стека Alpha , а кадр стека Alpha содержит ссылку на exc_info , в результате получается циклическая ссылка. Если да, то как разорвать такой цикл?

9
задан martineau 21 June 2017 в 17:56
поделиться