Skip to main content

import-linter-sees-function-level-imports

import-linter(및 대부분의 정적 아키텍처 게이트)는 AST 를 읽으므로 **함수 안 지연 import 도 계약 위반으로 잡는다**. 순환 import 를 피하려고 import 를 함수 안으로 옮기는 흔한 처방이 아키텍처 계약에는 통하지 않는다. 처방은 계약이 허용하는 레이어에 헬퍼를 두는 것(때로는 의도적 중복). 트리거: `lint-imports` 실패인데 파일 상단엔 그 import 가 없음, "circular import" 를 lazy import 로 고친 직후 계약 깨짐, layered/forbidden contract.

Jump to install

Source facts

Repository
blas1n/claude-skills
Last source activity
August 11, 2026 at 06:34
Detected SKILL.md language
Korean
Stars
2
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
import-linter-sees-function-level-imports
description
import-linter(및 대부분의 정적 아키텍처 게이트)는 AST 를 읽으므로 **함수 안 지연 import 도 계약 위반으로 잡는다**. 순환 import 를 피하려고 import 를 함수 안으로 옮기는 흔한 처방이 아키텍처 계약에는 통하지 않는다. 처방은 계약이 허용하는 레이어에 헬퍼를 두는 것(때로는 의도적 중복). 트리거: `lint-imports` 실패인데 파일 상단엔 그 import 가 없음, "circular import" 를 lazy import 로 고친 직후 계약 깨짐, layered/forbidden contract.
# 지연 import 는 순환은 피해도 아키텍처 계약은 못 피한다 ## Problem 레이어 A 에서 레이어 B 의 헬퍼가 필요하다. 상단에 import 하면 순환이 난다: ``` ImportError: cannot import name X from partially initialized module ... (circular import) ``` 관례적 처방대로 함수 안으로 내린다: ```python async def _provision(session, run, workspace_dir): from backend.workflow.application.runtime.account_resolution import ( # noqa: PLC0415 product_is_client_attach, ) ... ``` 런타임 순환은 사라진다. 그런데 `lint-imports` 가 깨진다: ``` backend.api.webhooks is not allowed to import plugin: - backend.api.webhooks -> ... -> connector_dispatch._github (l.96) connector_dispatch._github -> runtime.account_resolution (l.82) ← 함수 안 82행 runtime.account_resolution -> ... -> plugin.audit.service ``` - **증상**: 파일 **상단에는 그 import 가 없는데** 계약 위반 경로에 그 파일이 나온다. 줄 번호가 함수 본문을 가리킨다. - **근본 원인**: import-linter 는 실행하지 않는다. **AST 를 읽는다.** `import` 문이 모듈 최상단이든 함수 안이든 `if TYPE_CHECKING:` 밖이든, 전부 "이 모듈은 저 모듈에 의존한다"로 센다. 당연하다 — 함수가 호출되면 그 의존은 **실제로** 일어난다. - **흔한 오해**: "lazy import 는 의존이 아니다." 런타임 그래프에는 늦게 나타날 뿐, 의존은 의존이다. ## Solution 세 가지 중 하나. 위에서부터 시도한다. **1. 타입만 필요하면 `TYPE_CHECKING`** — 런타임 의존이 아니므로 대부분의 게이트가 제외한다. ```python from typing import TYPE_CHECKING if TYPE_CHECKING: from ._github import GithubDeliveryDeps ``` **2. 헬퍼를 계약이 허용하는 레이어로 옮긴다** — 가장 옳은 답인 경우가 많다. 그 헬퍼가 두 레이어 모두의 관심사라면 애초에 위치가 틀린 것이다. **3. 의도적으로 중복한다 — 그리고 왜인지 적는다.** 헬퍼가 작고(10줄) 계약이 진짜 지키고 싶은 것을 지키고 있다면, 중복이 계약을 뚫는 것보다 낫다. ```python async def product_runs_in_place(session, product_id) -> bool: """... Deliberately duplicated from ``runtime.account_resolution.product_is_client_attach`` — the R2c contract keeps the inbound layer free of plugin imports, and that module reaches ``plugin.audit`` transitively. Importing it from here (even LAZILY — import-linter reads function-level imports too) puts the inbound layer one hop from a plugin. """ ``` ⚠️ 중복을 택했다면 **두 구현이 드리프트하지 않게 테스트로 묶어라**(같은 입력에 같은 답). ## Key Insights - **순환 import 와 아키텍처 계약은 다른 문제다.** 전자는 런타임 초기화 순서, 후자는 의존 그래프의 모양. 같은 처방(lazy import)이 하나만 고친다. - 계약이 깨졌을 때 **줄 번호를 봐라.** 함수 본문을 가리키면 이 함정이다. - 계약을 우회할 방법을 찾고 있다면 대개 **설계가 신호를 보내는 중**이다. 왜 이 레이어가 저 레이어를 알아야 하는지 먼저 물어라 — 답이 "작은 사실 하나" 라면 중복이 정답이다. - 다음에 먼저 확인할 것: 지연 import 를 추가한 뒤 **`lint-imports` 를 돌려라.** 유닛 테스트는 통과한다. ## Red Flags - 순환 import 를 함수 안 import 로 고친 직후 아키텍처 게이트가 깨졌다 - 계약 위반 경로의 줄 번호가 모듈 상단이 아니다 - `# noqa: PLC0415` 를 새로 달았다(= 지연 import 를 추가했다) - 레이어 경계를 넘는 이유가 "불리언 하나 알아내려고"다
View on GitHub