Python initsubclass: проверка порядка миграций схемы

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

Другие упражнения по этой теме: Метаклассы и init_subclass Python: задачи на проверку классов.

Реестр миграций схемы

В хранилище документов цепочка обновлений схемы данных строится из отдельных классов, каждый из которых отвечает за свой шаг изменений. Чтобы последовательность оставалась надёжной, базовый класс миграций контролирует правильный порядок стадий прямо в момент объявления их классов с помощью __init_subclass__.

Каждый класс-наследник передаёт в заголовке объявления целое число version и имя класса-предшественника previous. Корневая миграция начинает цепочку: её предшественник равен None, а версия — строго 1. Любой последующий класс обязан ссылаться на уже зарегистрированного предшественника, а его версия должна быть строго больше версии этого предка. Если указанный предшественник ещё не известен или новая версия оказывается меньше либо равна версии предшественника, объявление такого класса немедленно возбуждает ValueError.

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

Что уже дано

В редакторе представлена заготовка базового класса MigrationBase с пустым общим словарём registry и заглушкой метода __init_subclass__.

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

Реализуйте класс MigrationBase со следующим поведением:

  • Атрибут класса registry — изначально пустой словарь dict[str, type], общий для всех наследников. В него заносятся только успешно созданные классы: ключ — имя класса (cls.__name__), значение — сам объект созданного класса.
  • Метод __init_subclass__(cls, version, previous, **kwargs) принимает обязательные именованные параметры заголовка класса:
  • version (int) — номер версии от -1 000 000 до 1 000 000;
  • previous (str | None) — None или строка с именем класса-предшественника (до 40 символов).
  • При успешной проверке созданный класс должен содержать публичные атрибуты version и previous с переданными значениями и быть зарегистрирован в MigrationBase.registry.
  • Правила валидации при объявлении класса:
  • Если previous is None, объявляется корневая миграция. Её версия обязана быть строго равна 1. При любом другом значении version создание класса отклоняется ошибкой ValueError.
  • Если previous — строка, она должна быть именем уже успешно созданного и зарегистрированного в registry класса, а значение version обязано быть строго больше версии этого предшественника. Если предшественник отсутствует в реестре либо новая версия меньше или равна версии предка, создание класса отклоняется ошибкой ValueError (допускаются также её подклассы; текст ошибки не регламентируется).
  • Отклонённые классы ни при каких обстоятельствах не должны попадать в registry или повреждать уже существующие записи.
  • Разрешены несколько корневых миграций, независимые ветвления цепочек, одинаковые версии на разных ветках и пропуски номеров при возрастании. Имена всех объявляемых классов уникальны (допустимые идентификаторы Python длиной до 40 символов). Всего в рамках одного прогона выполняется от 0 до 512 попыток создания классов.

Ввод и вывод

Стандартные потоки stdin и stdout не используются: считывать ввод и вызывать print в решении не требуется. Тестирующая система считывает класс верхнего уровня MigrationBase после выполнения кода ученика, объявляет подклассы миграций и проверяет содержимое словаря MigrationBase.registry.

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

class Initial(MigrationBase, version=1, previous=None):
    pass

class Expanded(MigrationBase, version=4, previous='Initial'):
    pass

print(list(MigrationBase.registry))
print(Expanded.version, Expanded.previous)
try:
    class InvalidStep(MigrationBase, version=4, previous='Expanded'):
        pass
except ValueError:
    print('ValueError')

Вывод:

['Initial', 'Expanded']
4 Initial
ValueError
РешениеPython

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

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

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