Контекстный менеджер Python: вложенные транзакции и глубина входа
Напишите решение на Python и проверьте его на странице.
Другие упражнения по этой теме: Контекстные менеджеры Python: задачи на with, откат и исключения.
Менеджер внешних транзакций
Модуль пакетной обработки выполняет иерархические операции над единым реестром задач. Разные функции могут вызывать друг друга и вложенно открывать сессии синхронизации через один и тот же менеджер контекста.
Чтобы не создавать лишних накладных расходов, система отслеживает общую глубину вложенности. При самом первом входе в блок контекстный менеджер фиксирует контрольную точку и выставляет флаг монопольного владения. Если внутри уже открытой сессии происходит повторный вход, повторный захват ресурса не выполняется: менеджер лишь увеличивает счётчик активных контекстов.
При выходе из блоков счётчик уменьшается, но фактическая финализация реестра откладывается до выхода из самого внешнего контекста, когда глубина обращается в ноль. Любая ошибка на любом уровне вложенности переводит сессию в аварийный режим, и при итоговом завершении внешнего блока вся цепочка операций должна быть полностью отменена.
Спроектируйте менеджер контекста для управления глубиной вложенности, который изолирует промежуточные вызовы и выполняет фиксацию либо откат изменений строго при закрытии самого верхнего блока.
Что уже дано
В среде выполнения доступен класс реестра задач и вспомогательные фабрики, включая make_registry(initial). У передаваемого ресурса есть:
data— рабочий изменяемый словарь с данными реестра (строковые ключи, целочисленные значения);active— булев флаг активности транзакции (изначальноFalse);events— список зафиксированных событий (изначально пуст);- метод
begin()— сохраняет снимок словаряdata, устанавливаетactive = Trueи добавляет'begin'вevents; - метод
commit()— сохраняет текущие изменения словаряdata, сбрасываетactiveвFalseи добавляет'commit'вevents; - метод
rollback()— восстанавливает словарьdataиз сохранённого снимка, сбрасываетactiveвFalseи добавляет'rollback'вevents.
Вам предстоит реализовать класс OutermostTransaction.
Что нужно сделать
Реализуйте класс OutermostTransaction(resource) с поддержкой протокола контекстного менеджера (__enter__ и __exit__).
Класс должен соблюдать следующие правила:
- Конструктор принимает объект реестра
resource. - При самом первом входе в контекст (на внешнем уровне) вызывается
resource.begin(). При любых вложенных входах в тот же экземпляр менеджера повторный вызовbegin()не выполняется — увеличивается лишь текущая глубина вложенности. - Метод
__enter__()на любом уровне вложенности возвращает именно оригинальный словарьresource.data. - Во время работы всех вложенных блоков сессия остаётся активной. На промежуточных выходах из вложенных контекстов методы
commit()иrollback()не вызываются. - Если в метод
__exit__на любой глубине передаётся информация об исключении (exc_type is not None), сессия безвозвратно помечается как аварийная. Даже если вызывающий код перехватил эту ошибку снаружи внутреннего блока и продолжил выполнение внешнего блока, статус аварийного сбоя для всей сессии сохраняется. - Исключения никогда не подавляются: метод
__exit__должен позволять им всплывать дальше (возвращаяFalseилиNone), сохраняя исходный объект ошибки. - Фактическая финализация происходит строго при выходе из самого внешнего блока (когда глубина вложенности возвращается к
0): - если за время всей сессии возникала хотя бы одна ошибка, вызывается
resource.rollback(); - если ни одной ошибки не было, вызывается
resource.commit().
Параметры входных данных:
- Число ключей в реестре — от
0до30, длина строковых ключей — от1до64символов, значения — целые числа от-1 000 000до1 000 000. - Глубина вложенности — от
1до50уровней, количество операций в теле блоков — от0до1000.
Ввод и вывод
Стандартные потоки stdin и stdout не используются: считывать данные через input() и печатать результат через print() не нужно. Проверяющая система после выполнения кода ученика получает из пространства имён объявленный на верхнем уровне класс OutermostTransaction и тестирует его работу серией вложенных вызовов.
Пример использования
resource = make_registry({"count": 1})
session = OutermostTransaction(resource)
with session as data:
data["count"] = 2
try:
with session as inner:
inner["extra"] = 3
raise ValueError("cancel")
except ValueError:
pass
print(resource.data)
print(resource.events)
print(resource.active)
Вывод:
{'count': 1}
['begin', 'rollback']
False
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Связанные задачи
- exit в Python: аргументы исключения и подавление ошибки
- Контекстный менеджер Python: подавление выбранных типов исключений
- Контекстный менеджер Python: откат списания ресурсов при ошибке
- Контекстный менеджер Python: временная подмена списка с восстановлением
- Проверка контекстного менеджера Python: подавление и замена исключения
- Contextmanager на Python: транзакционные изменения словаря с квотой
