본문 바로가기
C.W.K.
Stream
Lesson 05 of 06 · published

문단이 놓친 가장자리들

~13 min · imports, records, drift, verification

Level 0흩어진 부품
0 XP0/48 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete

기록은 하나라고 했어

큐 모듈은 서로를 임포트 안 한다. 앞 레슨의 이 규칙은 예외를 규칙 옆 문단에 이유랑 같이 실어. 다음 후보가 선례를 들먹이는 대신 논증을 하게 하려고. 한동안 그 문단은 딱 하나라고 했어. 행 기반 레지스트리가 평범한 레지스트리를 임포트한다.

그 뒤 일곱 주 동안 코드가 한 일은 이거야.

  • 제네릭 작업장 둘을 위한 명령 층이 들어오면서, 로드 때 작업장 명령 배관이랑 리뷰 커널을 임포트했어. 모듈 하나에서 가장자리가 둘 나온 거야. 이유는 첫 예외랑 같은 모양이야. 둘 중 뭘 주입하든 제네릭 작업장 둘이 같은 모듈을 넘기게 될 뿐이고, 그건 디커플링이 아니라 격식이야.
  • 리뷰 커널이 가족 설정 리더를 임포트하기 시작했어. 지연 임포트로, 로컬 모델 리뷰어의 모델을 정하는 함수 하나 안에서만. 지연 임포트인 건 그 리뷰어만 그 파일을 쓰기 때문이고, 이 가장자리는 빠진 파일이 얼마나 늦게 무는지도 보여줘. 이 가장자리가 발견됐을 땐, 설정 리더를 복사본으로 안 가진 소비자라면 그 리뷰어가 골라지는 순간 임포트 에러로 라운드 전체를 끝냈을 거야. 그 자리에서 라운드는 런타임 에러만 잡았거든. 같은 리뷰가 코드를 고쳐서, 이제 리더가 없으면 런타임 에러가 나고 라운드는 그 리뷰어 하나만 이유를 달아 건너뛰어.
  • 리뷰어 라우팅이 여러 두뇌 호출을 소유한 시스템으로 옮겨가면서, 리뷰 커널이 로드 때 두뇌 호출 클라이언트를 임포트하기 시작했어.

명령 층의 가장자리 둘이랑 설정 리더 가장자리는 그중 처음 것이 나타나고 세 주쯤 뒤에 기록에 올랐어. 문단이 아직 하나라고 말하는 동안 코드에서 그걸 찾아낸 리뷰 덕에. 그래서 기록엔 지금 예외 셋이 가장자리 넷을 덮고 있어. 두뇌 호출 가장자리는 오늘 코드에 있고, 기록의 목록엔 아직 없어. 코드엔 다섯, 기록엔 넷이야. 일부러 가장자리로 안 치는 예외도 하나 있어. 큐 모듈들은 생성된 두뇌 카탈로그를 임포트해. 그게 카탈로그 뿌리라서야. 자기 두뇌 목록을 따로 쥔 모듈이 있으면, 그게 바로 가족의 가드가 잡으려고 있는 그림자 목록이 되니까.

현장을 지킨 것

어느 소비자에서도 깨진 건 없어. 첫 규칙 옆에 두 번째 규칙이 있어서야. 그리고 기계적으로 확인할 수 있는 건 그쪽이야. 매니페스트는 가장자리를 통째로 지킨다. 임포트하는 모듈을 받는 저장소는 그게 임포트하는 모듈도 다 받아. 리뷰 커널의 소비자는 전부 설정 리더랑 두뇌 호출 클라이언트를 받고, 명령 층의 소비자는 그게 임포트하는 둘을 다 받아. 그래서 문단이 잊은 임포트도 배달은 지켰어.

두 규칙은 모양이 달라. "가장자리마다 적힌 이유가 있다"는 글에 살고, 사람이 마침 볼 때 읽혀. "임포트하는 모듈의 대상은 임포트되는 모듈 대상의 부분집합이다"는 파일 둘의 성질이고, 테스트가 돌 때마다 계산할 수 있어. 앞의 건 실제와 어긋났어. 뒤의 건 버텼고.

큐 밖에선 키트가 그래프가 됐어

임포트 금지 규칙은 처음부터 큐 커널에 대해 적힌 거였어. 파이썬 쪽 다른 데선 키트가 이제 일부러 가장자리를 가져. 트랙 6 의 공유 상태 층은 조립품이야. 그 권한 모듈이 저장소랑 라우트 묶음 여섯을 엮고, 제공자 권한은 가족 권한 위에 지어져. 암호화 도우미는 공용 키체인 리더로 자기 키를 읽어. 이미지 작업 클라이언트는 커버 이미지 클라이언트 위에 지어지고. 그 가장자리들도 하나하나 다 통째로 배달돼. 이 레슨을 쓴 날 가장자리마다 확인했어.

