Проверка значений модели¶
У диапазона начало не должно превышать конец. Это правило связывает два поля и не выражается выбором числового codec:
from bytespec import ProtoModel
from bytespec.types import UInt16
class Window(ProtoModel):
__header__ = ()
start: UInt16
end: UInt16
def __validate__(self) -> None:
if self.start > self.end:
raise ValueError("start must not exceed end")
window = Window(start=10, end=20)
assert Window.decode(window.encode()) == window
try:
Window(start=20, end=10)
except ValueError as error:
print(error)
start must not exceed end
Библиотека сама проверяет бинарную схему: известен ли тип, корректны ли
индексы и настройки. Ошибка схемы — SchemaError. __validate__
проверяет значения приложения. Это обычный метод экземпляра без
декоратора; к моменту вызова все поля уже присвоены, включая defaults и
результаты factories. Возвращаемое значение игнорируется: для отказа
поднимите исключение.
Когда вызывается проверка¶
Метод вызывается в конце ProtoModel.__init__ и поэтому работает также
при decode() и decode_from(). После чтения байтов создаётся обычный
экземпляр. Например, корректные числа могут нарушать правило диапазона:
try:
Window.decode(bytes.fromhex("00 14 00 0a"))
except ValueError as error:
print(error)
start must not exceed end
Исключение validator передаётся как есть: здесь это ValueError,
а не DecodeError или EncodeError. Выберите тип исключения, который
приложение сможет обработать наравне с ошибками чтения.
Присваивание атрибутов и encode() не запускают проверку повторно:
window.start = 30
print(window.encode().hex(" "))
try:
window.__validate__()
except ValueError as error:
print(error)
00 1e 00 14
start must not exceed end
Если меняете объект, вызовите метод явно перед записью или создайте новый
экземпляр. Числовые диапазоны и возможность записи по-прежнему проверяются
codecs при encode(). Полного runtime-контроля типов и автоматического
преобразования значений в модели нет.
Проверка в подклассе¶
Автоматический вызов ищет __validate__ только в самом конкретном классе.
Унаследованный метод доступен в Python, но автоматически не запускается:
class UncheckedWindow(Window):
pass
unchecked = UncheckedWindow(start=20, end=10)
assert unchecked.start > unchecked.end
Для повторного использования определите метод в дочернем классе:
class CheckedWindow(Window):
def __validate__(self) -> None:
super().__validate__()
try:
CheckedWindow(start=20, end=10)
except ValueError as error:
print(error)
start must not exceed end
Вложенная модель вызывает свою проверку при собственном создании, до validator внешней модели при decode. Дополнительные правила композиции и схемы подкласса описаны в Наследование моделей.
Далее — Обработка ошибок: какие исключения обрабатывать при обмене сообщениями.