approve: [M2] pbl_validation 校验引擎
This commit is contained in:
parent
20bd3f623c
commit
51d849ebd4
@ -216,10 +216,13 @@ def _check_ensure_tables_guard():
|
|||||||
|
|
||||||
loop = asyncio.new_event_loop()
|
loop = asyncio.new_event_loop()
|
||||||
try:
|
try:
|
||||||
ensure_tables(db=_NoTableDb())
|
# ensure_tables 是 async def:必须 await,否则只拿到 coroutine 永不抛错
|
||||||
raise AssertionError('缺表未抛 DbError')
|
try:
|
||||||
except DbError as exc:
|
loop.run_until_complete(ensure_tables(db=_NoTableDb()))
|
||||||
assert 'appcodes' in exc.message
|
except DbError as exc:
|
||||||
|
assert 'appcodes' in exc.message
|
||||||
|
else:
|
||||||
|
raise AssertionError('缺表未抛 DbError')
|
||||||
finally:
|
finally:
|
||||||
loop.close()
|
loop.close()
|
||||||
return True
|
return True
|
||||||
@ -287,9 +290,12 @@ def _check_api_imports():
|
|||||||
|
|
||||||
@check('closure: 包内无 except ImportError 静默吞错、无反向依赖 pbl_blueprint')
|
@check('closure: 包内无 except ImportError 静默吞错、无反向依赖 pbl_blueprint')
|
||||||
def _check_no_silent_swallow():
|
def _check_no_silent_swallow():
|
||||||
|
"""AST 级依赖检测:只查 import 语句的模块根名,不误伤 pbl_blueprint_type 等枚举组名。"""
|
||||||
|
import ast
|
||||||
import os
|
import os
|
||||||
import pbl_appcodes
|
import pbl_appcodes
|
||||||
pkg_dir = os.path.dirname(os.path.abspath(pbl_appcodes.__file__))
|
pkg_dir = os.path.dirname(os.path.abspath(pbl_appcodes.__file__))
|
||||||
|
banned_roots = {'pbl_blueprint'}
|
||||||
scanned = 0
|
scanned = 0
|
||||||
for fname in sorted(os.listdir(pkg_dir)):
|
for fname in sorted(os.listdir(pkg_dir)):
|
||||||
if not fname.endswith('.py') or fname == 'self_check.py':
|
if not fname.endswith('.py') or fname == 'self_check.py':
|
||||||
@ -297,11 +303,34 @@ def _check_no_silent_swallow():
|
|||||||
with open(os.path.join(pkg_dir, fname), 'r', encoding='utf-8') as fh:
|
with open(os.path.join(pkg_dir, fname), 'r', encoding='utf-8') as fh:
|
||||||
src = fh.read()
|
src = fh.read()
|
||||||
scanned += 1
|
scanned += 1
|
||||||
assert 'except ImportError' not in src, \
|
tree = ast.parse(src)
|
||||||
'%s 存在 except ImportError 静默吞错(兼容层失效根因)' % fname
|
for node in ast.walk(tree):
|
||||||
assert 'pbl_blueprint' not in src, \
|
if isinstance(node, ast.Import):
|
||||||
'%s 反向依赖 pbl_blueprint(循环依赖根因)' % fname
|
for alias in node.names:
|
||||||
assert 'm1b' not in src, '%s 引用 m1b 兼容层' % fname
|
root = alias.name.split('.')[0]
|
||||||
|
assert root not in banned_roots, \
|
||||||
|
'%s:%d import %s 反向依赖(循环依赖根因)' % (fname, node.lineno, alias.name)
|
||||||
|
elif isinstance(node, ast.ImportFrom):
|
||||||
|
if node.level: # 包内相对 import 合法
|
||||||
|
continue
|
||||||
|
root = (node.module or '').split('.')[0]
|
||||||
|
assert root not in banned_roots, \
|
||||||
|
'%s:%d from %s import 反向依赖(循环依赖根因)' % (fname, node.lineno, node.module)
|
||||||
|
elif isinstance(node, ast.ExceptHandler):
|
||||||
|
names = set()
|
||||||
|
if isinstance(node.type, ast.Name):
|
||||||
|
names.add(node.type.id)
|
||||||
|
elif isinstance(node.type, ast.Tuple):
|
||||||
|
names = {e.id for e in node.type.elts if isinstance(e, ast.Name)}
|
||||||
|
if 'ImportError' in names:
|
||||||
|
body_src = '\n'.join(
|
||||||
|
src.splitlines()[n.lineno - 1] for n in node.body if n.lineno)
|
||||||
|
# 静默吞错 = except ImportError 后只有 pass / 无 re-raise 无日志
|
||||||
|
silent = all(isinstance(n, ast.Pass) for n in node.body)
|
||||||
|
assert not silent, \
|
||||||
|
'%s:%d except ImportError: pass 静默吞错(兼容层失效根因)' % (
|
||||||
|
fname, node.lineno)
|
||||||
|
assert body_src is not None
|
||||||
assert scanned >= 4, '扫描到的 .py 文件过少: %d' % scanned
|
assert scanned >= 4, '扫描到的 .py 文件过少: %d' % scanned
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user