트랙 3 의 배포 모형한테 이게 중요해. 복사 기계는 의존 없는 잎에 맞는 기계고, 키트 자기 기록이 그걸 다시 살펴볼 다음 이유를 적어뒀어. 소비자가 아직 안 가진 의존을 기르는 복사본 파이썬 잎. 그 트리거가 아직 안 울린 건 통째로 지키는 규칙 덕이야. 파이썬 쪽이 가장자리를 또 기르면 제일 먼저 시험할 게 그거야.

기록된 예외는 코드에 대한 주장이야. 코드에 대고 확인해. 안 그러면 그 기록이 막으려고 쓴 선례가 돼. 아무도 소스에서 다시 뽑지 않는 예외 목록은 손으로 관리하는 표랑 똑같이 삭아. 임포트 추가는 파일 하나에 줄 하나고, 기록 갱신은 아무도 안 보는 어딘가의 편집이거든. 가장자리 목록을 코드에서 뽑고, 기록이랑 비교하고, 안 맞으면 깨뜨려. 이유는 글로 남아. 그런데 목록은 계산돼야 해.

뻔한 검사가 하나를 놓치는 이유

빠른 검사는 모듈마다 맨 위의 임포트 줄을 훑을 거야. 가장자리 대부분은 찾고 지연 임포트는 놓쳐. 그 임포트는 함수 몸통 안에 있고, 특정 종류의 리뷰어를 정할 때만 돌거든. 그게 바로 빠졌을 때 제일 비쌌던 가장자리야. 임포트 에러를 바꿔 잡기 전까지는, 설정 리더가 없는 소비자가 그 리뷰어가 골라진 순간, 어느 배달하고도 먼 자리에서 라운드 하나를 통째로 잃었을 거야. 오늘은 그 리뷰어 하나만 이유를 달고 빠져. 진짜 검사는 파일 첫 줄들이 아니라 구문 트리 전체를 걸어.

Code

가장자리는 코드에서 뽑고, 기록이랑 매니페스트를 거기 맞춰·python
import ast
from pathlib import Path

KIT = Path("kit/python")
MODULES = {p.stem for p in KIT.glob("kit_*.py")}
CATALOG_ROOT = {"kit_pippa_brains"}       # generated: a root, not an edge


def kit_imports(module: str) -> set[str]:
    """Every kit module this one imports - ANYWHERE in the file.
    A lazy import inside a function is still an edge; a scan of the
    first lines of the file would miss exactly that one."""
    tree = ast.parse((KIT / f"{module}.py").read_text())
    found: set[str] = set()
    for node in ast.walk(tree):
        if isinstance(node, ast.ImportFrom):
            names = [node.module or ""] + [a.name for a in node.names]
        elif isinstance(node, ast.Import):
            names = [a.name for a in node.names]
        else:
            continue
        for name in names:
            leaf = name.rsplit(".", 1)[-1]
            if leaf in MODULES and leaf != module:
                found.add(leaf)
    return found - CATALOG_ROOT


def code_edges() -> set[tuple[str, str]]:
    return {(m, dep) for m in MODULES for dep in kit_imports(m)}


def test_every_queue_edge_has_a_written_reason(recorded_queue_edges):
    """The RULE is scoped to the queue kernel; so is its record."""
    queue = {(a, b) for a, b in code_edges() if a.startswith("kit_queue_")}
    missing = queue - recorded_queue_edges
    assert not missing, f"queue edges with no written reason: {sorted(missing)}"


def test_every_edge_ships_whole(manifest_targets):
    """The rule that held, for EVERY edge in the kit: whoever receives
    the importer also receives what it imports."""
    for importer, dep in code_edges():
        assert manifest_targets[importer] <= manifest_targets[dep], (
            f"{importer} reaches a repo that lacks {dep}"
        )

External links

Exercise

네 코드베이스에서 예외 목록이랑 같이 문서화된 규칙을 하나 찾아. 허용된 임포트, 허락된 의존, 승인된 우회. 지연이나 조건부로 벌어지는 것까지 포함해서 실제 목록을 코드에서 뽑아. 둘을 비교해. 다른 데마다 코드랑 기록 중 뭐가 틀렸는지 정하고, 다음에 또 안 맞으면 깨지는 테스트를 붙여.
Hint
함수 몸통 안이랑 조건문 뒤의 임포트부터 찾아. 기록에서 빠져 있을 확률이 제일 높은 가장자리들이야. 경로 하나를 처리하려고, 규칙이 아니라 그 경로를 생각하던 사람이 붙인 거니까.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인 — 댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.