Почему стоит избегать использования словарей без валидации для данных из внешних источников в Python
Что проверяют
Понимаете ли, что данные из внешнего источника это не словарь, а обещание словаря, и умеете ли назвать конкретные последствия вместо общего «это небезопасно». Ждут границы, на которой сырой ввод превращается в типизированный объект.
Как отвечать
Главный аргумент: у словаря нет схемы. Он принимает любые ключи с любыми значениями, поэтому ошибка обнаруживается не там, где пришли плохие данные, а глубоко внутри, через несколько слоёв. Отсюда конкретика:
KeyErrorвместо внятного ответа клиенту, потому что обязательного ключа не оказалось;- молчаливая подмена типа: в поле пришла строка вместо числа, и падает уже арифметика или запись в базу;
data.get("x")возвращаетNone, значение расходится по коду и роняет что-то совсем в другом месте;- опечатку в ключе не ловит ни анализатор, ни тест, если тест писали по тем же данным;
- лишние ключи: если словарь потом уходит в конструктор модели распаковкой, клиент задаёт поля, которые задавать не должен.
Решение это разбор на границе. Один раз на входе превратить сырой словарь в объект с типами и дальше передавать по коду только его. Практически это pydantic, а без зависимостей dataclass плюс собственная функция разбора:
class UserIn(BaseModel):
name: str
age: intТогда ошибка возникает в одном месте, с указанием поля и причины, и её легко превратить в корректный ответ клиенту.
Где спотыкаются
Считают, что аннотации типов проверяют данные. TypedDict и подсказки типов существуют для статического анализатора, в рантайме их никто не смотрит, и объявленный dict[str, int] спокойно примет строку.
Проверяют данные в каждой функции по кусочку. Условия расползаются, часть путей остаётся непроверенной, сообщения об ошибках у всех разные. Проверка должна быть одна, на входе.
Третье: полагаются на приведение типов по умолчанию. В мягком режиме библиотека разбора сама превратит строку в число, и признак включённости молча поменяет смысл. Для внешнего ввода включают строгий режим и задают границы: длину строк, диапазон чисел, глубину вложенности.