pyTooling.Sphinx 0.1.0rc2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyTooling/Sphinx/Abbreviation.py +452 -0
- pyTooling/Sphinx/CodeCoverage.py +878 -0
- pyTooling/Sphinx/CodeCoveragePages.py +443 -0
- pyTooling/Sphinx/CondensedClass.py +471 -0
- pyTooling/Sphinx/CondensedSource.py +441 -0
- pyTooling/Sphinx/DependencyTable.py +1437 -0
- pyTooling/Sphinx/DocCoverage.py +1120 -0
- pyTooling/Sphinx/ExternalLink.py +654 -0
- pyTooling/Sphinx/GitHubMarkdown.py +517 -0
- pyTooling/Sphinx/HTML.py +614 -0
- pyTooling/Sphinx/LaTeX.py +322 -0
- pyTooling/Sphinx/Markdown.py +691 -0
- pyTooling/Sphinx/Node.py +251 -0
- pyTooling/Sphinx/Pages.py +475 -0
- pyTooling/Sphinx/Resources/GitHubEmoji.txt +1901 -0
- pyTooling/Sphinx/Resources/__init__.py +53 -0
- pyTooling/Sphinx/Resources/pyTooling.Sphinx.sty +24 -0
- pyTooling/Sphinx/Resources/pyTooling.css +1116 -0
- pyTooling/Sphinx/Resources/pyTooling.js +53 -0
- pyTooling/Sphinx/Roles.py +168 -0
- pyTooling/Sphinx/SchemaGraph.py +228 -0
- pyTooling/Sphinx/Shields.py +600 -0
- pyTooling/Sphinx/Tree.py +596 -0
- pyTooling/Sphinx/UnicodeTable.py +519 -0
- pyTooling/Sphinx/Unittest.py +644 -0
- pyTooling/Sphinx/UnittestPages.py +325 -0
- pyTooling/Sphinx/Workaround.py +73 -0
- pyTooling/Sphinx/XMLSchemaGraph.py +194 -0
- pyTooling/Sphinx/__init__.py +1125 -0
- pyTooling/Sphinx/py.typed +0 -0
- pytooling_sphinx-0.1.0rc2.dist-info/METADATA +213 -0
- pytooling_sphinx-0.1.0rc2.dist-info/RECORD +36 -0
- pytooling_sphinx-0.1.0rc2.dist-info/WHEEL +5 -0
- pytooling_sphinx-0.1.0rc2.dist-info/licenses/LICENSE.md +82 -0
- pytooling_sphinx-0.1.0rc2.dist-info/licenses/NOTICE.md +24 -0
- pytooling_sphinx-0.1.0rc2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,452 @@
|
|
|
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
|
+
"""
|
|
32
|
+
A list of abbreviations, and roles referring to an abbreviation in the form they name.
|
|
33
|
+
|
|
34
|
+
The directive ``abbreviations`` holds the list - a definition list of abbreviations, each with its long form and,
|
|
35
|
+
optionally, a description like a glossary's:
|
|
36
|
+
|
|
37
|
+
.. code-block:: ReST
|
|
38
|
+
|
|
39
|
+
.. abbreviations::
|
|
40
|
+
|
|
41
|
+
FSM
|
|
42
|
+
:long: finite-state machine
|
|
43
|
+
:long-plural: finite-state machines
|
|
44
|
+
|
|
45
|
+
A machine in exactly one of a finite number of states at a time.
|
|
46
|
+
|
|
47
|
+
HDL
|
|
48
|
+
:long: hardware description language
|
|
49
|
+
|
|
50
|
+
A role refers to an abbreviation from any document, in the form it names, as LaTeX' ``acro`` package does:
|
|
51
|
+
``:acs:`FSM``` writes *FSM*, ``:acl:`FSM``` *finite-state machine*, ``:acf:`FSM``` *finite-state machine (FSM)*,
|
|
52
|
+
and ``:acsp:``, ``:aclp:`` and ``:acfp:`` the plurals. ``:ac:`` and ``:acp:`` are ``:acs:`` and ``:acsp:``: a reader
|
|
53
|
+
doesn't read a page linearly, so there is no first use spelling an abbreviation out. A text written as the role's
|
|
54
|
+
title, ``:acs:`FSM's <FSM>```, replaces the form. Every reference links to the abbreviation in the list; in HTML, a
|
|
55
|
+
short form shows its long form in a box on hover.
|
|
56
|
+
|
|
57
|
+
.. seealso::
|
|
58
|
+
|
|
59
|
+
:ref:`DIR/Abbreviations`
|
|
60
|
+
|rarr| The directive and the roles, with rendered examples.
|
|
61
|
+
:mod:`pyTooling.Sphinx`
|
|
62
|
+
|rarr| The extension this belongs to, and what else it brings.
|
|
63
|
+
"""
|
|
64
|
+
from typing import Any, ClassVar, Iterable, Iterator, Literal, NamedTuple, Optional as Nullable
|
|
65
|
+
|
|
66
|
+
from docutils import nodes
|
|
67
|
+
from sphinx.addnodes import pending_xref
|
|
68
|
+
from sphinx.builders import Builder
|
|
69
|
+
from sphinx.domains import Domain, ObjType
|
|
70
|
+
from sphinx.environment import BuildEnvironment
|
|
71
|
+
from sphinx.roles import XRefRole
|
|
72
|
+
from sphinx.util.logging import getLogger
|
|
73
|
+
from sphinx.util.nodes import make_id, make_refnode
|
|
74
|
+
|
|
75
|
+
from pyTooling.Decorators import export, readonly
|
|
76
|
+
from pyTooling.Documentation import splitDocString
|
|
77
|
+
from pyTooling.Sphinx import BaseDirective, SphinxExtensionError, strip, stripAndNormalize
|
|
78
|
+
from pyTooling.Sphinx.Node import Abbreviation
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
__all__ = ["DOMAIN_NAME", "CONFIG_PREFIX", "ROLES", "FIELDS"]
|
|
82
|
+
|
|
83
|
+
#: Name of the domain collecting the abbreviations of all documents.
|
|
84
|
+
DOMAIN_NAME = "abbreviation"
|
|
85
|
+
|
|
86
|
+
#: Prefix of the configuration values the directive adds to :file:`conf.py`.
|
|
87
|
+
CONFIG_PREFIX = "pyTooling_Abbreviation"
|
|
88
|
+
|
|
89
|
+
#: The roles, mapping a role's name to the form it writes and whether it is the plural.
|
|
90
|
+
ROLES = {
|
|
91
|
+
"ac": ("short", False),
|
|
92
|
+
"acs": ("short", False),
|
|
93
|
+
"acl": ("long", False),
|
|
94
|
+
"acf": ("full", False),
|
|
95
|
+
"acp": ("short", True),
|
|
96
|
+
"acsp": ("short", True),
|
|
97
|
+
"aclp": ("long", True),
|
|
98
|
+
"acfp": ("full", True),
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
#: The fields an abbreviation states its forms in, and whether one is required.
|
|
102
|
+
FIELDS = {
|
|
103
|
+
"long": True,
|
|
104
|
+
"plural": False,
|
|
105
|
+
"long-plural": False,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@export
|
|
110
|
+
class AbbreviationEntry(NamedTuple):
|
|
111
|
+
"""An abbreviation of the list: where it is written, and its forms as plain text."""
|
|
112
|
+
|
|
113
|
+
docName: str #: Name of the document holding the list.
|
|
114
|
+
anchor: str #: Identifier of the abbreviation's entry in that document.
|
|
115
|
+
short: str #: The abbreviation itself, e.g. ``FSM``.
|
|
116
|
+
long: str #: The long form, e.g. ``finite-state machine``.
|
|
117
|
+
plural: str #: The plural of the short form, e.g. ``FSMs``.
|
|
118
|
+
longPlural: str #: The plural of the long form, e.g. ``finite-state machines``.
|
|
119
|
+
summary: str = "" #: The description's first paragraph as plain text; empty without a description.
|
|
120
|
+
|
|
121
|
+
def Form(self, form: str, plural: bool) -> str:
|
|
122
|
+
"""
|
|
123
|
+
Return one form of the abbreviation as plain text.
|
|
124
|
+
|
|
125
|
+
:param form: ``short``, ``long`` or ``full``, the long form followed by the short one in parentheses.
|
|
126
|
+
:param plural: Whether the plural is asked for.
|
|
127
|
+
:returns: The form's text.
|
|
128
|
+
"""
|
|
129
|
+
short = self.plural if plural else self.short
|
|
130
|
+
long = self.longPlural if plural else self.long
|
|
131
|
+
if form == "short":
|
|
132
|
+
return short
|
|
133
|
+
elif form == "long":
|
|
134
|
+
return long
|
|
135
|
+
|
|
136
|
+
return f"{long} ({short})"
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@export
|
|
140
|
+
class AbbreviationRole(XRefRole):
|
|
141
|
+
"""
|
|
142
|
+
A role referring to an abbreviation in the domain :data:`DOMAIN_NAME`, whatever name it is registered by.
|
|
143
|
+
|
|
144
|
+
Sphinx registers a role outside a domain without one, so the reference would be resolved by nobody; this role puts
|
|
145
|
+
its own domain on the reference, and uses its name as the reference's type.
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
def run(self) -> tuple[list[nodes.Node], list[nodes.system_message]]:
|
|
149
|
+
"""
|
|
150
|
+
Create the reference, or only the text if the role is written with ``!``.
|
|
151
|
+
|
|
152
|
+
:returns: The nodes and messages a role returns.
|
|
153
|
+
"""
|
|
154
|
+
self.refdomain = DOMAIN_NAME
|
|
155
|
+
self.reftype = self.name.rpartition(":")[2]
|
|
156
|
+
self.classes = ["xref", DOMAIN_NAME, f"{DOMAIN_NAME}-{self.reftype}"]
|
|
157
|
+
|
|
158
|
+
if self.disabled:
|
|
159
|
+
return self.create_non_xref_node()
|
|
160
|
+
|
|
161
|
+
return self.create_xref_node()
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@export
|
|
165
|
+
class AbbreviationDomain(Domain):
|
|
166
|
+
"""
|
|
167
|
+
The domain collecting the abbreviations of all documents, and resolving the references to them.
|
|
168
|
+
|
|
169
|
+
An abbreviation is registered once in the whole documentation; it gets no entry in the general index.
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
name = DOMAIN_NAME #: Name of the domain.
|
|
173
|
+
label = "Abbreviation" #: Name of the domain shown to a reader.
|
|
174
|
+
|
|
175
|
+
object_types = {"abbreviation": ObjType("abbreviation", *ROLES)} #: The one kind of object, referred to by every role.
|
|
176
|
+
initial_data: dict[str, Any] = {"abbreviations": {}} #: The data of an empty documentation: no abbreviation.
|
|
177
|
+
roles = {
|
|
178
|
+
roleName: AbbreviationRole(innernodeclass=nodes.inline, warn_dangling=True) for roleName in ROLES
|
|
179
|
+
} #: The roles, also as ``:abbreviation:<role>:``.
|
|
180
|
+
dangling_warnings = {
|
|
181
|
+
roleName: "abbreviation '%(target)s' isn't in a list of abbreviations" for roleName in ROLES
|
|
182
|
+
} #: The warning for a reference to an unknown abbreviation, per role.
|
|
183
|
+
|
|
184
|
+
@readonly
|
|
185
|
+
def Abbreviations(self) -> dict[str, AbbreviationEntry]:
|
|
186
|
+
"""
|
|
187
|
+
Read-only property to return the abbreviations of all documents, from the domain's data.
|
|
188
|
+
|
|
189
|
+
:returns: The abbreviations, keyed by their short form.
|
|
190
|
+
"""
|
|
191
|
+
return self.data["abbreviations"]
|
|
192
|
+
|
|
193
|
+
def Register(self, entry: AbbreviationEntry, location: nodes.Node) -> None:
|
|
194
|
+
"""
|
|
195
|
+
Register an abbreviation, or warn that it is registered in a list already.
|
|
196
|
+
|
|
197
|
+
:param entry: The abbreviation.
|
|
198
|
+
:param location: Node the warning points to.
|
|
199
|
+
"""
|
|
200
|
+
if (registered := self.Abbreviations.get(entry.short)) is not None:
|
|
201
|
+
getLogger(__name__).warning(
|
|
202
|
+
f"abbreviation '{entry.short}' is in the list of document '{registered.docName}' already",
|
|
203
|
+
location=location
|
|
204
|
+
)
|
|
205
|
+
return
|
|
206
|
+
|
|
207
|
+
self.Abbreviations[entry.short] = entry
|
|
208
|
+
|
|
209
|
+
def clear_doc(self, docname: str) -> None:
|
|
210
|
+
"""
|
|
211
|
+
Forget the abbreviations of a document, before it is read again.
|
|
212
|
+
|
|
213
|
+
:param docname: Name of the document.
|
|
214
|
+
"""
|
|
215
|
+
for short in [short for short, entry in self.Abbreviations.items() if entry.docName == docname]:
|
|
216
|
+
del self.Abbreviations[short]
|
|
217
|
+
|
|
218
|
+
def merge_domaindata(self, docnames: Iterable[str], otherdata: dict[str, Any]) -> None:
|
|
219
|
+
"""
|
|
220
|
+
Take over the abbreviations a parallel reader collected for its documents.
|
|
221
|
+
|
|
222
|
+
:param docnames: Names of the documents the other reader read.
|
|
223
|
+
:param otherdata: The other reader's domain data.
|
|
224
|
+
"""
|
|
225
|
+
documents = set(docnames)
|
|
226
|
+
for short, entry in otherdata["abbreviations"].items():
|
|
227
|
+
if entry.docName in documents:
|
|
228
|
+
self.Abbreviations[short] = entry
|
|
229
|
+
|
|
230
|
+
def resolve_xref(
|
|
231
|
+
self,
|
|
232
|
+
env: BuildEnvironment,
|
|
233
|
+
fromdocname: str,
|
|
234
|
+
builder: Builder,
|
|
235
|
+
typ: str,
|
|
236
|
+
target: str,
|
|
237
|
+
node: pending_xref,
|
|
238
|
+
contnode: nodes.Element
|
|
239
|
+
) -> Nullable[nodes.reference]:
|
|
240
|
+
"""
|
|
241
|
+
Resolve a reference to an abbreviation into a link to its entry, showing the form the role names.
|
|
242
|
+
|
|
243
|
+
A short form is an :class:`~pyTooling.Sphinx.Node.Abbreviation`, so HTML explains it on hover; a full form holds
|
|
244
|
+
one. A title written in the role replaces the form's text; the box still explains the abbreviation. With
|
|
245
|
+
``pyTooling_Abbreviation_ShowSummary`` set, the box also shows the summary of the abbreviation's description.
|
|
246
|
+
|
|
247
|
+
:param env: The build environment.
|
|
248
|
+
:param fromdocname: Name of the document holding the reference.
|
|
249
|
+
:param builder: The builder writing the documents.
|
|
250
|
+
:param typ: The role's name.
|
|
251
|
+
:param target: The abbreviation referred to.
|
|
252
|
+
:param node: The reference.
|
|
253
|
+
:param contnode: The reference's text.
|
|
254
|
+
:returns: The link, or ``None`` if the abbreviation isn't in a list.
|
|
255
|
+
"""
|
|
256
|
+
if (entry := self.Abbreviations.get(target)) is None:
|
|
257
|
+
return None
|
|
258
|
+
|
|
259
|
+
form, plural = ROLES[typ]
|
|
260
|
+
short, long = entry.Form("short", plural), entry.Form("long", plural)
|
|
261
|
+
summary = entry.summary if env.config[f"{CONFIG_PREFIX}_ShowSummary"] else ""
|
|
262
|
+
if node.get("refexplicit", False):
|
|
263
|
+
text = contnode.astext()
|
|
264
|
+
content: list[nodes.Node] = [
|
|
265
|
+
Abbreviation(text, text, short=short, long=long, summary=summary) if form == "short" else nodes.Text(text)
|
|
266
|
+
]
|
|
267
|
+
elif form == "short":
|
|
268
|
+
content = [Abbreviation(short, short, short=short, long=long, summary=summary)]
|
|
269
|
+
elif form == "long":
|
|
270
|
+
content = [nodes.Text(long)]
|
|
271
|
+
else:
|
|
272
|
+
content = [
|
|
273
|
+
nodes.Text(f"{long} ("), Abbreviation(short, short, short=short, long=long, summary=summary), nodes.Text(")")
|
|
274
|
+
]
|
|
275
|
+
|
|
276
|
+
inline = nodes.inline("", "", *content, classes=contnode["classes"])
|
|
277
|
+
return make_refnode(builder, fromdocname, entry.docName, entry.anchor, inline)
|
|
278
|
+
|
|
279
|
+
def resolve_any_xref(
|
|
280
|
+
self,
|
|
281
|
+
env: BuildEnvironment,
|
|
282
|
+
fromdocname: str,
|
|
283
|
+
builder: Builder,
|
|
284
|
+
target: str,
|
|
285
|
+
node: pending_xref,
|
|
286
|
+
contnode: nodes.Element
|
|
287
|
+
) -> list[tuple[str, nodes.reference]]:
|
|
288
|
+
"""
|
|
289
|
+
Resolve a reference of role ``:any:`` to an abbreviation, which shows its short form.
|
|
290
|
+
|
|
291
|
+
:param env: The build environment.
|
|
292
|
+
:param fromdocname: Name of the document holding the reference.
|
|
293
|
+
:param builder: The builder writing the documents.
|
|
294
|
+
:param target: The text referred to.
|
|
295
|
+
:param node: The reference.
|
|
296
|
+
:param contnode: The reference's text.
|
|
297
|
+
:returns: The role and link, if the text is an abbreviation; otherwise nothing.
|
|
298
|
+
"""
|
|
299
|
+
if (reference := self.resolve_xref(env, fromdocname, builder, "acs", target, node, contnode)) is None:
|
|
300
|
+
return []
|
|
301
|
+
|
|
302
|
+
return [(f"{DOMAIN_NAME}:acs", reference)]
|
|
303
|
+
|
|
304
|
+
def get_objects(self) -> Iterator[tuple[str, str, str, str, str, int]]:
|
|
305
|
+
"""
|
|
306
|
+
Name the abbreviations, for the search and for other documentations referring to them.
|
|
307
|
+
|
|
308
|
+
:returns: A tuple per abbreviation: name, display name, type, document, anchor and search priority.
|
|
309
|
+
"""
|
|
310
|
+
for short, entry in self.Abbreviations.items():
|
|
311
|
+
yield short, short, "abbreviation", entry.docName, entry.anchor, 1
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
@export
|
|
315
|
+
class Abbreviations(BaseDirective):
|
|
316
|
+
"""
|
|
317
|
+
The ``abbreviations`` directive: a list of abbreviations, which the roles in :data:`ROLES` refer to.
|
|
318
|
+
|
|
319
|
+
The content is a definition list. A term is an abbreviation; its definition starts with a field list stating the
|
|
320
|
+
forms - :data:`FIELDS` - and may continue with a description. The list is shown in alphabetical order, unless
|
|
321
|
+
``:ordered:`` is ``no``; ``:class:`` puts additional CSS classes on the list.
|
|
322
|
+
"""
|
|
323
|
+
|
|
324
|
+
directiveName: str = "abbreviations" #: Name the directive is invoked by.
|
|
325
|
+
|
|
326
|
+
#: The configuration values this directive adds to :file:`conf.py`, as ``name: (default, rebuild, types)``. Each is
|
|
327
|
+
#: registered with :data:`CONFIG_PREFIX` as prefix, e.g. ``pyTooling_Abbreviation_ShowSummary``.
|
|
328
|
+
#:
|
|
329
|
+
#: ``ShowSummary`` adds the summary of an abbreviation's description - its first paragraph - to the box HTML shows
|
|
330
|
+
#: on hover.
|
|
331
|
+
configValues: ClassVar[dict[str, tuple[Any, Literal["env"], Any]]] = {
|
|
332
|
+
"ShowSummary": (False, "env", bool),
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
has_content = True #: A boolean; ``True`` if content is allowed.
|
|
336
|
+
required_arguments = 0 #: Number of required directive arguments.
|
|
337
|
+
optional_arguments = 0 #: Number of optional arguments after the required ones.
|
|
338
|
+
final_argument_whitespace = False #: A boolean; ``True`` if the last argument may contain spaces.
|
|
339
|
+
# docutils declares 'option_spec' on 'Directive' and 'BaseDirective' assigns it, so mypy calls every
|
|
340
|
+
# spelling of this override a conflict with one of them
|
|
341
|
+
#: Mapping of option names to validator functions.
|
|
342
|
+
option_spec: dict[str, Any] = { # type: ignore[misc]
|
|
343
|
+
"class": strip,
|
|
344
|
+
"ordered": stripAndNormalize,
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
def run(self) -> list[nodes.Node]:
|
|
348
|
+
"""
|
|
349
|
+
Render the list, and register its abbreviations in the domain.
|
|
350
|
+
|
|
351
|
+
:returns: The list as a definition list, or the message of whatever the content got wrong.
|
|
352
|
+
"""
|
|
353
|
+
container = nodes.container()
|
|
354
|
+
self.state.nested_parse(self.content, self.content_offset, container)
|
|
355
|
+
|
|
356
|
+
try:
|
|
357
|
+
ordered = self._ParseBooleanOption("ordered", True)
|
|
358
|
+
except SphinxExtensionError as ex:
|
|
359
|
+
return [self.state.document.reporter.error(str(ex), line=self.lineno)]
|
|
360
|
+
|
|
361
|
+
try:
|
|
362
|
+
definitionList = self._DefinitionList(container)
|
|
363
|
+
entries = [self._ReadItem(item) for item in definitionList.children]
|
|
364
|
+
except SphinxExtensionError as ex:
|
|
365
|
+
return [self.state.document.reporter.error(f"{self.directiveName}: {ex}", line=self.lineno)]
|
|
366
|
+
|
|
367
|
+
if ordered:
|
|
368
|
+
items = sorted(zip(definitionList.children, entries), key=lambda pair: pair[1][0].casefold())
|
|
369
|
+
definitionList[:] = [item for item, _ in items]
|
|
370
|
+
entries = [entry for _, entry in items]
|
|
371
|
+
|
|
372
|
+
domain: AbbreviationDomain = self.env.get_domain(DOMAIN_NAME) # type: ignore[assignment]
|
|
373
|
+
for item, (short, forms, description) in zip(definitionList.children, entries):
|
|
374
|
+
anchor = make_id(self.env, self.state.document, "abbreviation", short)
|
|
375
|
+
term = item[0]
|
|
376
|
+
term["ids"].append(anchor)
|
|
377
|
+
self.state.document.note_explicit_target(term)
|
|
378
|
+
|
|
379
|
+
long, plural, longPlural = forms["long"], forms.get("plural"), forms.get("long-plural")
|
|
380
|
+
entry = AbbreviationEntry(
|
|
381
|
+
docName=self.env.docname,
|
|
382
|
+
anchor=anchor,
|
|
383
|
+
short=short,
|
|
384
|
+
long=long.astext(),
|
|
385
|
+
plural=f"{short}s" if plural is None else plural.astext(),
|
|
386
|
+
longPlural=f"{long.astext()}s" if longPlural is None else longPlural.astext(),
|
|
387
|
+
summary=splitDocString("\n\n".join(node.astext() for node in description), maxSummaryLength=0)[0]
|
|
388
|
+
)
|
|
389
|
+
domain.Register(entry, item)
|
|
390
|
+
|
|
391
|
+
definition = nodes.definition()
|
|
392
|
+
definition += nodes.paragraph("", "", *long.children, classes=["abbreviation-long"])
|
|
393
|
+
definition += description
|
|
394
|
+
item.replace(item[1], definition)
|
|
395
|
+
|
|
396
|
+
definitionList["classes"] += ["pytooling-abbreviations"] + self.options.get("class", "").split()
|
|
397
|
+
|
|
398
|
+
return [definitionList]
|
|
399
|
+
|
|
400
|
+
@staticmethod
|
|
401
|
+
def _DefinitionList(container: nodes.Element) -> nodes.definition_list:
|
|
402
|
+
"""
|
|
403
|
+
Return the definition list the content has to consist of.
|
|
404
|
+
|
|
405
|
+
:param container: The parsed content.
|
|
406
|
+
:returns: The definition list.
|
|
407
|
+
:raises SphinxExtensionError: If the content is anything but one definition list.
|
|
408
|
+
"""
|
|
409
|
+
if len(container.children) != 1 or not isinstance(container[0], nodes.definition_list):
|
|
410
|
+
raise SphinxExtensionError("The directive's content isn't a definition list of abbreviations.")
|
|
411
|
+
|
|
412
|
+
return container[0]
|
|
413
|
+
|
|
414
|
+
@staticmethod
|
|
415
|
+
def _ReadItem(item: nodes.definition_list_item) -> tuple[str, dict[str, nodes.paragraph], list[nodes.Node]]:
|
|
416
|
+
"""
|
|
417
|
+
Read an abbreviation: its term, the fields stating its forms, and the description following them.
|
|
418
|
+
|
|
419
|
+
:param item: An item of the definition list.
|
|
420
|
+
:returns: The abbreviation, its forms keyed by field name, and the description.
|
|
421
|
+
:raises SphinxExtensionError: If the term has a classifier.
|
|
422
|
+
:raises SphinxExtensionError: If the definition doesn't start with a field list.
|
|
423
|
+
:raises SphinxExtensionError: If a field is unknown, given twice, or not a single paragraph.
|
|
424
|
+
:raises SphinxExtensionError: If a required field is missing.
|
|
425
|
+
"""
|
|
426
|
+
term, definition = item[0], item[-1]
|
|
427
|
+
short = term.astext().strip()
|
|
428
|
+
if len(item.children) != 2:
|
|
429
|
+
raise SphinxExtensionError(f"Abbreviation '{short}' has a classifier, which isn't supported.")
|
|
430
|
+
|
|
431
|
+
if len(definition.children) == 0 or not isinstance(definition[0], nodes.field_list):
|
|
432
|
+
raise SphinxExtensionError(f"Abbreviation '{short}' doesn't start with a field list, e.g. ':long:'.")
|
|
433
|
+
|
|
434
|
+
forms: dict[str, nodes.paragraph] = {}
|
|
435
|
+
for field in definition[0].children:
|
|
436
|
+
name = field[0].astext().strip()
|
|
437
|
+
body = field[1]
|
|
438
|
+
if name not in FIELDS:
|
|
439
|
+
ex = SphinxExtensionError(f"Abbreviation '{short}' has an unknown field ':{name}:'.")
|
|
440
|
+
ex.add_note(f"Known fields: {', '.join(f':{field}:' for field in FIELDS)}")
|
|
441
|
+
raise ex
|
|
442
|
+
elif name in forms:
|
|
443
|
+
raise SphinxExtensionError(f"Abbreviation '{short}' states field ':{name}:' twice.")
|
|
444
|
+
elif len(body.children) != 1 or not isinstance(body[0], nodes.paragraph):
|
|
445
|
+
raise SphinxExtensionError(f"Field ':{name}:' of abbreviation '{short}' isn't a single paragraph.")
|
|
446
|
+
|
|
447
|
+
forms[name] = body[0]
|
|
448
|
+
|
|
449
|
+
for name in (name for name, required in FIELDS.items() if required and name not in forms):
|
|
450
|
+
raise SphinxExtensionError(f"Abbreviation '{short}' has no field ':{name}:'.")
|
|
451
|
+
|
|
452
|
+
return short, forms, list(definition.children[1:])
|