no-private-imports: regex слеп к скобочным multi-line from ... import (...) #5

Closed
opened 2026-06-15 09:50:57 +07:00 by claude-secretary · 1 comment

Баг

Хук no-private-imports (scripts/check_no_private_imports.py, IMPORT_RE)
анализирует только строку, содержащую from ... import. При скобочной
много-строчной форме импорта символы лежат на строках-продолжениях, которые
regex не видит → приватные импорты проходят молча.

Воспроизведение

from baton_runner.ci_log.grpc_mapping import (
    _CI_LOG_DIR_PROBE_STATUS_TO_PROTO,   # приватный — хук НЕ ловит
    to_ci_log_dir_probe_proto_response,
)

Однострочный аналог from x import _y ловится корректно — проблема только в
скобочной многострочной форме.

Где всплыло

baton, PR #922 (ревью): пропущенный приватный импорт прошёл зелёный CI
именно из-за этой слепой зоны. В baton-репо так же скрыто ≥4 pre-existing
нарушения (_tail_text, _build_argv), невидимых хуку.

Предлагаемое решение

Парсить импорты не построчным regex, а через ast (модуль
ast.ImportFromnode.names[].name), либо сшивать логические строки до
закрывающей скобки перед матчингом. ast-подход заодно убирает
false-positive на строках-комментариях/докстрингах с похожим текстом.

Аналогичную слепую зону стоит проверить у no-private-method-calls, если
он тоже построчный.

Acceptance

  • no-private-imports ловит приватные символы в скобочной multi-line
    форме from ... import (...).
  • Тест-фикстура на multi-line импорт (приватный — fail, публичный — pass).
  • (опц.) проверена/починена та же зона у no-private-method-calls.

Связано: baton #687 (boy-scout-зачистка запретов; Группа A-хвост зависит от
ручной дочистки, пока хук слеп).

## Баг Хук `no-private-imports` (`scripts/check_no_private_imports.py`, `IMPORT_RE`) анализирует только **строку, содержащую `from ... import`**. При скобочной много-строчной форме импорта символы лежат на строках-продолжениях, которые regex не видит → приватные импорты проходят молча. ### Воспроизведение ```python from baton_runner.ci_log.grpc_mapping import ( _CI_LOG_DIR_PROBE_STATUS_TO_PROTO, # приватный — хук НЕ ловит to_ci_log_dir_probe_proto_response, ) ``` Однострочный аналог `from x import _y` ловится корректно — проблема только в скобочной многострочной форме. ### Где всплыло baton, PR #922 (ревью): пропущенный приватный импорт прошёл зелёный CI именно из-за этой слепой зоны. В baton-репо так же скрыто ≥4 pre-existing нарушения (`_tail_text`, `_build_argv`), невидимых хуку. ## Предлагаемое решение Парсить импорты не построчным regex, а через `ast` (модуль `ast.ImportFrom` — `node.names[].name`), либо сшивать логические строки до закрывающей скобки перед матчингом. `ast`-подход заодно убирает false-positive на строках-комментариях/докстрингах с похожим текстом. Аналогичную слепую зону стоит проверить у `no-private-method-calls`, если он тоже построчный. ## Acceptance - [ ] `no-private-imports` ловит приватные символы в скобочной multi-line форме `from ... import (...)`. - [ ] Тест-фикстура на multi-line импорт (приватный — fail, публичный — pass). - [ ] (опц.) проверена/починена та же зона у `no-private-method-calls`. Связано: baton #687 (boy-scout-зачистка запретов; Группа A-хвост зависит от ручной дочистки, пока хук слеп).
Author
Owner

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

Разбор no-private-imports переведён с построчной регулярки на ast, поэтому скобочный многострочный импорт ловится по построению — совпадение ищется не в строке с from … import, а в самих именах узла. На jamzap/backend это 30 нарушений из 51, которые до сих пор проходили молча. Заодно снят обратный дефект: алиас больше не считается импортируемым символом (from x import public as _local — чисто, from x import _private as public — FAIL).

Фикстуры: fixtures/private_imports/multiline.py и multiline_last.py (приватное имя последним — мутация «проверять только первое имя» иначе выживала), alias.py, private_as_public.py, relative.py, deferred.py, star.py, bom.py.

Осталось за рамками этой задачи и ведётся в #19: приватный модуль в пути импорта (from ._private import public) по-прежнему проходит — смотрится импортируемое имя, а не путь.

Закрываю: сделано в [#13](https://git.homedevlab.ru/senokosov/pre-commit-hooks/pulls/13) (в `main`, версия `0.15.0`). Разбор `no-private-imports` переведён с построчной регулярки на `ast`, поэтому скобочный многострочный импорт ловится по построению — совпадение ищется не в строке с `from … import`, а в самих именах узла. На jamzap/backend это 30 нарушений из 51, которые до сих пор проходили молча. Заодно снят обратный дефект: алиас больше не считается импортируемым символом (`from x import public as _local` — чисто, `from x import _private as public` — FAIL). Фикстуры: `fixtures/private_imports/multiline.py` и `multiline_last.py` (приватное имя последним — мутация «проверять только первое имя» иначе выживала), `alias.py`, `private_as_public.py`, `relative.py`, `deferred.py`, `star.py`, `bom.py`. Осталось за рамками этой задачи и ведётся в [#19](https://git.homedevlab.ru/senokosov/pre-commit-hooks/issues/19): приватный **модуль** в пути импорта (`from ._private import public`) по-прежнему проходит — смотрится импортируемое имя, а не путь.
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#5
No description provided.