1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
|
# ==================================================================================================================== #
# _____ _ _ ____ _ _ #
# _ __ _ |_ _|__ ___ | (_)_ __ __ _ / ___| _ __ | |__ (_)_ __ __ __ #
# | '_ \| | | || |/ _ \ / _ \| | | '_ \ / _` | \___ \| '_ \| '_ \| | '_ \\ \/ / #
# | |_) | |_| || | (_) | (_) | | | | | | (_| |_ ___) | |_) | | | | | | | |> < #
# | .__/ \__, ||_|\___/ \___/|_|_|_| |_|\__, (_)____/| .__/|_| |_|_|_| |_/_/\_\ #
# |_| |___/ |___/ |_| #
# ==================================================================================================================== #
# Authors: #
# Patrick Lehmann #
# #
# License: #
# ==================================================================================================================== #
# Copyright 2026-2026 Patrick Lehmann - Bötzingen, Germany #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. #
# You may obtain a copy of the License at #
# #
# http://www.apache.org/licenses/LICENSE-2.0 #
# #
# Unless required by applicable law or agreed to in writing, software #
# distributed under the License is distributed on an "AS IS" BASIS, #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
# See the License for the specific language governing permissions and #
# limitations under the License. #
# #
# SPDX-License-Identifier: Apache-2.0 #
# ==================================================================================================================== #
#
"""
The docutils nodes pyTooling.Sphinx adds, and what registering one with Sphinx takes.
A node is a class here; the functions writing it are in a module per output format - :mod:`~pyTooling.Sphinx.HTML`
and :mod:`~pyTooling.Sphinx.LaTeX` - and :data:`~pyTooling.Sphinx.NODES` pairs both for :func:`~pyTooling.Sphinx.setup`.
A node without visitors for a format is written by the visitors of its base-class there.
"""
from typing import Any, Callable, NotRequired, TypedDict
from docutils import nodes
from pyTooling.Decorators import export
__all__ = ["visitFunc", "departFunc", "COVERAGE_STATES", "DOCUMENTATION_STATES"]
#: The coverage states of a listing's lines, as the listing's legend names them, by state; ``""`` for a line, which
#: isn't executable.
COVERAGE_STATES = {
"covered": "covered",
"partial": "partial",
"uncovered": "uncovered",
"excluded": "excluded",
"": "not executable",
}
#: The documentation states of a condensed listing's lines, as the listing's legend names them, by state; ``""`` for a
#: line, whose documentation isn't counted.
DOCUMENTATION_STATES = {
"documented": "documented",
"undocumented": "undocumented",
"ignored": "ignored",
"excluded": "excluded",
"": "not counted",
}
type visitFunc = Callable[[Any, Any], Any]
"""A function writing the start of a node: called with the translator and the node."""
type departFunc = Callable[[Any, Any], Any]
"""A function writing the end of a node: called with the translator and the node."""
@export
class RegisteredNode(TypedDict):
"""An entry of :data:`~pyTooling.Sphinx.NODES`: a node class, and its visitors per output format."""
name: str #: Name of the node.
node: type[nodes.Element] #: The node class to register.
html: tuple[visitFunc, departFunc] #: Visit and depart function writing the node in HTML.
latex: NotRequired[tuple[visitFunc, departFunc]] #: Visit and depart function writing the node in LaTeX, if any.
@export
class TreeItem(nodes.list_item):
"""
An entry of a tree: a list item, which HTML draws around a ``<details>`` element if the entry has children.
Attribute ``expanded`` states whether the entry is expanded initially, and is ``None`` if it has no children.
Attribute ``columnWidth`` holds the width of the column of the entries' texts as a CSS length, measured from the
tree's left edge, if the entry is a root of a tree with descriptions; else ``None``.
"""
@export
class TreeLabel(nodes.paragraph):
"""
An entry's text: a paragraph, which HTML draws as the ``<summary>`` of the entry's ``<details>`` element.
Attribute ``icon`` holds the icon of the entry's kind, ``expandedIcon`` and ``collapsedIcon`` the icons showing
whether the entry is expanded. Both are ``None`` if the entry has no children. Attribute ``level`` holds the entry's
level, ``0`` for a root, and ``descriptionColumn`` whether the tree has descriptions: then HTML draws the entry as
a row of two columns, whether this entry has a description or not.
An entry's description follows its text, as a :class:`TreeSeparator` and a :class:`TreeDescription`.
"""
@export
class TreeSeparator(nodes.inline):
"""
The separator between an entry's text and its description: an en dash, which HTML skips.
HTML draws the description in a column of its own. Every other format - e.g. LaTeX - writes its base-class, so the
description follows the text in the same line: ``text – description``.
"""
@export
class TreeDescription(nodes.inline):
"""
An entry's description, which HTML draws in a column of its own: all descriptions of a tree start at the same
horizontal position, whatever the entry's level.
"""
@export
class TreeLegend(nodes.paragraph):
"""
A tree's legend: a paragraph of :class:`TreeLegendItem` nodes, which HTML draws as one line above or below the tree.
Formats drawing no icons in a tree don't get it: :class:`~pyTooling.Sphinx.Tree.RemoveTreeLegends` removes it.
"""
@export
class TreeLegendItem(nodes.inline):
"""
An item of a tree's legend: the text explaining an icon. Attribute ``icon`` holds the icon.
"""
@export
class CopyButton(nodes.inline):
"""
A button copying a text into the clipboard, which HTML draws as a ``<button>``; every other format writes nothing.
Attribute ``copy`` holds the text to copy, and ``label`` what the button does, for a screen reader and as a tooltip.
The node has no children, so a format writing its base-class - an inline element - writes nothing.
The script :file:`pyTooling.js` gives the button its function.
"""
@export
class Abbreviation(nodes.abbreviation):
"""
A reference to an abbreviation in its short form, which HTML draws with a box explaining it on hover.
Attributes ``short`` and ``long`` hold the abbreviation's forms, even if the node's text is a title the role was
written with; ``summary`` holds the summary of its description the box shows, or an empty string. The attribute
``explanation`` of the base-class is left unset: LaTeX and text would append it in parentheses, and an abbreviation
is written in the form its role asks for.
"""
@export
class Landscape(nodes.container):
"""
A container whose content LaTeX puts on landscape pages; HTML writes the content only.
A wide table is such content.
LaTeX's ``landscape`` environment needs package ``pdflscape``, which :func:`~pyTooling.Sphinx.setup` requests. The
report tables of domain ``report`` are what this node holds.
"""
@export
class CoverageListing(nodes.literal_block):
"""
A source file's code coverage: its lines, syntax highlighted, each marked by its coverage state.
Attribute ``lines`` holds a tuple per line: the line number, the state (``covered``, ``partial``, ``uncovered``,
``excluded``, or ``""`` for a line, which isn't executable), the hits or ``None``, the number of branches and of
taken branches, and the line's tokens as ``(CSS class, text)`` pairs - the short classes of Pygments' token types,
which Sphinx' Pygments stylesheet colors. Attribute ``anchors`` states whether a line gets the ID ``L<number>``.
Attribute ``path`` holds the file's path in the report, which the listing's header shows beside the legend of the
coverage states.
The node's text is the plain source, so a builder without visitors for this node writes it as a literal block.
"""
@export
class DocCoverageListing(nodes.literal_block):
"""
A Python module's documentation coverage: its condensed source, syntax highlighted, each line marked by its
documentation state.
The source is condensed to what is documented: a function's or class' decorators, its header and its doc-string,
fields and variables; a body is elided to one ``...`` line.
Attribute ``lines`` holds a tuple per line: the line number - ``None`` for an elided body's ``...`` -, the state
(``documented``, ``undocumented``, ``ignored``, ``excluded``, or ``""`` for a line, whose documentation isn't
counted), and the line's tokens as ``(CSS class, text)`` pairs - the short classes of Pygments' token types, which
Sphinx' Pygments stylesheet colors. Attribute ``path`` holds the module's path, which the listing's header shows
beside the legend of the documentation states.
The node's text is the condensed source, so a builder without visitors for this node writes it as a literal block.
"""
@export
class Emoji(nodes.inline):
"""
An emoji: its characters, which HTML draws with its shortcode as tooltip.
Attribute ``shortcode`` holds the emoji's GitHub shortcode without colons, e.g. ``tada``. LaTeX writes its base-class,
so the characters as they are - LuaLaTeX draws them with a Unicode emoji font, which e.g. :file:`pytooling.sty` of
the MiKTeX image maps them to.
"""
@export
class TaskListCheckbox(nodes.inline):
"""
The checkbox opening a task list item, which HTML draws as a disabled checkbox.
Attribute ``checked`` states whether the task is done. The node's text is a ballot box, with check if the task is
done, so a format writing its base-class - an inline element - writes that character. LaTeX writes a box symbol.
"""
@export
class ReportIndentation(nodes.inline):
"""
The name of an entry in a report table, indented by the entry's depth: HTML as a padding, LaTeX by a macro.
Attribute ``level`` holds the number of indentation steps, e.g. ``2`` for a testcase in a testsuite. A wrapped name
stays at its indentation. A format writing its base-class - an inline element - writes the name without indentation.
"""
@export
class LineBreak(nodes.inline):
"""
A line break inside a paragraph, which HTML writes as ``<br />`` and LaTeX as ``\\newline``.
The node's text is a newline, so a format writing its base-class - an inline element - writes a space.
"""
|