Files
dev-skills/decisions/09-script-linters.md
T
av bf6a173115 журнал решений: разложен по теме на файл, метки решений стали номерами
- DECISIONS.md (4040 строк, 65 тем) → decisions/, файл на тему плюс указатель;
- буквенные метки решений заменены сквозными Р1–Р234, следствия получили
  префикс С при прежних номерах: схема букв выродилась до пятибуквенных и
  сломалась — `АЕАКЛ` была занята и темой 53, и темой 65;
- 42 перекрёстные ссылки переписаны под новые номера и стали живыми; где номер
  означал тему, а слово стояло «решение», формулировка исправлена.
2026-08-13 12:40:56 +03:00

5.5 KiB
Raw Blame History

9. Линтеры скриптов (2026-08-03)

Что было

Три скрипта на python, 3600 строк, ни одной проверки. tasks.py — 2450 строк, которые ходят по файловой системе, переименовывают и удаляют файлы задач. Требование к самим скриптам прежнее и не обсуждается: голый python3 3.12, ноль внешних зависимостей — они лежат рядом со скиллами и запускаются в чужом проекте, где ничего ставить нельзя.

Решено

Р30. pyproject.toml в корне dev-skills, зависимости через uv. Файл живёт только здесь и не уезжает никуда: он держит линтеры, а не зависимости скриптов. Скрипты остаются запускаемыми любым python3 — это проверено прогоном всех операций через /usr/bin/python3, а не через .venv.

Р31. Ноль зависимостей охраняется двумя способами, и главный — второй. banned-api у ruff ловит частые соблазны по имени (requests, yaml, pydantic, click, rich) — список заведомо неполный. Настоящий страж — pyrefly: в окружении нет ничего, кроме линтеров, поэтому любой сторонний импорт у него не разрешается. Первый способ даёт понятное сообщение, второй — полноту.

Р32. Версии линтеров прибиты точно (ruff==0.16.1, pyrefly==1.2.0) плюс uv.lock в git. Обновление линтера меняет набор находок, а находки правятся руками в скриптах, которые уезжают в чужие проекты. Обновление обязано быть отдельной осознанной правкой, а не побочным эффектом uv sync.

Р33. RUF001RUF003 выключены. Весь текст скриптов русский: сообщения, докстроки, комментарии. «Похожая на латиницу кириллица» здесь норма, а не опечатка, и три этих правила давали 311 срабатываний из 338 — шум, в котором тонут остальные 27.

Р34. av-dev-backlog исключён из проверки. (исчерпано темой 30: плагин удалён, исключение снято из pyproject.toml и copies.py.) Плагин помечен устаревшим и живёт до перевода последнего проекта, после чего удаляется целиком. Шесть его находок косметические (os.replace, l как имя), а правка замороженного кода без тестов — риск без выгоды. Исключение уходит вместе с плагином.

Р35. Голый except Exception разрешён только помеченный. Правило BLE включено, а два места последнего рубежа (main обоих скриптов, код выхода 4 по словарю) несут # noqa: BLE001 с причиной. Так третий такой except не появляется молча.

Что из этого следует

С40. Найдено и починено 27 находок ruff и 14 pyrefly. Содержательных две: мёртвая переменная ques в check (вычислялась и не использовалась — вопросы проверяет questions_open) и два места в check --fix, где find_entry_index может вернуть None, а результат идёт прямо в list.pop и в range. Оба сегодня недостижимы, и недостижимость держалась на рассуждении о вызывающем коде, а не на проверке. Поправлено по ревью: там стоит raise, а не continue. Тихий пропуск превратил бы сломанный инвариант в отчёт «индексы согласованы» — то есть в враньё; громкий отказ кодом 4 честнее.

С41. os из tasks.py ушёл целиком. os.replacePath.replace, os.path.basenamePath.name; импорт стал не нужен.

С42. fail() в docs.py объявлен NoReturn. Без этого read_config выглядел как возвращающий неинициализированное значение — и это ровно то, что читатель кода тоже не мог знать наверняка.

С43. Проверка не входит ни в один гейт. CI у репозитория нет, хука нет; запускается руками командой из README. Заводить хук ради двух скриптов, которые правятся раз в месяц, — плата ритуалом без выгоды.