mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-11-03 22:04:57 +08:00
Merge pull request #7050 from DDSRem-Dev/codex-comment-migration
docs: 统一全项目中文注释和结构化契约说明
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
"""检测关键 async 路径中新引入的直接阻塞调用。"""
|
||||
"""
|
||||
检测关键 async 路径中新引入的直接阻塞调用。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -15,6 +17,7 @@ DEFAULT_BASELINE = PROJECT_ROOT / "tests/fixtures/architecture/async-blocking-ba
|
||||
SCAN_ROOTS = ("app",)
|
||||
EXCLUDED_ROOTS = ("app/plugins",)
|
||||
|
||||
# RequestUtils 中直接执行同步 HTTP 请求或管理同步响应的方法。
|
||||
_SYNC_HTTP_METHODS = {
|
||||
"delete_res",
|
||||
"get",
|
||||
@@ -38,6 +41,7 @@ _REQUESTS_METHODS = {
|
||||
"put",
|
||||
"request",
|
||||
}
|
||||
# 这些 pathlib 操作会访问文件系统,不能视为纯路径计算。
|
||||
_PATH_IO_METHODS = {
|
||||
"exists",
|
||||
"glob",
|
||||
@@ -73,6 +77,7 @@ _SUBPROCESS_METHODS = {
|
||||
"run",
|
||||
}
|
||||
_OS_IO_METHODS = {"listdir", "scandir", "walk"}
|
||||
# SystemConfigOper 的这些读取由内存快照提供,不计入同步数据库阻塞。
|
||||
_SYSTEM_CONFIG_MEMORY_READS = {
|
||||
"app.db.oper.SystemConfigOper.all",
|
||||
"app.db.oper.SystemConfigOper.get",
|
||||
@@ -83,14 +88,22 @@ _SYSTEM_CONFIG_MEMORY_READS = {
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class _Binding:
|
||||
"""描述由明确 import 或局部赋值建立的符号来源。"""
|
||||
"""
|
||||
描述由明确导入或局部赋值建立的符号来源。
|
||||
|
||||
:param family: 能力来源分类,区分同步或异步 HTTP、路径、数据库和普通模块;unknown 表示来源不明
|
||||
:param qualified_name: 已解析的完整模块、类或方法路径;来源不明时为空字符串
|
||||
:param kind: 符号形态,区分模块、类、实例、可调用对象、集合和未知来源;默认 module
|
||||
"""
|
||||
|
||||
family: str
|
||||
qualified_name: str
|
||||
kind: str = "module"
|
||||
|
||||
|
||||
# 显式未知绑定用于遮蔽外层同名导入,避免错误继承其能力来源。
|
||||
_UNKNOWN_BINDING = _Binding("unknown", "", "unknown")
|
||||
# 控制流合并时优先保留这些同步能力,防止分支遮蔽已知阻塞风险。
|
||||
_BLOCKING_FAMILIES = {
|
||||
"os",
|
||||
"requests",
|
||||
@@ -104,7 +117,9 @@ _BLOCKING_FAMILIES = {
|
||||
|
||||
|
||||
def _binding_for_qualified(qualified_name: str) -> _Binding:
|
||||
"""按稳定模块路径识别门禁关心的符号族。"""
|
||||
"""
|
||||
按稳定模块路径识别门禁关心的符号族。
|
||||
"""
|
||||
if qualified_name == "app.adapters.network.http.RequestUtils":
|
||||
return _Binding("sync_http", qualified_name, "class")
|
||||
if qualified_name == "app.adapters.network.http.AsyncRequestUtils":
|
||||
@@ -134,7 +149,9 @@ def _resolve_binding(
|
||||
expression: ast.expr,
|
||||
bindings: dict[str, _Binding],
|
||||
) -> _Binding | None:
|
||||
"""解析明确 import、构造、属性访问和简单路径派生。"""
|
||||
"""
|
||||
解析明确导入、构造、属性访问和简单路径派生。
|
||||
"""
|
||||
if isinstance(expression, ast.Name):
|
||||
return bindings.get(expression.id)
|
||||
if isinstance(expression, ast.Attribute):
|
||||
@@ -169,7 +186,9 @@ def _merge_binding_options(
|
||||
name: str,
|
||||
options: Sequence[_Binding | None],
|
||||
) -> _Binding | None:
|
||||
"""控制流合流时保留任一分支可能进入的同步阻塞类型。"""
|
||||
"""
|
||||
控制流合流时保留任一分支可能进入的同步阻塞类型。
|
||||
"""
|
||||
if options and all(option == options[0] for option in options):
|
||||
return options[0]
|
||||
blocking = sorted(
|
||||
@@ -190,7 +209,9 @@ def _merge_binding_options(
|
||||
def _merge_binding_states(
|
||||
states: Sequence[dict[str, _Binding]],
|
||||
) -> dict[str, _Binding]:
|
||||
"""合并互斥控制流的局部符号表。"""
|
||||
"""
|
||||
合并互斥控制流的局部符号表。
|
||||
"""
|
||||
names = set().union(*(state.keys() for state in states))
|
||||
merged: dict[str, _Binding] = {}
|
||||
for name in names:
|
||||
@@ -201,7 +222,9 @@ def _merge_binding_states(
|
||||
|
||||
|
||||
def _load_oper_methods(root: Path) -> dict[str, set[str]]:
|
||||
"""从 Oper 源码建立同步方法索引,避免按方法名猜测数据库调用。"""
|
||||
"""
|
||||
从 Oper 源码建立同步方法索引,避免按方法名猜测数据库调用。
|
||||
"""
|
||||
methods: dict[str, set[str]] = {}
|
||||
oper_root = root / "app/db/oper"
|
||||
if not oper_root.is_dir():
|
||||
@@ -225,18 +248,29 @@ def _load_oper_methods(root: Path) -> dict[str, set[str]]:
|
||||
|
||||
|
||||
class _ImportCollector(ast.NodeVisitor):
|
||||
"""收集模块级 import,作为每个 async 函数的初始符号表。"""
|
||||
"""
|
||||
收集模块级导入,作为每个 async 函数的初始符号表。
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""
|
||||
初始化模块级符号来源表。
|
||||
"""
|
||||
self.bindings: dict[str, _Binding] = {}
|
||||
|
||||
def visit_Import(self, node: ast.Import) -> None:
|
||||
"""
|
||||
记录模块导入及别名对应的完整来源。
|
||||
"""
|
||||
for alias in node.names:
|
||||
local_name = alias.asname or alias.name.split(".", 1)[0]
|
||||
qualified_name = alias.name if alias.asname else local_name
|
||||
self.bindings[local_name] = _binding_for_qualified(qualified_name)
|
||||
|
||||
def visit_ImportFrom(self, node: ast.ImportFrom) -> None:
|
||||
"""
|
||||
记录绝对导入的符号来源,跳过相对导入和通配导入。
|
||||
"""
|
||||
if node.level or not node.module:
|
||||
return
|
||||
for alias in node.names:
|
||||
@@ -247,6 +281,9 @@ class _ImportCollector(ast.NodeVisitor):
|
||||
self.bindings[local_name] = _binding_for_qualified(qualified_name)
|
||||
|
||||
def _bind_target(self, target: ast.expr, binding: _Binding | None) -> None:
|
||||
"""
|
||||
传播简单赋值来源;解包目标标记为未知,避免猜测各元素类型。
|
||||
"""
|
||||
if isinstance(target, ast.Name):
|
||||
self.bindings[target.id] = binding or _UNKNOWN_BINDING
|
||||
elif isinstance(target, (ast.Tuple, ast.List)):
|
||||
@@ -254,26 +291,43 @@ class _ImportCollector(ast.NodeVisitor):
|
||||
self._bind_target(element, None)
|
||||
|
||||
def visit_Assign(self, node: ast.Assign) -> None:
|
||||
"""
|
||||
传播模块级简单别名,并清除无法证明来源的旧绑定。
|
||||
"""
|
||||
binding = _resolve_binding(node.value, self.bindings)
|
||||
for target in node.targets:
|
||||
self._bind_target(target, binding)
|
||||
|
||||
def visit_AnnAssign(self, node: ast.AnnAssign) -> None:
|
||||
"""
|
||||
按赋值右值传播模块级来源;没有右值时标记为未知。
|
||||
"""
|
||||
binding = _resolve_binding(node.value, self.bindings) if node.value else None
|
||||
self._bind_target(node.target, binding)
|
||||
|
||||
def visit_FunctionDef(self, node: ast.FunctionDef) -> None:
|
||||
"""
|
||||
跳过函数体,避免把局部导入泄漏到模块符号表。
|
||||
"""
|
||||
return
|
||||
|
||||
def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> None:
|
||||
"""
|
||||
跳过异步函数体,保持模块级导入收集边界。
|
||||
"""
|
||||
return
|
||||
|
||||
def visit_ClassDef(self, node: ast.ClassDef) -> None:
|
||||
"""
|
||||
跳过类体,避免将类内绑定当作模块全局来源。
|
||||
"""
|
||||
return
|
||||
|
||||
|
||||
class _AsyncCallVisitor(ast.NodeVisitor):
|
||||
"""用局部符号传播识别一个 async 函数直接执行的阻塞调用。"""
|
||||
"""
|
||||
用局部符号传播识别一个 async 函数直接执行的阻塞调用。
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -283,6 +337,14 @@ class _AsyncCallVisitor(ast.NodeVisitor):
|
||||
*,
|
||||
record_calls: bool,
|
||||
) -> None:
|
||||
"""
|
||||
复制可见符号来源并绑定参数,为单个函数建立调用统计。
|
||||
|
||||
:param function: 本次分析的函数定义
|
||||
:param module_bindings: 定义处可见的外层符号来源,不原地修改
|
||||
:param oper_methods: Oper 完整类名到同步方法名集合的索引
|
||||
:param record_calls: 是否统计当前函数中的阻塞调用;关闭时仍追踪嵌套函数
|
||||
"""
|
||||
self.calls: Counter[str] = Counter()
|
||||
self.nested_functions: list[
|
||||
tuple[ast.FunctionDef | ast.AsyncFunctionDef, dict[str, _Binding]]
|
||||
@@ -296,6 +358,9 @@ class _AsyncCallVisitor(ast.NodeVisitor):
|
||||
self,
|
||||
function: ast.FunctionDef | ast.AsyncFunctionDef,
|
||||
) -> None:
|
||||
"""
|
||||
用参数遮蔽外层同名绑定,并从显式注解恢复已知能力来源。
|
||||
"""
|
||||
arguments = (
|
||||
*function.args.posonlyargs,
|
||||
*function.args.args,
|
||||
@@ -309,6 +374,11 @@ class _AsyncCallVisitor(ast.NodeVisitor):
|
||||
self._bindings[argument.arg] = _UNKNOWN_BINDING
|
||||
|
||||
def _annotation_binding(self, annotation: ast.expr | None) -> _Binding | None:
|
||||
"""
|
||||
从联合、可选和容器注解中解析实例或集合元素来源。
|
||||
|
||||
:return: 可证明的实例或集合绑定;未识别时返回 None
|
||||
"""
|
||||
if annotation is None:
|
||||
return None
|
||||
if isinstance(annotation, ast.Subscript):
|
||||
@@ -345,6 +415,9 @@ class _AsyncCallVisitor(ast.NodeVisitor):
|
||||
|
||||
@staticmethod
|
||||
def _call_label(binding: _Binding) -> str:
|
||||
"""
|
||||
把完整调用路径归一为基线使用的稳定名称。
|
||||
"""
|
||||
parts = binding.qualified_name.split(".")
|
||||
if binding.family == "sync_oper" and len(parts) >= 2:
|
||||
return ".".join(parts[-2:])
|
||||
@@ -357,6 +430,11 @@ class _AsyncCallVisitor(ast.NodeVisitor):
|
||||
return binding.qualified_name
|
||||
|
||||
def _record_call(self, node: ast.Call, binding: _Binding | None) -> None:
|
||||
"""
|
||||
仅在启用统计且来源命中同步能力规则时累计调用次数。
|
||||
|
||||
未被局部符号遮蔽的 open 按内置阻塞调用处理;系统配置内存读取不计入数据库阻塞。
|
||||
"""
|
||||
if not self._record_calls:
|
||||
return
|
||||
if not binding:
|
||||
@@ -389,6 +467,9 @@ class _AsyncCallVisitor(ast.NodeVisitor):
|
||||
self.calls[self._call_label(binding)] += 1
|
||||
|
||||
def _bind_target(self, target: ast.expr, binding: _Binding | None) -> None:
|
||||
"""
|
||||
传播简单目标的已知来源;解包赋值将各目标标记为未知。
|
||||
"""
|
||||
if isinstance(target, ast.Name):
|
||||
self._bindings[target.id] = binding or _UNKNOWN_BINDING
|
||||
elif isinstance(target, (ast.Tuple, ast.List)):
|
||||
@@ -396,22 +477,34 @@ class _AsyncCallVisitor(ast.NodeVisitor):
|
||||
self._bind_target(element, None)
|
||||
|
||||
def visit_Import(self, node: ast.Import) -> None:
|
||||
"""
|
||||
把函数内的模块导入加入当前符号表。
|
||||
"""
|
||||
collector = _ImportCollector()
|
||||
collector.visit_Import(node)
|
||||
self._bindings.update(collector.bindings)
|
||||
|
||||
def visit_ImportFrom(self, node: ast.ImportFrom) -> None:
|
||||
"""
|
||||
把函数内可解析的绝对符号导入加入当前符号表。
|
||||
"""
|
||||
collector = _ImportCollector()
|
||||
collector.visit_ImportFrom(node)
|
||||
self._bindings.update(collector.bindings)
|
||||
|
||||
def visit_Assign(self, node: ast.Assign) -> None:
|
||||
"""
|
||||
先统计右值调用,再将可证明来源传播给赋值目标。
|
||||
"""
|
||||
self.visit(node.value)
|
||||
binding = self._resolve(node.value)
|
||||
for target in node.targets:
|
||||
self._bind_target(target, binding)
|
||||
|
||||
def visit_AnnAssign(self, node: ast.AnnAssign) -> None:
|
||||
"""
|
||||
统计右值调用,并依次使用右值来源和类型注解绑定目标。
|
||||
"""
|
||||
if node.value:
|
||||
self.visit(node.value)
|
||||
binding = self._resolve(node.value) if node.value else None
|
||||
@@ -419,6 +512,9 @@ class _AsyncCallVisitor(ast.NodeVisitor):
|
||||
self._bind_target(node.target, binding)
|
||||
|
||||
def visit_AugAssign(self, node: ast.AugAssign) -> None:
|
||||
"""
|
||||
统计增量赋值右值,并使目标的旧来源失效。
|
||||
"""
|
||||
self.visit(node.value)
|
||||
self._bind_target(node.target, None)
|
||||
|
||||
@@ -427,6 +523,11 @@ class _AsyncCallVisitor(ast.NodeVisitor):
|
||||
statements: Sequence[ast.stmt],
|
||||
initial: dict[str, _Binding],
|
||||
) -> dict[str, _Binding]:
|
||||
"""
|
||||
在指定符号副本中分析分支,并恢复调用方的符号表。
|
||||
|
||||
:return: 分支执行后的来源映射;调用统计仍累计在当前访问器中
|
||||
"""
|
||||
saved = self._bindings
|
||||
self._bindings = dict(initial)
|
||||
for statement in statements:
|
||||
@@ -436,6 +537,9 @@ class _AsyncCallVisitor(ast.NodeVisitor):
|
||||
return result
|
||||
|
||||
def visit_If(self, node: ast.If) -> None:
|
||||
"""
|
||||
分别分析互斥分支并合并来源,保留任一路径可能存在的阻塞能力。
|
||||
"""
|
||||
self.visit(node.test)
|
||||
initial = dict(self._bindings)
|
||||
body = self._visit_branch(node.body, initial)
|
||||
@@ -443,6 +547,9 @@ class _AsyncCallVisitor(ast.NodeVisitor):
|
||||
self._bindings = _merge_binding_states((body, alternate))
|
||||
|
||||
def visit_For(self, node: ast.For | ast.AsyncFor) -> None:
|
||||
"""
|
||||
从集合来源推断循环元素,并合并零次执行与循环完成路径。
|
||||
"""
|
||||
self.visit(node.iter)
|
||||
initial = dict(self._bindings)
|
||||
collection = self._resolve(node.iter)
|
||||
@@ -459,6 +566,9 @@ class _AsyncCallVisitor(ast.NodeVisitor):
|
||||
self._bindings = _merge_binding_states((initial, completed))
|
||||
|
||||
def visit_AsyncFor(self, node: ast.AsyncFor) -> None:
|
||||
"""
|
||||
按相同的元素传播和零次执行规则分析异步循环。
|
||||
"""
|
||||
self.visit_For(node)
|
||||
|
||||
def _visit_comprehension(
|
||||
@@ -466,6 +576,9 @@ class _AsyncCallVisitor(ast.NodeVisitor):
|
||||
generators: Sequence[ast.comprehension],
|
||||
outputs: Sequence[ast.expr],
|
||||
) -> None:
|
||||
"""
|
||||
在独立符号副本中分析推导式的迭代、条件和输出表达式。
|
||||
"""
|
||||
saved = self._bindings
|
||||
self._bindings = dict(saved)
|
||||
for generator in generators:
|
||||
@@ -484,22 +597,40 @@ class _AsyncCallVisitor(ast.NodeVisitor):
|
||||
self._bindings = saved
|
||||
|
||||
def visit_ListComp(self, node: ast.ListComp) -> None:
|
||||
"""
|
||||
隔离列表推导式的目标绑定,并统计其表达式中的阻塞调用。
|
||||
"""
|
||||
self._visit_comprehension(node.generators, (node.elt,))
|
||||
|
||||
def visit_SetComp(self, node: ast.SetComp) -> None:
|
||||
"""
|
||||
隔离集合推导式的目标绑定,并统计其表达式中的阻塞调用。
|
||||
"""
|
||||
self._visit_comprehension(node.generators, (node.elt,))
|
||||
|
||||
def visit_GeneratorExp(self, node: ast.GeneratorExp) -> None:
|
||||
"""
|
||||
在独立符号表中扫描生成器表达式,记录其中可证明的阻塞调用。
|
||||
"""
|
||||
self._visit_comprehension(node.generators, (node.elt,))
|
||||
|
||||
def visit_DictComp(self, node: ast.DictComp) -> None:
|
||||
"""
|
||||
隔离字典推导式的目标绑定,同时扫描键和值表达式。
|
||||
"""
|
||||
self._visit_comprehension(node.generators, (node.key, node.value))
|
||||
|
||||
def visit_NamedExpr(self, node: ast.NamedExpr) -> None:
|
||||
"""
|
||||
统计赋值表达式的右值,并传播其来源到当前作用域。
|
||||
"""
|
||||
self.visit(node.value)
|
||||
self._bind_target(node.target, self._resolve(node.value))
|
||||
|
||||
def visit_Call(self, node: ast.Call) -> None:
|
||||
"""
|
||||
记录已知阻塞调用,并继续检查参数和立即调用的匿名函数体。
|
||||
"""
|
||||
binding = self._resolve(node.func)
|
||||
self._record_call(node, binding)
|
||||
if isinstance(node.func, ast.Lambda):
|
||||
@@ -516,6 +647,9 @@ class _AsyncCallVisitor(ast.NodeVisitor):
|
||||
self,
|
||||
node: ast.FunctionDef | ast.AsyncFunctionDef,
|
||||
) -> None:
|
||||
"""
|
||||
扫描函数定义时执行的装饰器和默认参数,不进入函数体。
|
||||
"""
|
||||
for decorator in node.decorator_list:
|
||||
self.visit(decorator)
|
||||
for default in node.args.defaults:
|
||||
@@ -528,17 +662,29 @@ class _AsyncCallVisitor(ast.NodeVisitor):
|
||||
self,
|
||||
node: ast.FunctionDef | ast.AsyncFunctionDef,
|
||||
) -> None:
|
||||
"""
|
||||
记录嵌套函数及定义处的来源快照,交由独立分析统计函数体。
|
||||
"""
|
||||
self._visit_definition_expressions(node)
|
||||
self.nested_functions.append((node, dict(self._bindings)))
|
||||
self._bindings[node.name] = _UNKNOWN_BINDING
|
||||
|
||||
def visit_FunctionDef(self, node: ast.FunctionDef) -> None:
|
||||
"""
|
||||
扫描定义期表达式,并将同步嵌套函数加入后续分析队列。
|
||||
"""
|
||||
self._visit_nested_function(node)
|
||||
|
||||
def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> None:
|
||||
"""
|
||||
扫描定义期表达式,并将异步嵌套函数加入后续分析队列。
|
||||
"""
|
||||
self._visit_nested_function(node)
|
||||
|
||||
def _visit_lambda_defaults(self, node: ast.Lambda) -> None:
|
||||
"""
|
||||
统计匿名函数创建时执行的默认参数表达式。
|
||||
"""
|
||||
for default in node.args.defaults:
|
||||
self.visit(default)
|
||||
for keyword_default in node.args.kw_defaults:
|
||||
@@ -546,13 +692,18 @@ class _AsyncCallVisitor(ast.NodeVisitor):
|
||||
self.visit(keyword_default)
|
||||
|
||||
def visit_Lambda(self, node: ast.Lambda) -> None:
|
||||
"""
|
||||
仅扫描匿名函数默认参数,函数体由直接调用路径另行访问。
|
||||
"""
|
||||
self._visit_lambda_defaults(node)
|
||||
|
||||
|
||||
def _root_functions(
|
||||
tree: ast.Module,
|
||||
) -> Iterator[tuple[str, ast.AsyncFunctionDef]]:
|
||||
"""产出模块顶层和类直接拥有的 async 入口。"""
|
||||
"""
|
||||
产出模块顶层和类直接拥有的 async 入口。
|
||||
"""
|
||||
for node in tree.body:
|
||||
if isinstance(node, ast.AsyncFunctionDef):
|
||||
yield node.name, node
|
||||
@@ -563,7 +714,9 @@ def _root_functions(
|
||||
|
||||
|
||||
def _is_excluded_path(root: Path, path: Path) -> bool:
|
||||
"""判断源码路径是否位于明确排除的插件目录。"""
|
||||
"""
|
||||
判断源码路径是否位于明确排除的插件目录。
|
||||
"""
|
||||
relative = path.relative_to(root).as_posix()
|
||||
return any(
|
||||
relative == excluded or relative.startswith(f"{excluded}/")
|
||||
@@ -572,7 +725,9 @@ def _is_excluded_path(root: Path, path: Path) -> bool:
|
||||
|
||||
|
||||
def _scan_paths(root: Path, scan_roots: Sequence[str | Path]) -> Iterator[Path]:
|
||||
"""按稳定顺序产出存在且不属于插件目录的扫描目标。"""
|
||||
"""
|
||||
按稳定顺序产出存在且不属于插件目录的扫描目标。
|
||||
"""
|
||||
for scan_root in scan_roots:
|
||||
target = root / scan_root
|
||||
if target.is_file():
|
||||
@@ -590,7 +745,13 @@ def collect_async_blocking(
|
||||
root: Path = PROJECT_ROOT,
|
||||
scan_roots: Sequence[str | Path] = SCAN_ROOTS,
|
||||
) -> dict[str, int]:
|
||||
"""扫描关键目录并以文件、函数、调用名聚合存量次数。"""
|
||||
"""
|
||||
扫描关键目录并以文件、函数、调用名聚合存量次数。
|
||||
|
||||
:param root: 仓库根目录,同时用于读取 Oper 同步方法声明
|
||||
:param scan_roots: 相对于仓库根目录的文件或目录;插件路径始终排除
|
||||
:return: 文件、函数限定名与调用名组成的键到静态调用次数的映射
|
||||
"""
|
||||
debt: Counter[str] = Counter()
|
||||
oper_methods = _load_oper_methods(root)
|
||||
for path in _scan_paths(root, scan_roots):
|
||||
@@ -639,7 +800,13 @@ def collect_async_blocking(
|
||||
def compare_async_blocking(
|
||||
baseline: dict[str, int], current: dict[str, int]
|
||||
) -> list[str]:
|
||||
"""允许存量减少或删除,拒绝新增阻塞调用和调用次数增长。"""
|
||||
"""
|
||||
允许存量减少或删除,拒绝新增阻塞调用和调用次数增长。
|
||||
|
||||
:param baseline: 已审查的阻塞调用次数上限
|
||||
:param current: 当前源码收集出的阻塞调用次数
|
||||
:return: 新增或增长条目的诊断列表;下降不构成问题
|
||||
"""
|
||||
problems = []
|
||||
for entry, count in current.items():
|
||||
previous = baseline.get(entry)
|
||||
@@ -651,7 +818,9 @@ def compare_async_blocking(
|
||||
|
||||
|
||||
def main() -> int:
|
||||
"""执行 async 阻塞 baseline check 或显式 write。"""
|
||||
"""
|
||||
执行 async 阻塞基线检查或显式写入。
|
||||
"""
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--write", action="store_true", help="写入当前阻塞债务")
|
||||
parser.add_argument("--baseline", type=Path, default=DEFAULT_BASELINE)
|
||||
|
||||
Reference in New Issue
Block a user