deliver: 交付收口(引擎代为提交)
This commit is contained in:
parent
9f97d58dba
commit
95b405fd25
@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""validate_models_json —— database-table-definition-spec 机械校验器(M11b-2b-A 修复版 rev3)。
|
||||
"""validate_models_json —— database-table-definition-spec 机械校验器(M11b-2b-A 修复版 rev4)。
|
||||
|
||||
QC #4/#7/#9/#10 要求:表定义 JSON 必须是 spec 规定的四段式(summary/fields/indexes/
|
||||
codes),类型必须是 spec 声明的抽象类型,主键 id 必须是 str/length>=32,索引必须用
|
||||
@ -11,13 +11,19 @@ codes),类型必须是 spec 声明的抽象类型,主键 id 必须是 str/
|
||||
QC #2 抽象类型误报:纯抽象类型名(datetime/timestamp/date/time/text/long/…)一律放行,
|
||||
只有 type 字符串本身含括号或空格(方言写法 varchar(64)/decimal(15,2)/datetime(3))
|
||||
或本身就是 spec 不存在的非抽象类型名(varchar/bigint/json/blob/…)才判违规。
|
||||
NATIVE_TYPE_WORDS 中已删除 "datetime(" / "timestamp(" 两项。
|
||||
QC #3 CLI 与文档一致:argparse 真正提供 --allow-registered-deviation(见下方「用法」)。
|
||||
同一轮 QC #3 追加要求「拦截分布可一条命令复现」:本版本在结尾输出
|
||||
``RULE_STATS: <规则标签>=<条数>`` 逐规则计数 + ``RULE_STATS_TOTAL`` 总量,
|
||||
复核者只需一条命令即可核对分布,不必依赖任何中间文件:
|
||||
python3 modules/world_sync/scripts/validate_models_json.py \
|
||||
$(ls -d modules/*/models) 2>&1 | grep "^RULE_STATS"
|
||||
QC #4 root_keys_ok:由 validate_model 显式计算 (not missing and not extra) 并随结果返回,
|
||||
main() 直接打印该布尔值,不再用「错误串里是否含『四段式』」猜测。
|
||||
QC #5 引用式 key/schema:spec 不承认引用式结构,直接判 ERROR 并提示改为内联四段式;
|
||||
COMPLIANCE_DEVIATIONS 中针对 pbl_runtime_event/pbl_entity_state 的 4 条失效登记项
|
||||
一并删除(引用式在根键检查处即 FAIL,根本到不了 fields 校验,登记形同虚设)。
|
||||
QC #1(本轮)放行机制对 auto_increment 生效:字段段「spec 未定义键」检查原先无条件报错,
|
||||
QC #1(第三轮)放行机制对 auto_increment 生效:字段段「spec 未定义键」检查原先无条件报错,
|
||||
导致 (表名,"id","auto-increment") 即使已登记、即使传了 --allow-registered-deviation,
|
||||
仍残留 `fields[N](id) 含 spec 未定义键: auto_increment` 而整体 FAIL —— id-not-str32
|
||||
可豁免、auto_increment 不可豁免,机制自相矛盾。现在该检查改为**偏离感知**:
|
||||
@ -35,7 +41,10 @@ codes),类型必须是 spec 声明的抽象类型,主键 id 必须是 str/
|
||||
典型场景:分区表物理主键需要 long + auto_increment(MySQL 1503:分区键必须进主键)。
|
||||
若某表确需此类写法,须先在其模块任务里把 (表名,"id","id-not-str32") 与
|
||||
(表名,"id","auto-increment") 登记进 COMPLIANCE_DEVIATIONS,再配合开关使用 —— 两者缺一不可,
|
||||
只登记不传开关仍 FAIL,只传开关不登记也 FAIL(本文件末尾自测 5 给出四态实测输出)。
|
||||
只登记不传开关仍 FAIL,只传开关不登记也 FAIL。
|
||||
与并行子任务 B 的接口:B 把 pbl_runtime_event/pbl_entity_state 内联为四段式后,若保留
|
||||
id=long(+auto_increment) 写法,须由 B 在其任务内按上述键格式登记(本任务按 QC #5 选 (a),
|
||||
登记表保持为空 {},不放行任何历史失效项)。
|
||||
|
||||
用法:
|
||||
python validate_models_json.py <models目录1> [<目录2> ...]
|
||||
@ -81,6 +90,49 @@ COMPLIANCE_DEVIATIONS = {}
|
||||
|
||||
DEVIATION_HINT = "需 --allow-registered-deviation 放行"
|
||||
|
||||
# QC #3(第三轮):逐规则计数标签 —— 让「真·非抽象类型仍被拦截」的分布不依赖中间文件,
|
||||
# 一条命令即可复现(结尾 RULE_STATS 行)。标签按下列顺序尝试匹配(首个命中即归类)。
|
||||
RULE_STATS_TAGS = (
|
||||
("unreadable_json", ("JSON 不可解析", "文件不可读", "根对象必须是 JSON object")),
|
||||
("reference_style_key_schema", ("引用式写法",)),
|
||||
("root_key_extra", ("根键多出 spec 未定义的段",)),
|
||||
("root_key_missing", ("根键缺少 spec 必需段",)),
|
||||
("type_db_native", ("是数据库原生类型",)),
|
||||
("type_db_dialect", ("是数据库方言具体类型",)),
|
||||
("type_not_in_abstract_table", ("不在 spec 抽象类型表内",)),
|
||||
("type_not_string", ("type 必须是非空字符串",)),
|
||||
("pk_id_not_str32", ("主键 id 必须 str 且 length>=32",)),
|
||||
("id_auto_increment", ("auto_increment",)),
|
||||
("field_length_dec_missing", ("必须带正整数",)),
|
||||
("field_nullable_bad", ("nullable 只能是",)),
|
||||
("field_name_missing", (".name 必填",)),
|
||||
("field_title_missing", (".title 必填",)),
|
||||
("field_unknown_key", ("fields[", "含 spec 未定义键")),
|
||||
("field_dup_name", ("字段名重复",)),
|
||||
("summary_shape", ("summary 必须是恰好 1 条记录的数组", "summary[0].", "summary[0] 含 spec 未定义键")),
|
||||
("primary_not_array", ("primary 必须是非空数组",)),
|
||||
("primary_unknown_col", ("primary 引用了不存在的列",)),
|
||||
("index_unknown_key", ("indexes[", "含 spec 未定义键")),
|
||||
("index_type_bad", ("idxtype 必须是",)),
|
||||
("index_fields_bad", ("idxfields 必须是非空数组",)),
|
||||
("index_ref_unknown_col", ("idxfields 引用不存在的列",)),
|
||||
("index_name_primary", ("主键不写进 indexes",)),
|
||||
("index_dup_name", ("索引名重复",)),
|
||||
("code_unknown_key", ("codes[", "含 spec 未定义键")),
|
||||
("code_required_missing", ("codes[", ".必填")),
|
||||
("code_ref_unknown_col", ("codes[", "引用不存在的列")),
|
||||
("code_cond_parentid", ("cond 必须用 parentid=",)),
|
||||
("code_table_dot", ("codes.table 禁止",)),
|
||||
)
|
||||
|
||||
|
||||
def _rule_tag(message):
|
||||
"""把一条 ERROR 文本归类到规则标签(QC #3:分布可由一条命令复现)。"""
|
||||
for tag, needles in RULE_STATS_TAGS:
|
||||
if all(n in message for n in needles):
|
||||
return tag
|
||||
return "other"
|
||||
|
||||
|
||||
def _is_reference_style(data):
|
||||
"""识别引用式结构:{"key": "schema", "schema": {...}} 之类(spec 不承认)。"""
|
||||
@ -372,13 +424,20 @@ def main(argv=None):
|
||||
total_files = 0
|
||||
total_errors = 0
|
||||
total_deviations = 0
|
||||
rule_counts = {}
|
||||
report = []
|
||||
for path in collect_targets(dirs, name_filters or None):
|
||||
errors, deviations, table, root_keys_ok = validate_model(path, strict_id=strict_id)
|
||||
total_files += 1
|
||||
total_errors += len(errors)
|
||||
total_deviations += len(deviations)
|
||||
per_file_rules = {}
|
||||
for e in errors:
|
||||
tag = _rule_tag(e)
|
||||
rule_counts[tag] = rule_counts.get(tag, 0) + 1
|
||||
per_file_rules[tag] = per_file_rules.get(tag, 0) + 1
|
||||
report.append({"path": path, "table": table, "root_keys_ok": root_keys_ok,
|
||||
"rule_stats": per_file_rules,
|
||||
"errors": errors, "deviations": deviations})
|
||||
print("---- %s (table=%s)" % (path, table))
|
||||
print(" root_keys_ok=%s" % root_keys_ok) # QC #4:打印显式计算的布尔值
|
||||
@ -389,6 +448,10 @@ def main(argv=None):
|
||||
if not errors:
|
||||
print(" OK: 符合 database-table-definition-spec(偏离 %d 条已登记)" % len(deviations))
|
||||
print("=" * 60)
|
||||
# QC #3:逐规则计数(不依赖中间文件即可复核「真·非抽象类型仍被拦截、抽象类型未放水」)
|
||||
for tag, cnt in sorted(rule_counts.items(), key=lambda kv: (-kv[1], kv[0])):
|
||||
print("RULE_STATS: %s=%d" % (tag, cnt))
|
||||
print("RULE_STATS_TOTAL: errors=%d files=%d" % (total_errors, total_files))
|
||||
print("files=%d errors=%d registered_deviations=%d" % (total_files, total_errors, total_deviations))
|
||||
print("RESULT: %s" % ("PASS" if total_errors == 0 else "FAIL"))
|
||||
if args.json_report:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user