chronaxis.core.validation
源码基线:8904c2f744951857659abd40aaa3535d57faf53c
1"""检查核心模型的文本、简单名称、来源序号和不可变标量。23输入为已经解析的Python对象;文本检查返回原文,序号检查返回精确Python整数,4标量检查只判断类型。原字段可以含点号;标识符须符合ASCII简单名称规则,5不得占用Python关键字或已注册的表达式函数名。本模块不裁剪或改名,不把bool6当整数,不检查样本质量。仅依赖标准库,没有数据读取、日志或会话状态。7"""89# 标准库提供明确的名称语法和整数协议,不用eval或对象反射。10import keyword11import re12from numbers import Integral1314# 名称语法来自设计9.2;两个首版函数名保留给表达式层。15IDENTIFIER_PATTERN = re.compile(r"[A-Za-z_][A-Za-z0-9_]*")1617# 首版注册函数名占用统一名称空间,避免信号遮蔽函数语义。18RESERVED_NAMES = frozenset({"abs", "isclose"})19# 逻辑标量只接受这些不可变Python类型,数组标量由适配层显式转换。20SCALAR_TYPES = (int, float, bool, str, type(None))212223def require_text(value: str, field: str) -> str:24 """检查非空来源文本并保留原文本身份,不自动strip或改名。2526 参数: value (str)为来源名或原字段文本;field (str)用于错误定位。27 返回: str;原文本,点号及两端非全空白不被改写。28 异常: TypeError为非文本;ValueError为空或全空白。29 """30 # 来源身份不能由任意对象的str猜测。31 if not isinstance(value, str):32 # 精确指出哪个模型字段类型错误。33 raise TypeError(f"{field}必须为字符串")34 # 全空白不是可定位的来源标识;正常文本不作归一化。35 if not value.strip():36 # 缺项不能用空名字继续分析。37 raise ValueError(f"{field}不能为空或全空白")38 # 返回原文本以保留字段定位身份。39 return value404142def require_identifier(value: str, field: str) -> str:43 """校验设计9.2的简单标识符,不接受属性路径或隐藏替换。4445 参数: value (str)为标识符、来源名或表达式别名;field (str)用于错误定位。46 返回: str;原合法标识,大小写保留。47 异常: TypeError为非文本;ValueError为语法、关键字或保留名冲突。48 """49 # 先处理缺项和类型,后续regex只看到明确文本。50 text = require_text(value, field)51 # fullmatch不允许点号、空格、非ASCII或尾随其他字符。52 if IDENTIFIER_PATTERN.fullmatch(text) is None or keyword.iskeyword(text) or text in RESERVED_NAMES:53 # 不自动把点号替换为下划线;保留名按首版契约拒绝。54 raise ValueError(f"{field}须为非关键字/保留名的ASCII简单标识符")55 # 名称只做校验,不改变不同大小写的身份。56 return text575859def require_sequence(value: int) -> int:60 """校验来源内非负精确序号,不设置未经设计要求的64位上限。6162 参数: value (Integral)为原来源稳定序号;bool不属于序号。63 返回: int;精确序号,不使用float或取模。64 异常: TypeError为非整数或bool;ValueError为负序号。65 """66 # True不能被当作来源序号1。67 if isinstance(value, bool) or not isinstance(value, Integral):68 # 非整数不靠截断转换修补。69 raise TypeError("sequence必须为非布尔整数")70 # 不损失位值地归一到Python整数。71 result = int(value)72 # 来源序号非负,不自动重编号负输入。73 if result < 0:74 # 错误序号不能作为合法样本身份。75 raise ValueError("sequence必须非负")76 # 返回不可变精确身份。77 return result787980def require_scalar(value: int | float | bool | str | None) -> None:81 """限制逻辑样本的值类型,允许保留尚未检查质量的原始值。8283 参数:84 value: 原生int、float、bool、str或None;不接受数组、容器和自定义对象。85 返回:86 None: 只验证值属于允许的不可变标量类型。有效性、数值范围和声明类型87 由后续模型及数值层检查,None和非有限浮点可以保留为诊断值。88 异常:89 TypeError: 输入不是允许的原生标量类型。90 """91 # 精确类型避免自定义数值子类或容器携带可变状态。92 if type(value) not in SCALAR_TYPES:93 # 数组标量不能未经适配校验就伪装成逻辑输入。94 raise TypeError("逻辑值仅接受原生int/float/bool/str/None")95