Класс-декоратор Python: переключение источника после ошибки

Напишите решение на Python и проверьте его на странице.

Другие упражнения по этой теме: Классы-декораторы Python: задачи на call, методы и состояние.

Резервное питание прибора

Лабораторный прибор выполняет серию замеров и связывается с силовой шиной через функцию опроса, принимающую именованный параметр источника питания line. В штатном режиме обращения направляются в основную сеть со значением line="grid". За непрерывностью работы следит управляющий декоратор, удерживающий текущее состояние линии.

Если при обращении к основной сети возникает сетевой сбой ConnectionError, декоратор перехватывает эту ошибку, переводит внутренний тумблер на резервную батарею (line="battery") и сразу повторяет замер ровно один раз уже с новым источником. Все последующие замеры прибор продолжает выполнять исключительно от батареи, не предпринимая попыток вернуться к сети самостоятельно, пока оператор не выполнит явный сброс вызовом метода restore_grid().

Любые другие неполадки вроде неверных аргументов ValueError не влияют на положение тумблера и сразу пробрасываются вызывающему коду. Точно так же наружу передаются любые сбои при работе от батареи, включая неудачу повторного вызова после переключения.

Реализуйте класс-декоратор, который избирательно реагирует на сетевой сбой основной линии мгновенным переходом на резервную батарею, повторяет замер ровно один раз и сохраняет аварийный режим до явного восстановления.

Что уже дано

В редакторе представлена заготовка класса-декоратора BackupPower с методами __init__, __call__ и restore_grid. Во время тестирования доступна вспомогательная функция наблюдения observe_backup_power, реализовывать которую не требуется.

Что нужно сделать

Реализуйте класс-декоратор BackupPower.

Конструктор __init__(self, func) принимает целевую синхронную функцию func, которая помимо аргументов вызывающей стороны принимает строго именованный параметр line (line="grid" или line="battery").

Экземпляр класса должен поддерживать вызов __call__(self, *args, **kwargs) и метод сброса restore_grid(self):

  • Начальное состояние каждого экземпляра декоратора — основная сеть ("grid"). Экземпляры декоратора независимы друг от друга и сохраняют собственное состояние линии.
  • Вызывающий код передаёт любые позиционные и именованные аргументы (*args, **kwargs), сохраняя их идентичность. Параметр line зарезервирован за декоратором и вызывающей стороной не передаётся.
  • При работе от сети вызывается целевая функция с переданными аргументами и line="grid".
  • Только ошибки семейства ConnectionError (включая любые их подклассы), возникшие при вызове с line="grid", переводят внутреннее состояние линии на батарею ("battery"). Переключение линии происходит до выполнения повторного вызова. Сразу после переключения декоратор выполняет ровно одну попытку повторного вызова с line="battery" и теми же аргументами.
  • Любые другие исключения (включая другие подклассы Exception или BaseException), возникшие при работе от сети, не меняют состояние линии (остаётся "grid") и сразу пробрасываются вызывающему коду без повторного вызова.
  • Если декоратор уже находится в режиме батареи, целевая функция вызывается один раз с line="battery". Любая ошибка при вызове от батареи (в том числе при единственной повторной попытке сразу после переключения) немедленно пробрасывается наружу без повторных попыток.
  • После перехода на батарею все последующие вызовы продолжают выполняться с line="battery", пока не будет вызван метод restore_grid().
  • Метод restore_grid() сбрасывает состояние линии текущего экземпляра обратно на "grid". Метод не вызывает целевую функцию и не обязан возвращать значение.
  • При успешном выполнении целевой функции возвращается её оригинальный результат (включая ложные значения None, False, 0 и изменяемые объекты).

Ввод и вывод

Стандартные потоки ввода stdin и вывода stdout в решении не используются: считывать ввод и вызывать print() не нужно. После выполнения кода учащегося проверяющая система считывает из пространства имён модуля класс верхнего уровня BackupPower и вызывает его с проверочными аргументами.

Пример использования

attempts = []
@BackupPower
def measure(*, line):
    attempts.append(line)
    if len(attempts) == 1:
        raise ConnectionError('grid unavailable')
    return line
print(measure())
print(measure())
measure.restore_grid()
print(measure())
print(attempts)

Вывод:

battery
battery
grid
['grid', 'battery', 'battery', 'grid']
РешениеPython

Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.

Отправьте решение, чтобы увидеть результаты тестов.

Связанные задачи