- 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>
82 lines
2.5 KiB
Python
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]
|