Как назначить уровень стека для предупреждения в зависимости от вызывающего абонента? - PullRequest
0 голосов
/ 28 января 2019

У меня есть класс Python, который выдает предупреждение внутри __init__().Он также предоставляет метод фабричного класса для открытия и чтения файла:

from warnings import warn

class MyWarning(Warning):
    """Warning issued when an invalid name is found."""
    pass

class MyClass:
    def __init__(self, names):
        # Simplified; actual code is longer
        if is_invalid(names):
            names = fix_names(names)
            warn(f'{names!r} contains invalid element(s)',
                MyWarning, stacklevel=2)
        self._names = names

    @classmethod
    def from_file(cls, filename):
        with open(filename) as file:
            names = extract_names(file)
        return cls(names)

stacklevel=2 заставляет предупреждение относиться к вызову MyClass(), а не к самому оператору warn().Это работает, когда пользовательский код напрямую создает экземпляр MyClass.Однако, когда MyClass.from_file() выдает предупреждение, MyWarning относится к return cls(names), а не к пользовательскому коду, вызывающему from_file().

. Как я могу убедиться, что фабричный метод также выдает предупреждение, указывающее навызывающий?Некоторые варианты, которые я рассмотрел:

  1. Добавьте «скрытый» параметр _stacklevel к __init__() и создайте экземпляр MyClass с _stacklevel=2 внутри from_file().
    • Это очень уродливо и подвергает API внутреннему поведению.
  2. Добавьте «скрытый» атрибут класса _stacklevel и получите доступ к нему внутри __init__().Затем временно измените этот атрибут в from_file()
    • Также очень уродливо.
  3. Добавьте метод _set_names(), который проверяет / исправляет имена и при необходимости выдает предупреждение.Затем вызовите этот метод внутри конструктора.Для from_file() сначала создайте экземпляр MyClass с пустыми аргументами, а затем напрямую вызовите _set_names(), чтобы MyWarning указывал на вызывающего.
    • По-прежнему хакерский и эффективно вызывает _set_names() дважды, когда вызывается from_file().
  4. Поймать и повторно выдать предупреждение, подобно цепочке исключений.
    • Звучит хорошо, но я понятия не имею, как это сделать.

Я прочитал warning модуль документации , но этопредлагает небольшую помощь по безопасному отлову и повторному выбрасыванию предупреждений.Преобразование предупреждения в исключение с использованием warnings.simplefilter() приведет к прерыванию MyClass() и заставит меня вызвать его снова.

...