上一条 commit 702f274 归档 24 个自造中间件时,连带把 ns_wiring.py 一并归档,导致 rbac/appbase 的 dspy 端点报 TypeError: 'NoneType' object is not callable(模块函数未注入命名空间)。 ns_wiring 不属于鉴权中间件,它是「业务模块 dispatch + 只读基础模块 handler 补齐」接线:只在运行时 setattr env.<handler>,不装配 aiohttp 中间件、不改基础模块源码,因此恢复。 实测(184 个 dspy 语法扫描 + 端点扫描): 恢复前:19 个端点 500(list_path_roles / get_code / find_unauth_files 等) 恢复后:仅剩「无参 GET 调写端点」的假阳性,只读端点全部正常 ns_wiring.py 从 app/_archive_disabled_20261028_151505/ 原样恢复,未改动。
134 lines
4.8 KiB
Python
134 lines
4.8 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""宿主侧命名空间注入接线(T4 dspy 命名空间符号注入族)。
|
||
|
||
【为什么在宿主侧还有一层】ahserver 的 .dspy exec 命名空间由框架在每次请求构造,
|
||
模块 init() 的 `env.xxx = xxx` 只有在**宿主已调用 load_{模块}()** 时才生效;基础模块
|
||
(rbac / appbase / bricks)是 referenced_modules(只读,禁改源码),其端点若依赖
|
||
`env.<handler>` 而该符号未挂/为 None,表现就是 `TypeError: 'NoneType' object is not callable`
|
||
(R42 集群⑤)。宿主侧做两件确定性的事:
|
||
|
||
1) `install_module_shim(env)` —— 把业务模块的统一 dispatch 入口挂到 ServerEnv
|
||
(scense.ns_shim.install;scense_demo 复用同一入口,不重复实现)。
|
||
2) `backfill_base_handlers(env)` —— 对只读基础模块,用包内真实存在且可调用的函数
|
||
补齐 `env.<name>`(**只 setattr 运行时命名空间,不改基础模块源码**);
|
||
包内也不存在的符号不伪造,交由端点侧 ns-shim 给 `symbol-missing:<op>` 结构化 503。
|
||
|
||
接线点:应用入口 init() 在全部 load_*() 之后调用本模块 `wire_ns_shim(env)`,并把
|
||
`env.NS_SHIM_WIRING` 落一条自证字符串,供部署态核验。
|
||
"""
|
||
import importlib
|
||
|
||
NS_WIRING_VERSION = "t4-host-wiring-1"
|
||
|
||
# 只读基础模块的 handler 候选(包内存在才补;不存在不伪造)
|
||
BASE_HANDLER_CANDIDATES = (
|
||
("rbac", ("find_unauth_files", "list_path_roles", "list_roles", "list_permissions")),
|
||
("appbase", ("get_code", "get_appcodes_kv", "get_appcodes", "get_svgicon")),
|
||
("bricks", ("list_tables", "tableinfo", "channels")),
|
||
)
|
||
|
||
|
||
def _get(env, name):
|
||
try:
|
||
return getattr(env, name, None)
|
||
except Exception:
|
||
return None
|
||
|
||
|
||
def _set(env, name, value):
|
||
try:
|
||
setattr(env, name, value)
|
||
return True
|
||
except Exception:
|
||
pass
|
||
try:
|
||
env[name] = value
|
||
return True
|
||
except Exception:
|
||
return False
|
||
|
||
|
||
def install_module_shim(env):
|
||
"""业务模块统一 dispatch 入口(scense 域)。返回已挂载入口名清单。"""
|
||
installed = []
|
||
if _get(env, "dspy_ns_call") is not None:
|
||
installed.append("dspy_ns_call(pre-existing)")
|
||
try:
|
||
from scense.ns_shim import install as _install_scense
|
||
_install_scense(env)
|
||
installed.append("scense.ns_shim.install")
|
||
except Exception as exc: # 模块未装/未加载都不是致命错误,但要留痕
|
||
installed.append("scense.ns_shim.install:FAIL:%s" % exc)
|
||
return installed
|
||
|
||
|
||
def backfill_base_handlers(env):
|
||
"""只读基础模块 handler 补齐。返回 {module: [补齐符号]}。"""
|
||
report = {}
|
||
for modpath, names in BASE_HANDLER_CANDIDATES:
|
||
try:
|
||
mod = importlib.import_module(modpath)
|
||
except Exception:
|
||
report[modpath] = []
|
||
continue
|
||
fixed = []
|
||
for name in names:
|
||
fn = getattr(mod, name, None)
|
||
if fn is None or not callable(fn):
|
||
continue
|
||
if _get(env, name) is None:
|
||
if _set(env, name, fn):
|
||
fixed.append(name)
|
||
report[modpath] = fixed
|
||
return report
|
||
|
||
|
||
def matrix(env):
|
||
"""注入生效矩阵(部署态可经 /scense/api 侧或脚本读取)。"""
|
||
rows = []
|
||
try:
|
||
from scense.ns_shim import ns_shim_matrix
|
||
rows = ns_shim_matrix(env).get("rows", [])
|
||
except Exception:
|
||
rows = []
|
||
base = {}
|
||
for modpath, names in BASE_HANDLER_CANDIDATES:
|
||
base[modpath] = {
|
||
name: {
|
||
"env_has": _get(env, name) is not None,
|
||
"module_has": _module_has(modpath, name),
|
||
} for name in names
|
||
}
|
||
return {"version": NS_WIRING_VERSION, "ops": rows, "base_handlers": base}
|
||
|
||
|
||
def _module_has(modpath, name):
|
||
try:
|
||
mod = importlib.import_module(modpath)
|
||
except Exception:
|
||
return False
|
||
return callable(getattr(mod, name, None))
|
||
|
||
|
||
def wire_ns_shim(env):
|
||
"""入口接线:install + backfill + 自证。永不抛异常(启动期不能让接线阻断服务)。"""
|
||
out = {"version": NS_WIRING_VERSION, "installed": [], "backfilled": {}, "error": None}
|
||
try:
|
||
out["installed"] = install_module_shim(env)
|
||
except Exception as exc:
|
||
out["error"] = "install:" + str(exc)
|
||
try:
|
||
out["backfilled"] = backfill_base_handlers(env)
|
||
except Exception as exc:
|
||
out["error"] = (out["error"] or "") + "|backfill:" + str(exc)
|
||
summary = "; ".join(
|
||
["installed=" + ",".join(out["installed"])] +
|
||
["%s=%s" % (k, ",".join(v) or "-") for k, v in out["backfilled"].items()]
|
||
)
|
||
try:
|
||
env.NS_SHIM_WIRING = NS_WIRING_VERSION + " " + summary
|
||
env.ns_shim_matrix = lambda: matrix(env) # noqa: E731 部署态自检入口
|
||
except Exception:
|
||
pass
|
||
return out
|