"""依赖规则 6:`types` 与 `ports` 不许 import 任何第三方包。 写不成 import-linter 契约,因为「任何第三方」不是一份可枚举的清单——契约要求你把禁止 的包名列出来,而这条规则要禁的是**清单之外的一切**。所以判据反过来写:允许的只有标准库 和 `polyloop` 自己,其余一律违规。 **这个文件只读源码文本,不执行被测代码。** 它仍然属于 unit,因为分层判据是「依赖什么」 (`CLAUDE.md` §1.9),而它不连任何外部服务。 规则本身与它的理由在 `research-wiki/design/0003-public-api-shape.md` 决策八:公共类型与 接缝签名上一旦出现第三方类型,那个包的 major 就是我们的 major。 `TYPE_CHECKING` 分支里的 import 同样算数。那种 import 在运行时不发生,但它会出现在签名 的类型注解里,下游的类型检查器要解析它——于是那个包照样成了我们对外承诺的一部分。 """ import ast import sys from pathlib import Path import pytest REPO_ROOT = Path(__file__).resolve().parents[2] PACKAGE_ROOT = REPO_ROOT / "src" / "polyloop" #: 这两个模块受本规则约束。其余模块允许用第三方包,由 import-linter 的分层契约管。 PURE_MODULES = ("types", "ports") pytestmark = pytest.mark.unit def _imported_root_packages(source: str) -> set[str]: """收集一段源码里所有 import 的顶层包名,含 `TYPE_CHECKING` 分支里的。 相对 import(`from . import x`)不产生顶层包名,直接跳过——它指向的一定是本包内部。 """ roots: set[str] = set() for node in ast.walk(ast.parse(source)): if isinstance(node, ast.Import): for alias in node.names: roots.add(alias.name.split(".")[0]) elif isinstance(node, ast.ImportFrom) and node.level == 0 and node.module: roots.add(node.module.split(".")[0]) return roots def _python_files(module_name: str) -> list[Path]: return sorted((PACKAGE_ROOT / module_name).rglob("*.py")) @pytest.mark.parametrize("module_name", PURE_MODULES) def test_module_directory_is_scannable(module_name: str) -> None: """守卫自身的 fail-closed 检查:扫描目标必须真的存在且有 `.py` 文件。 没有这一条,哪天目录被改名或搬走,上面那条断言会扫到一个空列表然后安静地绿—— 而绿的含义从「没有违规」变成了「没有检查」,两者在输出上分不出来。 """ directory = PACKAGE_ROOT / module_name assert directory.is_dir(), f"扫描目标不存在:{directory}" assert _python_files(module_name), f"扫描目标里没有任何 .py 文件:{directory}" @pytest.mark.parametrize("module_name", PURE_MODULES) def test_pure_module_imports_only_stdlib_or_polyloop(module_name: str) -> None: """`types` 与 `ports` 的每一处 import 都必须落在标准库或 `polyloop` 内。""" allowed = set(sys.stdlib_module_names) | {"polyloop"} violations: list[str] = [] for path in _python_files(module_name): for root in sorted(_imported_root_packages(path.read_text(encoding="utf-8")) - allowed): violations.append(f"{path.relative_to(REPO_ROOT)}: {root}") assert violations == [], f"{module_name} 里出现了第三方 import:{violations}"