Coverage for pyTooling/Documentation/Sphinx/Roles.py: 81%
21 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-03 23:02 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-03 23:02 +0000
1# ==================================================================================================================== #
2# _____ _ _ ____ _ _ _ #
3# _ __ _ |_ _|__ ___ | (_)_ __ __ _ | _ \ ___ ___ _ _ _ __ ___ ___ _ __ | |_ __ _| |_(_) ___ _ __ #
4# | '_ \| | | || |/ _ \ / _ \| | | '_ \ / _` | | | | |/ _ \ / __| | | | '_ ` _ \ / _ \ '_ \| __/ _` | __| |/ _ \| '_ \ #
5# | |_) | |_| || | (_) | (_) | | | | | | (_| |_| |_| | (_) | (__| |_| | | | | | | __/ | | | || (_| | |_| | (_) | | | |#
6# | .__/ \__, ||_|\___/ \___/|_|_|_| |_|\__, (_)____/ \___/ \___|\__,_|_| |_| |_|\___|_| |_|\__\__,_|\__|_|\___/|_| |_|#
7# |_| |___/ |___/ #
8# ==================================================================================================================== #
9# Authors: #
10# Patrick Lehmann #
11# #
12# License: #
13# ==================================================================================================================== #
14# Copyright 2026-2026 Patrick Lehmann - Bötzingen, Germany #
15# #
16# Licensed under the Apache License, Version 2.0 (the "License"); #
17# you may not use this file except in compliance with the License. #
18# You may obtain a copy of the License at #
19# #
20# http://www.apache.org/licenses/LICENSE-2.0 #
21# #
22# Unless required by applicable law or agreed to in writing, software #
23# distributed under the License is distributed on an "AS IS" BASIS, #
24# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
25# See the License for the specific language governing permissions and #
26# limitations under the License. #
27# #
28# SPDX-License-Identifier: Apache-2.0 #
29# ==================================================================================================================== #
30#
31"""
32The ReST roles shared by pyTooling's documentation and its sibling projects.
34Every project used to declare these in its own :file:`doc/prolog.inc`, as ReST source re-parsed into every document
35of every project - and eighteen hand-copied files had already drifted apart. Registering them from an extension
36makes one declaration serve all of them, and puts the styling in a stylesheet instead of a ``raw:: html`` block.
38.. seealso::
40 :mod:`pyTooling.Documentation.Sphinx`
41 |rarr| The extension registering these, and what else it brings.
42"""
43from typing import Any, Optional as Nullable
45from docutils import nodes
46from docutils.parsers.rst.roles import code_role
47from docutils.parsers.rst.states import Inliner
48from docutils.utils import unescape
50from pyTooling.Decorators import export
53__all__ = ["STYLE_ROLES", "PYTHON_CODE_ROLE", "BREAK_ROLES"]
55#: The style roles, mapping a role's name to the CSS classes it applies.
56STYLE_ROLES = {
57 "bolditalic": ("bolditalic", ),
58 "underline": ("underline", ),
59 "strike": ("strike", ),
60 "xlarge": ("xlarge", ),
61 "red": ("colorred", ),
62 "green": ("colorgreen", ),
63 "blue": ("colorblue", ),
64 "purple": ("colorpurple", ),
65 "deletion": ("colorred", "strike"),
66 "addition": ("colorgreen", ),
67}
69#: Name of the role rendering inline Python code.
70PYTHON_CODE_ROLE = "pycode"
72#: The break roles, mapping a role's name to what each output format writes for it.
73#:
74#: A :class:`~docutils.nodes.raw` node carries the format it is meant for and a writer ignores every other one, so
75#: emitting all of them lets each builder pick its own. That is what makes ``|br|`` work outside HTML.
76BREAK_ROLES = {
77 "br": {"html": "<br />", "latex": r"\\"},
78 "hr": {"html": "<hr />", "latex": r"\par\noindent\rule{\textwidth}{0.4pt}\par"},
79}
82@export
83def styleRole(
84 name: str,
85 rawText: str,
86 text: str,
87 lineNumber: int,
88 inliner: Inliner,
89 options: Nullable[dict[str, Any]] = None,
90 content: Nullable[list[str]] = None
91) -> tuple[list[nodes.Node], list[nodes.system_message]]:
92 """
93 Render inline text with the CSS classes registered for the role's name.
95 This is what ``.. role:: red`` with ``:class: colorred`` does in a prolog, without the prolog: the role's name is
96 looked up in :data:`STYLE_ROLES` and the classes it maps to are put on an :class:`~docutils.nodes.inline` node.
98 :param name: Name the role was invoked by, which is what selects the classes.
99 :param rawText: The role's text including its markup.
100 :param text: The role's text with the markup removed.
101 :param lineNumber: Line the role was used on.
102 :param inliner: The inliner that called this role.
103 :param options: Options given to the role; unused, because the classes come from the role's name.
104 :param content: Content given to the role; unused.
105 :returns: Tuple of the produced nodes and the system messages, as a docutils role returns.
106 """
107 classes = STYLE_ROLES.get(name, (name, ))
109 return [nodes.inline(rawText, unescape(text), classes=list(classes))], []
112@export
113def pythonCodeRole(
114 name: str,
115 rawText: str,
116 text: str,
117 lineNumber: int,
118 inliner: Inliner,
119 options: Nullable[dict[str, Any]] = None,
120 content: Nullable[list[str]] = None
121) -> tuple[list[nodes.Node], list[nodes.system_message]]:
122 r"""
123 Render inline Python code, syntax-highlighted.
125 The docutils ``code`` role does the work; this fixes its language to Python and adds the ``highlight`` class, so
126 a page writes ``:pycode:`isinstance(x, int)``` instead of repeating the options at every use.
128 :param name: Name the role was invoked by; unused, because the language is fixed.
129 :param rawText: The role's text including its markup.
130 :param text: The role's text with the markup removed.
131 :param lineNumber: Line the role was used on.
132 :param inliner: The inliner that called this role.
133 :param options: Options given to the role; replaced by the fixed ones.
134 :param content: Content given to the role, handed to the ``code`` role unchanged.
135 :returns: Tuple of the produced nodes and the system messages, as a docutils role returns.
136 """
137 options = {"language": "python", "classes": ["highlight"]}
139 return code_role(name, rawText, text, lineNumber, inliner, options, [] if content is None else content)
142@export
143def breakRole(
144 name: str,
145 rawText: str,
146 text: str,
147 lineNumber: int,
148 inliner: Inliner,
149 options: Nullable[dict[str, Any]] = None,
150 content: Nullable[list[str]] = None
151) -> tuple[list[nodes.Node], list[nodes.system_message]]:
152 """
153 Emit a line or a horizontal break in every output format that has one.
155 A :class:`~docutils.nodes.raw` node names the format it is for, and a writer skips the ones that aren't its own -
156 so one node per format is emitted and each builder takes what it understands. Written as a ``raw:: html`` block,
157 which is what these were before, a break reached HTML and nothing else.
159 :param name: Name the role was invoked by, which selects what is written.
160 :param rawText: The role's text including its markup.
161 :param text: The role's text with the markup removed; unused, a break has no content.
162 :param lineNumber: Line the role was used on.
163 :param inliner: The inliner that called this role.
164 :param options: Options given to the role; unused.
165 :param content: Content given to the role; unused.
166 :returns: Tuple of the produced nodes and the system messages, as a docutils role returns.
167 """
168 return [nodes.raw("", written, format=outputFormat) for outputFormat, written in BREAK_ROLES[name].items()], []