fix(ptica): три места, где код делал не то, что говорил (#2464) #3003
No reviewers
Labels
No labels
Fable 5 ревью
GG-форсайт
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
feedback/max
generative
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
ИРД
вторичка
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#3003
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/2464-three-small"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Три оставшихся [L]-пункта эпика одним PR — все три одного класса: код делает не то, что сам о себе сообщает.
1. Период водоканала попадал в лог, но не в ответ
period— строка вида «III кв. 2025» или None, поэтому фильтр выбрасывал его всегда. По логам казалось, что период отдаётся; вызывающий не получал его ни разу.Фильтр стоял ради аннотации
dict[str, int]и ничего не защищал: соседняя веткаload_water_reservesкладёт в тот же словарь{"error": str(...)}, а единственный потребитель — задачаsync_water_reserves— результат просто логирует и возвращает.Период полезен: без него «загружено 42 записи» не отличить от прошлогодних. Аннотация исправлена вместе с фильтром — иначе следующий проход mypy вернул бы его обратно; на это поставлен отдельный тест.
2. TOCTOU в
get_sqlite_infotry/exceptпокрывал толькоsqlite3.connectниже. Файл между проверками реально исчезает — его переписывает выгрузка Объектива, — и OSError улетал наружу, превращая диагностическую функцию в источник отказа. Теперь отдаём то, что успели узнать, с ключомstat_error.3. Предупреждение placement называло не тот размер
Если элемент программы переопределил габарит, сообщение называет размер, которым никто не пытался ставить. Диагностика уводит от причины — тот же класс, что «диагностика со знаком вопроса».
Как проверено
origin/mainчетыре теста красные с конкретными значениями:период выброшен из ответа: {'records': 1, 'inserted': 1, 'updated': 0};аннотация всё ещё требует только int: dict[str, int];FileNotFoundErrorиз самой функции;exists=Falseбез ошибки;fp_w/fp_d— действительно те переменные, которыми ставят (иначе первый тест сверял бы имена, а не смысл).pytest backend/tests/services/— 3207 passed, 14 skipped; смежныеtest_placement/test_program_placement— 44 passed.Часть эпика #2464.
1. `load_water_reserves_from_docx` собирал `result` с ключом `period`, печатал ПОЛНЫЙ словарь в лог, а возвращал `{k: v for … if isinstance(v, int)}` — период это строка или None, поэтому выбрасывался всегда. По логам казалось, что период отдаётся; вызывающий не получал его ни разу. Фильтр стоял ради аннотации `dict[str, int]` и ничего не защищал: соседняя ветка `load_water_reserves` кладёт в тот же словарь `{"error": str(...)}`, а единственный потребитель — задача `sync_water_reserves` — результат логирует и возвращает как есть. Период полезен: без него «загружено 42 записи» не отличить от прошлогодних. Аннотация исправлена, иначе следующий проход mypy вернул бы фильтр обратно — на это поставлен отдельный тест. 2. `get_sqlite_info` — TOCTOU: `p.exists()`, затем незащищённый `p.stat()`. try/except покрывал только `sqlite3.connect` ниже, поэтому OSError из stat улетал наружу и превращал диагностическую функцию в источник отказа. Файл между проверками реально исчезает — его переписывает выгрузка Объектива. Теперь отдаём то, что успели узнать, с ключом `stat_error`. 3. `place_program` — предупреждение «участок мал» печатало КАТАЛОЖНЫЕ `house.footprint_*`, хотя ставили по `fp_w`/`fp_d`. При переопределённом в программе габарите сообщение называло размер, которым никто не пытался ставить, и уводило от причины. Двусторонне: против origin/main четыре теста красные с конкретными значениями («период выброшен из ответа: {'records': 1, 'inserted': 1, 'updated': 0}», «аннотация всё ещё требует только int: dict[str, int]»). Контроли зелёные с обеих сторон: отсутствующий файл по-прежнему даёт exists=False без ошибки; `fp_w`/`fp_d` — действительно те размеры, которыми ставят (иначе первый тест сверял бы имена, а не смысл). pytest backend/tests/services/ — 3207 passed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>