我为 Sphinx 编写了一个扩展来读取代码覆盖率文件,并将它们以表格形式呈现在 Sphinx 生成的 HTML 文档中。
目前该表只有一个标题行,例如3 列用于语句相关值,4 列用于分支相关数据。我想创建一个 2 行表标题,以便对多列进行分组。
在纯 HTML 中,可以通过添加
colspan=3
来完成。但是如何用 docutils 解决这个问题呢?
完整来源可以在这里找到:https://github.com/pyTooling/sphinx-reports/blob/main/sphinx_reports/CodeCoverage.py#L169
有趣的代码是这样的:
def _PrepareTable(self, columns: Dict[str, int], identifier: str, classes: List[str]) -> Tuple[nodes.table, nodes.tgroup]:
table = nodes.table("", identifier=identifier, classes=classes)
tableGroup = nodes.tgroup(cols=(len(columns)))
table += tableGroup
tableRow = nodes.row()
for columnTitle, width in columns.items():
tableGroup += nodes.colspec(colwidth=width)
tableRow += nodes.entry("", nodes.paragraph(text=columnTitle))
tableGroup += nodes.thead("", tableRow)
return table, tableGroup
def _GenerateCoverageTable(self) -> nodes.table:
# Create a table and table header with 5 columns
table, tableGroup = self._PrepareTable(
identifier=self._packageID,
columns={
"Module": 500,
"Total Statements": 100,
"Excluded Statements": 100,
"Covered Statements": 100,
"Missing Statements": 100,
"Total Branches": 100,
"Covered Branches": 100,
"Partial Branches": 100,
"Missing Branches": 100,
"Coverage in %": 100
},
classes=["report-doccov-table"]
)
tableBody = nodes.tbody()
tableGroup += tableBody
不熟悉 docutils API,但您是否考虑过使用具有层序遍历的树?比如:
from collections import deque
class _Column:
def __init__(self, title: str, width: Optional[int] = None, children: Optional[List['_Column']] = None):
# Only leaf nodes can have widths
assert width is None or children is None
self.width = width
self.children = children
self._width_computed = None
def getWidth(self) -> int:
if self._width_computed is None:
self._width_computed = self.width or sum([c.getWidth() for c in self.children or []])
return self._width_computed
def countLeaves(self) -> int:
if self.children is None:
return 1
return sum([c.countLeaves() for c in self.children or []])
...
def _PrepareTable(self, columns: List[_Column], identifier: str, classes: List[str]) -> Tuple[nodes.table, nodes.tgroup]:
table = nodes.table("", identifier=identifier, classes=classes)
tableGroup = nodes.tgroup(cols=(sum([col.countLeaves() for col in columns])))
table += tableGroup
tableRow = nodes.row()
# level-order traversal to get widths.
current_level = 0
queue = deque([c, 0 for c in columns])
for col, level in queue:
if current_level != level:
current_level = level
tableGroup += nodes.thead("", tableRow)
tableRow = nodes.row()
# You can use level how you like.
tableGroup += nodes.colspec(colwidth=col.getWidth())
tableRow += nodes.entry("", nodes.paragraph(text=col.title))
for child_col in col.children or []:
queue.appendleft((child_col, level + 1))
# Add the last level.
tableGroup += nodes.thead("", tableRow)
return table, tableGroup
def _GenerateCoverageTable(self) -> nodes.table:
# Create a table and table header with 5 columns
table, tableGroup = self._PrepareTable(
identifier=self._packageID,
columns=[
_Column(
title="Module",
width=500,
),
_Column(
title="Statements",
children=[
_Column(
title="Total Statements",
width=100,
),
_Column(
title="Excluded Statements",
width=100,
),
_Column(
title="Covered Statements",
width=100,
),
_Column(
title="Missing Statements",
width=100,
),
]
),
_Column(
title="Branches",
children=[
_Column(
title="Total Branches",
width=100,
),
_Column(
title="Covered Branches",
width=100,
),
_Column(
title="Partial Branches",
width=100,
),
_Column(
title="Missing Branches",
width=100,
),
]
),
_Column(
title="Coverage in %",
width=100,
),
],
classes=["report-doccov-table"]
)
tableGroup += nodes.tbody()