check-file-length: exclude alembic/proto не срабатывает в плоской раскладке; no-broad-except слеп к BOM #17

Closed
opened 2026-08-27 11:56:17 +07:00 by claude-secretary · 1 comment

Два независимых дефекта, найденных ревью #7 и #13 по соседству с их скоупом. В тех PR не чинились намеренно — другой хук.

1. check_file_length.sh: */alembic/versions/* не матчит верхнеуровневый путь

scripts/check_file_length.sh:135:

case "$file" in
    */alembic/versions/*) continue ;;
    */__init__.py) continue ;;
    */proto/generated/*) continue ;;
    proto/generated/*) continue ;;     # ← для proto обе формы есть
esac

Для proto обе формы предусмотрены, для alembic — только вложенная. Путь alembic/versions/a1b2c3_add_user.py (миграции лежат в корне репозитория — типовая раскладка) под исключение не попадает и проверяется на длину. То же и с __init__.py в корне пакета верхнего уровня.

Лечится симметрично proto — добавить формы alembic/versions/* и __init__.py.

Тот же класс дефекта был в README-примере max-filename-words (.*/alembic/versions/.* не исключал src/alembic/versions/…) и там уже исправлен на (.*/)?….

2. check_no_broad_except.py: файл с BOM молча зелёный

scripts/check_no_broad_except.py:35:

source = open(path).read()
tree = ast.parse(source)

open() без encoding читает BOM как U+FEFF, ast.parse падает SyntaxError: invalid non-printable character U+FEFF, а except SyntaxError: return [] превращает это в «нарушений нет». То есть достаточно BOM'а в начале файла, чтобы весь файл прошёл мимо хука — с молчаливым зелёным, а не с ошибкой.

Лечение — encoding="utf-8-sig" и except (OSError, UnicodeDecodeError), ровно как сделано для no-private-imports/no-private-method-calls в #13 и для no-issue-refs-in-comments в #12. Фикстура с BOM обязательна: без неё мутация «убрать encoding=» переживает smoke — прецедент fixtures/issue_refs/bom_docstring.py.

Стоит заодно пройтись по всем хукам, читающим файл, и свести чтение к одному месту: это уже четвёртый хук с той же дырой, каждый чинился отдельно.

Приёмка

  • Миграция и __init__.py в корне исключаются check-file-length; кейс в smoke
  • BOM-файл с except Exception роняет no-broad-except; фикстура + кейс
  • Мутация «убрать encoding=» роняет smoke
