Files
Aislo/docs/tools/group.py
T
eomsangdonandClaude Opus 5.5 2e6086c5ee feat(478-2 문서 사이트): 묶음 규칙 · 원고 생성 · mkdocs 설정 · 손글 옮김 · 검사 시험(가짜 추출)
- docs/tools/groups.yml · group.py — 함수를 로직 묶음에 자동 배정(계산 등록부 id · 파일 이름 무늬 · 안 맞음 = 묶음 없음 · 함수 목록 지문)
- docs/tools/build.py — 추출 JSON + 손글로 페이지 · 묶음 화면 · 함수 칸 · mermaid 흐름 · 세션 칸 · 서버 주소 · 설명 확인 필요 표시 · 함수 화면은 TypeDoc(markdown) · mkdocstrings(griffe)
- docs/mkdocs.yml · nav_hook.py — Material · 한국어 · 검색 · mermaid · 왼쪽 메뉴 페이지 → 묶음 → 함수
- docs/guide/B05_B06 — 8 파일을 page.md(개요 · 규칙 · 사용자 결정) + groups/*.md 17 개로 줄여 옮김
- resources/tester/test_478_docs.py · fixtures/478_docs — 가짜 추출로 배정 · 사이트 strict · 도구 대 추출 · 진짜 추출은 있을 때만

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 18:53:43 +09:00

82 lines
2.5 KiB
Python

"""함수 → 로직 묶음 자동 배정(PLAN 478-2).
규칙 = docs/tools/groups.yml · 위에서부터 첫 맞음:
registry — 계산 등록부 id 무늬 → 그 줄의 ts · pair 함수
files — 파일 이름(확장자 뺀) 무늬
어디에도 안 맞는 함수 = 「묶음 없음」(None).
"""
from __future__ import annotations
import hashlib
from fnmatch import fnmatchcase
from pathlib import Path, PurePosixPath
import yaml
TOOLS = Path(__file__).resolve().parent
NONE = "none"
def load_rules(path: Path = TOOLS / "groups.yml") -> dict:
return yaml.safe_load(path.read_text(encoding="utf-8"))
def stem(file: str) -> str:
return PurePosixPath(file.replace("\\", "/")).stem
def registry_ref_id(ref: str | None) -> str | None:
"""등록부 칸 `<경로>#<함수>` → 함수 id."""
if not ref or "#" not in ref:
return None
lang = "py" if ref.split("#", 1)[0].endswith(".py") else "ts"
return f"{lang}:{ref}"
def registry_owners(registry: list[dict], groups: list[dict]) -> dict[str, str]:
"""등록부로 정해지는 함수 id → 묶음 id."""
owners: dict[str, str] = {}
for row in registry:
rid = row.get("id", "")
group = next(
(g["id"] for g in groups if any(fnmatchcase(rid, p) for p in g.get("registry", []))),
None,
)
if group is None:
continue
for key in ("ts", "pair"):
fid = registry_ref_id(row.get(key))
if fid:
owners.setdefault(fid, group)
return owners
def file_group(file: str, groups: list[dict]) -> str | None:
name = stem(file)
for g in groups:
if any(fnmatchcase(name, p) for p in g.get("files", [])):
return g["id"]
return None
def assign(
functions: list[dict], registry: list[dict], groups: list[dict]
) -> dict[str, list[dict]]:
"""묶음 id → 함수 목록(파일 · 줄 차례) · 「묶음 없음」 = NONE."""
owners = registry_owners(registry, groups)
out: dict[str, list[dict]] = {g["id"]: [] for g in groups}
out[NONE] = []
for fn in functions:
gid = owners.get(fn["id"]) or file_group(fn["file"], groups) or NONE
out[gid].append(fn)
for fns in out.values():
fns.sort(key=lambda f: (f["file"], f.get("line", 0)))
return out
def fingerprint(fns: list[dict]) -> str:
"""함수 목록 지문 — 손글에 적힌 지문과 다르면 「설명 확인 필요」."""
text = "\n".join(sorted(f["id"] for f in fns))
return hashlib.sha1(text.encode("utf-8")).hexdigest()[:10]