wms_feature_info (nspd_bulk_client.py:418-420): отсутствует isinstance(data, dict)-guard — non-dict JSON-ответ роняет ячейку grid-walk через AttributeError, потеря без сигнала #1627

Closed
opened 2026-06-16 18:44:19 +00:00 by bot-backend · 0 comments
Collaborator

Severity: low · Категория: parsing-robustness · Файл: backend/app/scrapers/nspd_bulk_client.py:418-420

Файл: backend/app/scrapers/nspd_bulk_client.py:418-420.

Проблема: data = await self._get_json(url, ...) возвращает Any (на :249 return resp.json() без проверки типа), затем raw_features = (data or {}).get("features") or []. Нет проверки типа data. Если NSPD/GeoServer вернёт валидный JSON, но не объект (list или str — известный кейс Bug_Nspd_Geo_Str_Object, на который ссылается search_by_quarter:324), то для непустого list/str data or {} вернёт сам data (truthy), и .get("features") бросит AttributeError.

Контраст: search_by_quarter:321-331 имеет guard payload = data if isinstance(data, dict) else {} и явную ветку для str-обёртки (warning + возврат пустого snapshot). В wms_feature_info этой унификации нет.

Импакт: AttributeError — не NspdBulkServerError и не httpx.TransportError. В _grid_walk_category (bulk_harvest.py:391-403) он попадает в общий except Exception: ячейка считается «не дошла» (requests+=1, continue), но НЕ учитывается в server_errors и НЕ инкрементит ok_cells. Так как layer_failed = server_errors>0 and ok_cells==0 (bulk_harvest.py:426), аномалия формата ответа выкидывает фичи ячейки молча — нет следа ни в server_errors, ни в layer_failed. На сетке grid_n×grid_n один битый ответ = недосбор без сигнала.

Фикс: добавить guard как в search_by_quarter — data = data if isinstance(data, dict) else {} перед .get("features"), либо raw_features = data.get("features", []) if isinstance(data, dict) else [] с warning-логом для не-dict ответа (унифицировать обработку Bug_Nspd_Geo_Str_Object с search_by_quarter).

Почему баг: AttributeError не httpx.TransportError и не NspdBulkServerError, поэтому в _grid_walk_category он попадает в общий except Exception (bulk_harvest.py:391-403): ячейка считается «не дошла» (НЕ server_error), молча пропускается, фичи ячейки теряются. На сетку 15×15 одна аномалия формата ответа выкидывает объекты ячейки без следа в server_errors/layer_failed → недосбор без сигнала. Тот же класс ответа в search_by_quarter обрабатывается корректно (возврат пустого snapshot с warning), здесь — нет, что указывает на пропущенную унификацию.

Как чинить: Зеркалить guard из search_by_quarter: payload = data if isinstance(data, dict) else {}; raw_features = payload.get("features") or [] — тогда не-dict ответ даёт пустой список features (ячейка пуста), а не исключение, проглатываемое broad-except.


deep-audit backend v2 (2-й проход, после PR #1543) · verify+harden, confidence 0.82 · unit B11

**Severity:** low · **Категория:** parsing-robustness · **Файл:** `backend/app/scrapers/nspd_bulk_client.py:418-420` Файл: backend/app/scrapers/nspd_bulk_client.py:418-420. Проблема: `data = await self._get_json(url, ...)` возвращает Any (на :249 `return resp.json()` без проверки типа), затем `raw_features = (data or {}).get("features") or []`. Нет проверки типа data. Если NSPD/GeoServer вернёт валидный JSON, но не объект (list или str — известный кейс Bug_Nspd_Geo_Str_Object, на который ссылается search_by_quarter:324), то для непустого list/str `data or {}` вернёт сам data (truthy), и `.get("features")` бросит AttributeError. Контраст: search_by_quarter:321-331 имеет guard `payload = data if isinstance(data, dict) else {}` и явную ветку для str-обёртки (warning + возврат пустого snapshot). В wms_feature_info этой унификации нет. Импакт: AttributeError — не NspdBulkServerError и не httpx.TransportError. В _grid_walk_category (bulk_harvest.py:391-403) он попадает в общий `except Exception`: ячейка считается «не дошла» (requests+=1, continue), но НЕ учитывается в server_errors и НЕ инкрементит ok_cells. Так как layer_failed = server_errors>0 and ok_cells==0 (bulk_harvest.py:426), аномалия формата ответа выкидывает фичи ячейки молча — нет следа ни в server_errors, ни в layer_failed. На сетке grid_n×grid_n один битый ответ = недосбор без сигнала. Фикс: добавить guard как в search_by_quarter — `data = data if isinstance(data, dict) else {}` перед `.get("features")`, либо `raw_features = data.get("features", []) if isinstance(data, dict) else []` с warning-логом для не-dict ответа (унифицировать обработку Bug_Nspd_Geo_Str_Object с search_by_quarter). **Почему баг:** AttributeError не httpx.TransportError и не NspdBulkServerError, поэтому в _grid_walk_category он попадает в общий `except Exception` (bulk_harvest.py:391-403): ячейка считается «не дошла» (НЕ server_error), молча пропускается, фичи ячейки теряются. На сетку 15×15 одна аномалия формата ответа выкидывает объекты ячейки без следа в server_errors/layer_failed → недосбор без сигнала. Тот же класс ответа в search_by_quarter обрабатывается корректно (возврат пустого snapshot с warning), здесь — нет, что указывает на пропущенную унификацию. **Как чинить:** Зеркалить guard из search_by_quarter: `payload = data if isinstance(data, dict) else {}; raw_features = payload.get("features") or []` — тогда не-dict ответ даёт пустой список features (ячейка пуста), а не исключение, проглатываемое broad-except. --- <sub>deep-audit backend v2 (2-й проход, после PR #1543) · verify+harden, confidence 0.82 · unit B11</sub>
bot-backend added the
week ревью 1
label 2026-06-16 18:44:19 +00:00
Sign in to join this conversation.
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#1627
No description provided.