| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960 |
- # -*- coding: utf-8 -*-
- r"""Markdown 表格渲染(**零可选依赖**)。
- 为什么需要它(2026-09-19 实逮,一次真实事故):
- `src/windcms/report.py` 用 `DataFrame.to_markdown()` 渲染"L4 过闸谱线"表 —— 而 pandas 的
- `to_markdown` 依赖**可选包 `tabulate`**,本交付包没有它。此前 `model_run_l6.parquet` 是空的
- (六层链那两步没实现),`len(l6)` 为 0 走的是 `'无'` 分支 ⇒ **没人踩到**;2026-09-19 我把
- `model_run` 按口径重建、L6 真的有 14 行之后,重算链在 `report` 步当场炸:
- ImportError: `Import tabulate` failed. Use pip or conda to install the tabulate package.
- at src/windcms/report.py:504 l6[cols].to_markdown(index=False)
- 后果不是"少一张表",而是**整条重算链停在第 4 步**:后面的 ⑥重启 / ⑦本体(`ontology/objects.json` 等)
- 全不执行 ⇒ 页面上"决策链/本体/报告"一片空白。教训与 memory `optional-dep-in-hot-path` 同型:
- **热路径不许依赖可选包**,何况交付包是离线整包(不能 pip)。
- 这里用纯 Python 渲染同样的 Markdown 表(列宽按显示宽度补空格,中文按 2 列宽算)。
- """
- from __future__ import annotations
- def _w(s) -> int:
- """显示宽度: CJK 与全角标点算 2, 其余算 1(对齐用, 不追求像素级)。"""
- n = 0
- for ch in str(s):
- n += 2 if ('\u4e00' <= ch <= '\u9fff' or '\u3000' <= ch <= '\u303f'
- or '\uff00' <= ch <= '\uffef') else 1
- return n
- def to_md(rows: list[list], header: list[str] | None = None) -> str:
- """[[单元格…], …] → Markdown 表(首行可作表头)。空数据返回 ''。"""
- if not rows:
- return ''
- esc = lambda x: ('' if x is None else str(x)).replace('|', '\\|') # 竖线要转义, 否则表被拆列
- body = [[esc(c) for c in r] for r in rows]
- if header:
- cols = [str(h) for h in header]
- body = [cols] + body
- ncol = max(len(r) for r in body)
- body = [r + [''] * (ncol - len(r)) for r in body]
- width = [max(_w(r[i]) for r in body) for i in range(ncol)]
- out = []
- for i, r in enumerate(body):
- out.append('| ' + ' | '.join(str(c) + ' ' * (width[j] - _w(c)) for j, c in enumerate(r)) + ' |')
- if i == 0:
- out.append('|' + '|'.join('-' * (width[j] + 2) for j in range(ncol)) + '|')
- return '\n'.join(out)
- def df_to_md(df, cols=None, index: bool = False) -> str:
- """DataFrame → Markdown 表(`to_markdown(index=False)` 的零依赖替代)。"""
- if df is None or len(df) == 0:
- return ''
- d = df[cols] if cols else df
- head = ([d.index.name or ''] if index else []) + [str(c) for c in d.columns]
- rows = []
- for idx, r in zip(d.index, d.itertuples(index=False)):
- rows.append(([idx] if index else []) + list(r))
- return to_md(rows, header=head)
|