100 lines
3.7 KiB
Python
100 lines
3.7 KiB
Python
|
|
"""引导器的配置解析自测: 脏配置不许把引导器搞崩.
|
||
|
|
|
||
|
|
用法: python3 内核/自测配置.py
|
||
|
|
|
||
|
|
覆盖:
|
||
|
|
* 读包要求 / db段 / _解析包表 对脏数据 (字符串当数组, 数字当对象, 缺键, 坏 json) 的容错
|
||
|
|
* 取对象 / 取清单 两个 json 取值助手
|
||
|
|
* 版本比较的边界 (相等, 短版本补零, 空要求)
|
||
|
|
跑完 0 退出, 有 FAIL 退出 1.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import importlib.util
|
||
|
|
import sys
|
||
|
|
from pathlib import Path
|
||
|
|
from types import ModuleType
|
||
|
|
from typing import Any
|
||
|
|
|
||
|
|
项目根 = Path(__file__).resolve().parent.parent
|
||
|
|
if not (项目根 / "UEFI.boot.py").exists():
|
||
|
|
项目根 = Path(__file__).resolve().parent
|
||
|
|
|
||
|
|
失败数 = 0
|
||
|
|
|
||
|
|
|
||
|
|
def 载入引导器() -> Any:
|
||
|
|
"""动态加载 UEFI.boot.py (文件名带点, 没法当模块名 import).
|
||
|
|
|
||
|
|
坑: exec_module 之前必须把模块塞进 sys.modules, 否则 py3.14 的 dataclass 找不到
|
||
|
|
cls.__module__ 对应的模块, 直接 AttributeError ('NoneType' has no attribute '__dict__').
|
||
|
|
返回类型写 Any: 动态加载出来的模块成员对类型检查器是未知的, 当成 Any 用最省事.
|
||
|
|
"""
|
||
|
|
spec = importlib.util.spec_from_file_location("uefi_boot", str(项目根 / "UEFI.boot.py"))
|
||
|
|
if spec is None or spec.loader is None:
|
||
|
|
print("[FAIL] 加载 UEFI.boot.py 失败")
|
||
|
|
sys.exit(1)
|
||
|
|
模块: ModuleType = importlib.util.module_from_spec(spec)
|
||
|
|
sys.modules["uefi_boot"] = 模块
|
||
|
|
spec.loader.exec_module(模块)
|
||
|
|
return 模块
|
||
|
|
|
||
|
|
|
||
|
|
def 核对(名称: str, 实际: object, 期望: object) -> None:
|
||
|
|
global 失败数
|
||
|
|
好 = 实际 == 期望
|
||
|
|
if not 好:
|
||
|
|
失败数 += 1
|
||
|
|
print(f"[{'OK ' if 好 else 'FAIL'}] {名称}: {实际!r}" + ("" if 好 else f" (期望 {期望!r})"))
|
||
|
|
|
||
|
|
|
||
|
|
引导 = 载入引导器()
|
||
|
|
|
||
|
|
脏包 = {
|
||
|
|
"packages": [
|
||
|
|
{"name": "a", "version": ">=1", "required": True},
|
||
|
|
"脏项",
|
||
|
|
3,
|
||
|
|
{"name": ""},
|
||
|
|
{"name": "b"},
|
||
|
|
]
|
||
|
|
}
|
||
|
|
核对(
|
||
|
|
"读包要求: 脏项混合只留两条有名字的",
|
||
|
|
[(条.名, 条.要求, 条.必需) for 条 in 引导.读包要求(脏包)],
|
||
|
|
[("a", ">=1", True), ("b", "", False)],
|
||
|
|
)
|
||
|
|
核对("读包要求: packages 是字符串", 引导.读包要求({"packages": "不是数组"}), [])
|
||
|
|
核对("读包要求: 没有 packages 键", 引导.读包要求({}), [])
|
||
|
|
核对("读包要求: 项是数字", 引导.读包要求({"packages": [7]}), [])
|
||
|
|
|
||
|
|
核对("db段: 正常", 引导.db段({"db": {"name": "x"}}), {"name": "x"})
|
||
|
|
核对("db段: 缺键", 引导.db段({}), {})
|
||
|
|
核对("db段: 是数字", 引导.db段({"db": 5}), {})
|
||
|
|
核对("db段: 是 None", 引导.db段({"db": None}), {})
|
||
|
|
|
||
|
|
核对("_解析包表: 空数组", 引导._解析包表("[]"), {})
|
||
|
|
核对("_解析包表: 坏 json", 引导._解析包表("坏 json"), None)
|
||
|
|
核对(
|
||
|
|
"_解析包表: 包名要归一化 (PEP 503)",
|
||
|
|
引导._解析包表('[{"name": "P_Sql.Thing", "version": "1.2"}]'),
|
||
|
|
{"p-sql-thing": "1.2"},
|
||
|
|
)
|
||
|
|
|
||
|
|
核对("取对象: None", 引导.取对象(None), {})
|
||
|
|
核对("取对象: 数组", 引导.取对象([1]), {})
|
||
|
|
核对("取清单: 字符串", 引导.取清单("x"), [])
|
||
|
|
核对("取清单: 数组", 引导.取清单([1, 2]), [1, 2])
|
||
|
|
|
||
|
|
核对("缺 db 段时库名兜底", 引导.db.从配置(引导.db段({})).name, "efi_kernel")
|
||
|
|
核对("版本比较: 相等算满足", 引导.满足要求("2.9.9", ">=2.9.9"), True)
|
||
|
|
核对("版本比较: 短版本补零", 引导.满足要求("2.9", ">=2.9.0"), True)
|
||
|
|
核对("版本比较: 差一位不算满足", 引导.满足要求("2.9.8", ">=2.9.9"), False)
|
||
|
|
核对("版本比较: 空要求 = 不校验", 引导.满足要求("", ""), True)
|
||
|
|
核对("版本比较: 没装就是没装", 引导.满足要求("", ">=1.0"), False)
|
||
|
|
|
||
|
|
print()
|
||
|
|
print(f"结论: {'全过' if 失败数 == 0 else f'{失败数} 项没过'}")
|
||
|
|
sys.exit(0 if 失败数 == 0 else 1)
|