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

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. 

33 

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. 

37 

38.. seealso:: 

39 

40 :mod:`pyTooling.Documentation.Sphinx` 

41 |rarr| The extension registering these, and what else it brings. 

42""" 

43from typing import Any, Optional as Nullable 

44 

45from docutils import nodes 

46from docutils.parsers.rst.roles import code_role 

47from docutils.parsers.rst.states import Inliner 

48from docutils.utils import unescape 

49 

50from pyTooling.Decorators import export 

51 

52 

53__all__ = ["STYLE_ROLES", "PYTHON_CODE_ROLE", "BREAK_ROLES"] 

54 

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} 

68 

69#: Name of the role rendering inline Python code. 

70PYTHON_CODE_ROLE = "pycode" 

71 

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} 

80 

81 

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. 

94 

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. 

97 

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, )) 

108 

109 return [nodes.inline(rawText, unescape(text), classes=list(classes))], [] 

110 

111 

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. 

124 

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. 

127 

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"]} 

138 

139 return code_role(name, rawText, text, lineNumber, inliner, options, [] if content is None else content) 

140 

141 

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. 

154 

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. 

158 

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()], []