fix(ptica): дрейф формы ответа DaData не роняет весь гео-проход (#2464) #2942

Merged
bot-backend merged 1 commit from fix/2464-dadata-data-shape into main 2026-08-19 16:59:50 +00:00
Collaborator

Дефект

_suggest_geocode брал вложенный словарь так:

data = suggestions[0].get("data") or {}
lat = _coerce_float(data.get("geo_lat"))

or {} ловит только falsy. Истинное не-словарное значение — список, строка, число — доходит до .get и поднимает AttributeError.

Почему это роняет весь проход, а не один адрес

Летит наружу:

  1. в suggest попадают из clean_address по фолбэку 401/403 («Feature CLEAN disabled» — реальный прод-инцидент 03.07, на него есть отдельный тест в файле);
  2. этот вызов стоит за пределами try/except самой clean_address (строка 112 — после блока на 95-102);
  3. у вызывающего гео-прохода (objective_backfill._geocode, строка 864) обёртки нет вовсе.

Один такой ответ убил бы прогон целиком.

Непоследовательность видна прямо в файле: payload, suggestions[0] и item в clean_address проверяются через isinstance. Защита пропала ровно на уровень глубже.

Честно про масштаб

За 14 суток в логах gendesign-backend и gendesign-worker ноль упоминаний dadata_client — гео-проход ручной и не запускался.

Но это не «мёртвый код»: путь ничем внешним не заблокирован, и я сам записал в #2929 критерий приёмки, который требует этот проход запустить. Чинить лучше до, а не после того, как прогон упадёт на середине.

Проверка

тест origin/main с правкой
data = список красный: AttributeError: 'list' object has no attribute 'get' зелёный
data = строка красный: AttributeError: 'str' object has no attribute 'get' зелёный
data = число красный зелёный
контроль: правильная форма отдаёт координаты зелёный зелёный

Последний контроль существенен: без него «починка» могла бы свестись к «всегда возвращаем None», и все три первых теста были бы зелёными по неверной причине.

pytest tests/services: 3078 passed, 14 skipped, rc=0

Refs #2464

## Дефект `_suggest_geocode` брал вложенный словарь так: ```python data = suggestions[0].get("data") or {} lat = _coerce_float(data.get("geo_lat")) ``` `or {}` ловит только falsy. Истинное **не-словарное** значение — список, строка, число — доходит до `.get` и поднимает `AttributeError`. ## Почему это роняет весь проход, а не один адрес Летит наружу: 1. в suggest попадают из `clean_address` по фолбэку 401/403 («Feature CLEAN disabled» — реальный прод-инцидент 03.07, на него есть отдельный тест в файле); 2. этот вызов стоит **за** пределами `try/except` самой `clean_address` (строка 112 — после блока на 95-102); 3. у вызывающего гео-прохода (`objective_backfill._geocode`, строка 864) обёртки нет вовсе. Один такой ответ убил бы прогон целиком. Непоследовательность видна прямо в файле: `payload`, `suggestions[0]` и `item` в `clean_address` проверяются через `isinstance`. Защита пропала ровно на уровень глубже. ## Честно про масштаб За 14 суток в логах `gendesign-backend` и `gendesign-worker` **ноль** упоминаний `dadata_client` — гео-проход ручной и не запускался. Но это не «мёртвый код»: путь ничем внешним не заблокирован, и я сам записал в #2929 критерий приёмки, который **требует** этот проход запустить. Чинить лучше до, а не после того, как прогон упадёт на середине. ## Проверка | тест | `origin/main` | с правкой | |---|---|---| | `data` = список | **красный**: `AttributeError: 'list' object has no attribute 'get'` | зелёный | | `data` = строка | **красный**: `AttributeError: 'str' object has no attribute 'get'` | зелёный | | `data` = число | **красный** | зелёный | | контроль: правильная форма отдаёт координаты | зелёный | зелёный | Последний контроль существенен: без него «починка» могла бы свестись к «всегда возвращаем None», и все три первых теста были бы зелёными по неверной причине. `pytest tests/services`: **3078 passed, 14 skipped, rc=0** Refs #2464
bot-backend added 1 commit 2026-08-19 16:40:12 +00:00
fix(ptica): дрейф формы ответа DaData не роняет весь гео-проход (#2464)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 10s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 12s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m9s
CI / backend-tests (pull_request) Successful in 17m22s
6c3743ade9
`_suggest_geocode` брал вложенный словарь как

    data = suggestions[0].get("data") or {}

`or {}` ловит только falsy. Истинное не-словарное значение (список, строка,
число — дрейф контракта) доходило до `.get` и поднимало AttributeError.

Летел он НАРУЖУ. В suggest попадают из clean_address по фолбэку 401/403
(«Feature CLEAN disabled» — реальный прод-инцидент 03.07, на него есть тест), и
этот вызов стоит УЖЕ ЗА пределами её try/except. У вызывающего гео-прохода
(objective_backfill._geocode, строка 864) обёртки нет вовсе. То есть один такой
ответ убил бы весь проход целиком, а не один адрес.

Непоследовательность видна в самом файле: `payload`, `suggestions[0]` и `item`
проверяются через isinstance, а `data` — нет. Защита пропала ровно на уровень
глубже.

Про масштаб честно: за 14 суток в логах бэкенда и воркера НЕТ ни одного
упоминания dadata_client — гео-проход ручной и не запускался. Но путь не
заблокирован ничем внешним, и я сам записал в #2929 критерий приёмки, который
требует этот проход запустить.

Тесты: три параметра (список / строка / число) красные на origin/main с
`AttributeError: 'list' object has no attribute 'get'`; контроль на правильную
форму зелёный с обеих сторон — иначе «починка» могла бы свестись к «всегда None».

pytest tests/services: 3078 passed, 14 skipped, rc=0
bot-backend merged commit 8eeb35cee5 into main 2026-08-19 16:59:50 +00:00
bot-backend deleted branch fix/2464-dadata-data-shape 2026-08-19 16:59:50 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#2942
No description provided.