#!/usr/bin/env python3 # -*- coding: utf-8 -*- """import_closure 门禁离线单测(复刻 pbls pbl_common 半迁移事故形状)。""" import os import shutil import sys # 本地无 ahserver 等平台依赖 → 绕开包 __init__,按文件直接加载被测模块 import importlib.util as _ilu _spec = _ilu.spec_from_file_location( "import_closure", os.path.join(os.path.dirname(os.path.abspath(__file__)), "pipeline_service", "import_closure.py")) _ic = _ilu.module_from_spec(_spec) _spec.loader.exec_module(_ic) check_closure_for_delivery = _ic.check_closure_for_delivery closure_report = _ic.closure_report ROOT = "/tmp/ic_test" FAILS = [] def check(name, cond, detail=""): print(("PASS" if cond else "FAIL"), name, detail[:160]) if not cond: FAILS.append(name) def w(path, content): full = os.path.join(ROOT, path) os.makedirs(os.path.dirname(full), exist_ok=True) with open(full, "w", encoding="utf-8") as f: f.write(content) # ── 用例 1:事故形状复刻——重写 errors 删旧符号,内部+依赖仍 import 旧名 ── shutil.rmtree(ROOT, ignore_errors=True) w("modules/pbl_common/pbl_common/__init__.py", "from .kernel import PblError\n") w("modules/pbl_common/pbl_common/errors.py", "class PBLError(Exception):\n pass\n\n\ndef err(cat, key):\n return {}\n\n\ndef fail(cat, key):\n return {}\n") w("modules/pbl_common/pbl_common/kernel.py", "class PblError(Exception):\n code = 'X'\n") w("modules/pbl_common/pbl_common/context.py", "from pbl_common.errors import TenantMissingError, ErrorCode\n") w("modules/pbl_validation/pbl_validation/__init__.py", "") w("modules/pbl_validation/pbl_validation/api.py", "from pbl_common.errors import fail\nfrom pbl_common.audit import write_audit\n") # 本任务写了 pbl_common 的 errors.py(重写)→ 门禁应抓到 context.py 断裂 written = [os.path.join(ROOT, "modules/pbl_common/pbl_common/errors.py")] probs, involved = check_closure_for_delivery(ROOT, written) check("事故形状被拦截", probs is not None and len(probs) >= 2, "problems=%s involved=%s" % (probs and len(probs), involved)) check("涉及包=pbl_common", involved == {"pbl_common"}, str(involved)) syms = {(p["file"], p["symbol"]) for p in (probs or [])} check("抓到 context.py TenantMissingError", ("modules/pbl_common/pbl_common/context.py", "TenantMissingError") in syms, str(syms)) check("抓到依赖方 pbl_validation missing-module", any(p["kind"] == "missing-module" and p["symbol"] == "pbl_common.audit" for p in (probs or [])), str(probs)) rep = closure_report(ROOT, written) check("closure_report 非空含断裂", "断裂" in rep and "ImportError" in rep, rep[:80]) # ── 用例 2:未触碰的包存量断裂不拦(防死锁)── w("modules/pbl_unrelated/pbl_unrelated/__init__.py", "") w("modules/pbl_unrelated/pbl_unrelated/api.py", "from pbl_common.nothere import X\n") probs2, involved2 = check_closure_for_delivery(ROOT, written) check("未触碰包不在 involved", "pbl_unrelated" not in involved2, str(involved2)) # 但它 import pbl_common(涉及包)→ 应作为引用方被核验 check("引用涉及包的文件被核验", any(p["file"].startswith("modules/pbl_unrelated") for p in (probs2 or [])), str(probs2)) # ── 用例 3:修复后(补兼容符号)门禁放行 ── w("modules/pbl_common/pbl_common/errors.py", "class PBLError(Exception):\n pass\n\n\ndef err(cat, key):\n return {}\n\n\ndef fail(cat, key):\n return {}\n\n\n" "# 兼容层(设计文档§5 向后兼容)\n" "class PblError(PBLError):\n code = 'PBL_E'\n\n\nclass TenantMissingError(PblError):\n pass\n\n\nclass TenantInvalidError(PblError):\n pass\n\n\nclass ErrorCode(object):\n TENANT_MISSING = 'PBL-TENANT-0001'\n") w("modules/pbl_common/pbl_common/audit.py", "def write_audit(*a, **kw):\n return ''\n") # context.py 引用修复 + unrelated 的 nothere 仍在 → 只应剩 unrelated 断裂 probs3, _ = check_closure_for_delivery(ROOT, written) remain = [p for p in (probs3 or []) if not p["file"].startswith("modules/pbl_unrelated")] check("补兼容符号后 pbl_common 闭包干净", not remain, str(remain)) # ── 用例 4:逃逸阀——__getattr__ 动态模块不误伤 ── w("modules/pbl_dyn/pbl_dyn/__init__.py", "def __getattr__(name):\n return None\n") w("modules/pbl_dyn/pbl_dyn/sub.py", "X = 1\n") w("modules/pbl_validation/pbl_validation/dyn_user.py", "from pbl_dyn import WHATEVER\n") probs4, _ = check_closure_for_delivery( ROOT, [os.path.join(ROOT, "modules/pbl_dyn/pbl_dyn/sub.py")]) check("动态模块逃逸阀", not any("dyn" in p["file"] for p in (probs4 or [])), str(probs4)) # ── 用例 5:语法坏文件跳过(归空壳门禁)不崩 ── w("modules/pbl_validation/pbl_validation/broken.py", "def broken(:\n") probs5, _ = check_closure_for_delivery( ROOT, [os.path.join(ROOT, "modules/pbl_validation/pbl_validation/broken.py")]) check("语法坏文件不崩", probs5 is not None, str(probs5)) # ── 用例 6:无 .py 写入(纯文档任务)零成本放行 ── probs6, involved6 = check_closure_for_delivery(ROOT, [os.path.join(ROOT, "docs/x.md")]) check("纯文档任务不拦", probs6 == [] and not involved6, str((probs6, involved6))) # ── 用例 7:相对导入 + 别名 + 星号 ── w("modules/pbl_rel/pbl_rel/__init__.py", "from .core import helper\n__all__ = ['helper']\n") w("modules/pbl_rel/pbl_rel/core.py", "def helper():\n return 1\n") w("modules/pbl_rel/pbl_rel/use.py", "from . import helper\nfrom .core import helper as h2\nfrom pbl_rel.core import *\n") probs7, _ = check_closure_for_delivery(ROOT, [os.path.join(ROOT, "modules/pbl_rel/pbl_rel/use.py")]) check("相对导入/别名/星号解析", not any("pbl_rel" in p["file"] for p in (probs7 or [])), str(probs7)) w("modules/pbl_rel/pbl_rel/bad.py", "from . import NOT_THERE\n") probs8, _ = check_closure_for_delivery(ROOT, [os.path.join(ROOT, "modules/pbl_rel/pbl_rel/bad.py")]) check("相对导入断裂也抓", any(p["symbol"] == "NOT_THERE" for p in (probs8 or [])), str(probs8)) # ── 用例 8:仓库归属——无包目录的仓库(modules/pbls 形态)入口断裂也拦 ── w("modules/pbls/app/pbls.py", "from world.api import load_world\nfrom pbl_common.kernel import PblError\n") w("modules/world/world/__init__.py", "def load_world(env=None):\n return True\n") w("modules/world/__init__.py", "from .world import load_world\n") probs9, involved9 = check_closure_for_delivery( ROOT, [os.path.join(ROOT, "modules/pbls/app/pbls.py")]) check("无包仓库入口纳入核验", "pbls" in involved9, str(involved9)) check("抓到 world.api 断裂", any(p["symbol"] == "world.api" for p in (probs9 or [])), str(probs9)) check("pbl_common.kernel 存在不误报", not any("pbl_common.kernel" in str(p) for p in (probs9 or [])), str(probs9)) print("\nFAILS:", FAILS or "NONE") sys.exit(1 if FAILS else 0)