Два независимых дефекта, найденных ревью [#7](https://git.homedevlab.ru/senokosov/pre-commit-hooks/pulls/7) и [#13](https://git.homedevlab.ru/senokosov/pre-commit-hooks/pulls/13) по соседству с их скоупом. В тех PR не чинились намеренно — другой хук. ## 1. `check_file_length.sh`: `*/alembic/versions/*` не матчит верхнеуровневый путь `scripts/check_file_length.sh:135`: ```sh case "$file" in */alembic/versions/*) continue ;; */__init__.py) continue ;; */proto/generated/*) continue ;; proto/generated/*) continue ;; # ← для proto обе формы есть esac ``` Для `proto` обе формы предусмотрены, для `alembic` — только вложенная. Путь `alembic/versions/a1b2c3_add_user.py` (миграции лежат в корне репозитория — типовая раскладка) под исключение не попадает и проверяется на длину. То же и с `__init__.py` в корне пакета верхнего уровня. Лечится симметрично proto — добавить формы `alembic/versions/*` и `__init__.py`. Тот же класс дефекта был в README-примере `max-filename-words` (`.*/alembic/versions/.*` не исключал `src/alembic/versions/…`) и там уже исправлен на `(.*/)?…`. ## 2. `check_no_broad_except.py`: файл с BOM молча зелёный `scripts/check_no_broad_except.py:35`: ```python source = open(path).read() tree = ast.parse(source) ``` `open()` без `encoding` читает BOM как `U+FEFF`, `ast.parse` падает `SyntaxError: invalid non-printable character U+FEFF`, а `except SyntaxError: return []` превращает это в «нарушений нет». То есть достаточно BOM'а в начале файла, чтобы весь файл прошёл мимо хука — с молчаливым зелёным, а не с ошибкой. Лечение — `encoding="utf-8-sig"` и `except (OSError, UnicodeDecodeError)`, ровно как сделано для `no-private-imports`/`no-private-method-calls` в [#13](https://git.homedevlab.ru/senokosov/pre-commit-hooks/pulls/13) и для `no-issue-refs-in-comments` в [#12](https://git.homedevlab.ru/senokosov/pre-commit-hooks/pulls/12). Фикстура с BOM обязательна: без неё мутация «убрать `encoding=`» переживает smoke — прецедент `fixtures/issue_refs/bom_docstring.py`. Стоит заодно пройтись по всем хукам, читающим файл, и свести чтение к одному месту: это уже четвёртый хук с той же дырой, каждый чинился отдельно. ## Приёмка - [ ] Миграция и `__init__.py` в корне исключаются `check-file-length`; кейс в smoke - [ ] BOM-файл с `except Exception` роняет `no-broad-except`; фикстура + кейс - [ ] Мутация «убрать `encoding=`» роняет smoke
Author
Owner

Закрываю: сделано в #22main, версия 0.18.0).

  1. Исключения check-file-length сравниваются с "/$file", поэтому одна форма паттерна покрывает и вложенный, и верхнеуровневый путь — дублировать строку следующему исключению не придётся. alembic/versions/…, __init__.py и proto/generated/… в корне репозитория теперь скипаются; alembic/env.py и голый versions/… по-прежнему проверяются (негативный контроль в smoke ловит и это переширение).
  2. no-broad-except читает файл в utf-8-sig и ловит UnicodeDecodeError: BOM больше не отправляет весь файл мимо хука через ast.parseSyntaxError → «нарушений нет». Фикстура fixtures/broad_except/bom.py; мутация «убрать encoding» роняет smoke. У хука заодно появилась своя секция в README — раньше её не было вовсе.

Остаётся не сделанным, что предлагала последняя часть задачи, — свести чтение файла в один хелпер: это шестой хук с одинаковой строкой. Заведу отдельно, вместе с двумя соседними находками ревью: migrations/versions/… (раскладка alembic init migrations) под исключение не попадает, и недекодируемый файл во всех трёх ast-хуках пропускается молча, без строки в stderr — что политике fail-loud противоречит.

Закрываю: сделано в [#22](https://git.homedevlab.ru/senokosov/pre-commit-hooks/pulls/22) (в `main`, версия `0.18.0`). 1. Исключения `check-file-length` сравниваются с `"/$file"`, поэтому одна форма паттерна покрывает и вложенный, и верхнеуровневый путь — дублировать строку следующему исключению не придётся. `alembic/versions/…`, `__init__.py` и `proto/generated/…` в корне репозитория теперь скипаются; `alembic/env.py` и голый `versions/…` по-прежнему проверяются (негативный контроль в smoke ловит и это переширение). 2. `no-broad-except` читает файл в `utf-8-sig` и ловит `UnicodeDecodeError`: BOM больше не отправляет весь файл мимо хука через `ast.parse` → `SyntaxError` → «нарушений нет». Фикстура `fixtures/broad_except/bom.py`; мутация «убрать encoding» роняет smoke. У хука заодно появилась своя секция в README — раньше её не было вовсе. Остаётся не сделанным, что предлагала последняя часть задачи, — свести чтение файла в один хелпер: это шестой хук с одинаковой строкой. Заведу отдельно, вместе с двумя соседними находками ревью: `migrations/versions/…` (раскладка `alembic init migrations`) под исключение не попадает, и недекодируемый файл во всех трёх ast-хуках пропускается молча, без строки в stderr — что политике fail-loud противоречит.
Sign in to join this conversation.
No labels
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
senokosov/pre-commit-hooks#17
No description provided.