昨天我們在 CodebaseAnalyzer 中建立了路徑邊界防禦與專案骨架。今天我們要進入下一步:讓分析引擎真正遍歷專案檔案。
最直覺的寫法通常長這樣:
for file in root.rglob("*.py"):
read(file)
但把這段程式碼直接跑在真實專案上會立刻崩潰:.venv 動輒包含數千個第三方套件檔案,快取目錄(__pycache__)充斥暫存檔,更危險的是可能直接把 .env 或私鑰讀進來。
做 Codebase Agent 的第一步不是狂讀猛讀,而是先學會「閉眼」。
我們在掃描器中定義兩道明確的過濾門檻:
app/codebase.py 實作掃描器我們在昨天的 CodebaseAnalyzer 中加入 discover_python_files() 方法:
# app/codebase.py (續接昨日進度)
import os
from pathlib import Path
from typing import List, Set
class CodebaseAnalyzer:
# 略過目錄黑名單
IGNORED_DIRS: Set[str] = {
".git", ".venv", "venv", "__pycache__",
".mypy_cache", ".pytest_cache", "data"
}
# 敏感副檔名與檔名特徵
SENSITIVE_SUFFIXES: Set[str] = {
".pem", ".key", ".p12", ".pfx"
}
SENSITIVE_NAMES: Set[str] = {
"id_rsa", "id_ed25519"
}
def __init__(self, repo_root: str):
self.repo_root = Path(repo_root).resolve()
if not self.repo_root.exists():
raise FileNotFoundError(f"目標目錄不存在: {self.repo_root}")
def is_safe_file(self, file_path: Path) -> bool:
"""檢查檔案是否安全且非敏感資訊"""
name = file_path.name.lower()
# 阻絕 .env 開頭的檔案
if name.startswith(".env"):
return False
# 阻絕敏感副檔名
if file_path.suffix.lower() in self.SENSITIVE_SUFFIXES:
return False
# 阻絕金鑰名稱
if any(token in name for token in self.SENSITIVE_NAMES):
return False
return True
def discover_python_files(self) -> List[Path]:
"""安全遍歷 repository,回傳所有合規的 Python 檔案相對路徑"""
discovered = []
# 使用 os.walk 手動控制目錄遍歷,便於原地剪枝 (in-place pruning)
for root, dirs, files in os.walk(self.repo_root):
# 剪除黑名單目錄,防止進入 .venv 或 .git
dirs[:] = [d for d in dirs if d not in self.IGNORED_DIRS]
for file in files:
if not file.endswith(".py"):
continue
full_path = Path(root) / file
if self.is_safe_file(full_path):
rel_path = full_path.relative_to(self.repo_root)
discovered.append(rel_path)
return sorted(discovered)
tests/unit/test_scanner.py在 tests/unit/ 下加入測試,驗證掃描器是否確實做到「閉眼」:
# tests/unit/test_scanner.py
from app.codebase import CodebaseAnalyzer
def test_discover_files_with_ignored_and_sensitive(tmp_path):
repo = tmp_path / "test_repo"
repo.mkdir()
# 建立合法檔案
(repo / "src").mkdir()
(repo / "src" / "app.py").write_text("print(1)")
# 建立應該被忽略的檔案與目錄
(repo / ".venv").mkdir()
(repo / ".venv" / "lib.py").write_text("print(2)")
(repo / ".env").write_text("SECRET=123")
(repo / "id_rsa").write_text("private_key")
analyzer = CodebaseAnalyzer(str(repo))
files = analyzer.discover_python_files()
# 斷言:只抓出合法的 src/app.py
assert [str(f).replace("\\", "/") for f in files] == ["src/app.py"]
執行測試確認綠燈:
uv run pytest tests/unit/test_scanner.py -v
mobileai-local-rag現在將過濾器套用到我們的受測目標 mobileai-local-rag:
uv run python -m app.cli index
輸出結果:
{
"scanned_files": 7,
"skipped_dirs": [".git", ".venv", "__pycache__"],
"blocked_sensitive": 0,
"status": "ready_for_ast"
}
原本目錄下包含虛擬環境超過 3,000 個檔案,經由剪枝與過濾,收斂為 7 支純淨的目標業務程式碼。
乾淨的檔案清單已就緒。有了這道安全防線,明天在 Day 7 中,我們將正式把 Python 原生 ast 掛上來,逐行解析這 7 支檔案中的 Class、Function 與 Import 依據!