Контекстный менеджер Python: временная подмена списка с восстановлением

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

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

Временный учебный канал пульта

Центральный пульт диспетчерской отправляет оперативные извещения в активный канал console.receiver. Во время учебных тревог этот поток нужно временно перехватывать, чтобы тестовые сигналы не смешивались с реальными сообщениями. Для этого вход в учебный режим подменяет рабочий список в приёмнике на отдельный чистый буфер и передаёт его для накопления записей в блок with.

При любом выходе из тренировочной сессии исходный приёмник гарантированно возвращается в console.receiver. Штатный рабочий канал и независимый внешний накопитель archive никогда не ссылаются на один и тот же список. Если тренировка прошла без ошибок, накопленные сообщения дописываются в конец archive и остаются в самом учебном буфере. Если же внутри блока произошёл сбой, выданный буфер очищается на месте вызовом clear(), в архив ничего не добавляется, а возникшая ошибка пробрасывается наружу.

Реализуйте функцию-менеджер контекста с декоратором @contextlib.contextmanager, которая изолирует учебные записи пульта во временном буфере, надёжно восстанавливает рабочий приёмник и пополняет внешний архив только при успешном завершении сессии.

Что уже дано

В заготовке уже импортирован декоратор contextmanager из модуля contextlib и объявлена заготовка функции temporary_alert_channel(console, archive). Вспомогательные классы AlertConsole, его тестовые варианты и проверочные функции observe_prepared_empty_alert, observe_alert_sessions и observe_alert_redirected_buffer уже доступны в окружении при выполнении, реализовывать их заново не требуется.

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

Реализуйте генераторную функцию-менеджер контекста temporary_alert_channel(console, archive) с декоратором @contextmanager.

Функция принимает два аргумента:

  • console — объект пульта с перезаписываемым атрибутом receiver (текущий рабочий список list[str]) и методом emit(record: str), который добавляет запись в конец текущего установленного приёмника console.receiver. Требовать точный конкретный тип от пульта нельзя: важен только интерфейс;
  • archive — внешний накопительный список строк list[str], объект которого должен оставаться неизменным. При входе исходный приёмник пульта и архив гарантированно являются двумя разными объектами списков (их совпадение по ссылке не входит в поддерживаемые входные данные, добавлять проверку или особое исключение для этого случая не нужно).

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

  • Генератор инициализируется лениво: подготовка и переключение канала происходят только при фактическом входе в блок with.
  • При входе в контекст менеджер запоминает ровно тот объект приёмника, который установлен в console.receiver в этот момент, создаёт свежий пустой список [] для учебных записей, устанавливает его в console.receiver и отдаёт этот же список наружу через yield.
  • При любом выходе из блока with (как успешном, так и при исключении) в атрибут console.receiver должен быть гарантированно возвращён исходный сохранённый объект приёмника по идентичности (is), с сохранением его содержимого.
  • При успешном выходе из блока все элементы выданного учебного буфера последовательно дописываются в конец существующего списка archive. Сам учебный буфер при этом не очищается и не заменяется.
  • Если внутри блока with возникло любое исключение (BaseException), в archive ничего не добавляется, выданный учебный буфер очищается на месте вызовом .clear(), а само исключение беспрепятственно распространяется дальше без подавления или подмены.
  • Правило принадлежности буфера: архивация при успехе и очистка при ошибке всегда применяются именно к исходно выданному учебному списку, даже если внутри тела with атрибут console.receiver был переназначен на другой список. Подменять архив или очищать сторонний список запрещено.
  • Каждый вызов temporary_alert_channel создаёт независимый однократный менеджер контекста с новым буфером. Учебные буферы предыдущих успешных сессий должны оставаться нетронутыми.

Объём проверочных данных: от 0 до 100 последовательных сессий, от 0 до 1000 строк в исходных списках и до 1000 строк длиной до 100 символов за одну сессию.

Ввод и вывод

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

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

console = AlertConsole(['main'])
archive = ['old']
with temporary_alert_channel(console, archive) as training:
    console.emit('test')
print(console.receiver)
print(training)
print(archive)
try:
    with temporary_alert_channel(console, archive) as failed:
        console.emit('discard')
        raise ValueError('stop')
except ValueError:
    print(failed)
print(console.receiver)
print(archive)

Вывод:

['main']
['test']
['old', 'test']
[]
['main']
['old', 'test']
РешениеPython

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

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

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