fix(plot): 修复复审问题(2 P2 + 1 P3)

- P2 复杂表达式绕过异常回退:解析与渲染共同纳入局部异常回退;
  AST 深度/节点数上限拦截 RecursionError
- P2 极端有限范围生成 nan SVG:校验坐标跨度有限且 >0,回退安全范围;
  _polyline 拒绝非有限像素坐标
- P3 更新接口契约文档:function-plot 静态 SVG 已实现

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
yxx
2026-09-05 22:49:01 +08:00
co-authored by Claude Code
parent 50d7fb4c7d
commit b87f94551b
5 changed files with 120 additions and 18 deletions
+9 -8
View File
@@ -203,16 +203,17 @@ class HtmlExporter:
return f"函数图像:{diag.message}{loc}"
def _render_function_plot(self, node: DocumentNode, warnings: list[str]) -> str:
# 解析 fenced 源码:有合法 plot 且无 error → 内嵌静态 SVG;否则回退占位并转诊断
parsed = parse_source(node.text)
for diag in parsed.diagnostics:
warnings.append(self._format_plot_diagnostic(diag))
if parsed.plot is None:
return f'<pre class="function-plot">{html.escape(node.text)}</pre>'
# 解析与渲染共同纳入局部异常回退:单个图像失败只回退占位 + warning,
# 绝不阻断整篇导出(含复杂表达式触发的 RecursionError 等异常)。
try:
parsed = parse_source(node.text)
for diag in parsed.diagnostics:
warnings.append(self._format_plot_diagnostic(diag))
if parsed.plot is None:
return f'<pre class="function-plot">{html.escape(node.text)}</pre>'
rendered = render_svg(parsed.plot)
except Exception as exc: # 渲染异常回退占位,绝不阻断整篇导出
warnings.append(f"函数图像:渲染失败,已回退占位({exc}")
except Exception as exc:
warnings.append(f"函数图像:解析或渲染失败,已回退占位({exc}")
return f'<pre class="function-plot">{html.escape(node.text)}</pre>'
warnings.extend(rendered.warnings)
return f'<figure class="function-plot">{rendered.content}</figure>'