1# $Id$
2# Author: David Goodger <goodger@python.org>
3# Copyright: This module has been placed in the public domain.
4
5"""
6This is the Docutils (Python Documentation Utilities) package.
7
8Package Structure
9=================
10
11Modules:
12
13- __init__.py: Contains component base classes, exception classes, and
14 Docutils version information.
15
16- core.py: Contains the ``Publisher`` class and ``publish_*()`` convenience
17 functions.
18
19- frontend.py: Runtime settings (command-line interface, configuration files)
20 processing, for Docutils front-ends.
21
22- io.py: Provides a uniform API for low-level input and output.
23
24- nodes.py: Docutils document tree (doctree) node class library.
25
26- statemachine.py: A finite state machine specialized for
27 regular-expression-based text filters.
28
29Subpackages:
30
31- languages: Language-specific mappings of terms.
32
33- parsers: Syntax-specific input parser modules or packages.
34
35- readers: Context-specific input handlers which understand the data
36 source and manage a parser.
37
38- transforms: Modules used by readers and writers to modify
39 the Docutils document tree.
40
41- utils: Contains the ``Reporter`` system warning class and miscellaneous
42 utilities used by readers, writers, and transforms.
43
44 utils/urischemes.py: Contains a complete mapping of known URI addressing
45 scheme names to descriptions.
46
47- utils/math: Contains functions for conversion of mathematical notation
48 between different formats (LaTeX, MathML, text, ...).
49
50- writers: Format-specific output translators.
51"""
52
53from __future__ import annotations
54
55from collections import namedtuple
56from typing import TYPE_CHECKING
57
58if TYPE_CHECKING:
59 from collections.abc import Sequence
60 from typing import Any, ClassVar, Literal, Protocol, Union
61
62 from docutils.nodes import Element
63 from docutils.transforms import Transform
64
65 _Components = Literal['reader', 'parser', 'writer', 'input', 'output']
66 _OptionTuple = tuple[str, list[str], dict[str, Any]]
67 _ReleaseLevels = Literal['alpha', 'beta', 'candidate', 'final']
68 _SettingsSpecTuple = Union[
69 tuple[str|None, str|None, Sequence[_OptionTuple]],
70 tuple[str|None, str|None, Sequence[_OptionTuple],
71 str|None, str|None, Sequence[_OptionTuple]],
72 tuple[str|None, str|None, Sequence[_OptionTuple],
73 str|None, str|None, Sequence[_OptionTuple],
74 str|None, str|None, Sequence[_OptionTuple]],
75 ]
76
77 class _UnknownReferenceResolver(Protocol):
78 """See `TransformSpec.unknown_reference_resolvers`."""
79
80 priority: int
81
82 def __call__(self, node: Element, /) -> bool:
83 ...
84
85__docformat__ = 'reStructuredText'
86
87__version__ = '0.22b.dev'
88"""Docutils version identifier (complies with PEP 440)::
89
90 major.minor[.micro][releaselevel[serial]][.dev]
91
92For version comparison operations, use `__version_info__` (see, below)
93rather than parsing the text of `__version__`.
94
95https://docutils.sourceforge.io/docs/dev/policies.html#version-identification
96"""
97
98__version_details__ = ''
99"""Optional extra version details (e.g. 'snapshot 2005-05-29, r3410').
100
101For development and release status, use `__version__ and `__version_info__`.
102"""
103
104
105class VersionInfo(namedtuple('VersionInfo',
106 'major minor micro releaselevel serial release')):
107 __slots__ = ()
108
109 major: int
110 minor: int
111 micro: int
112 releaselevel: _ReleaseLevels
113 serial: int
114 release: bool
115
116 def __new__(cls,
117 major: int = 0, minor: int = 0, micro: int = 0,
118 releaselevel: _ReleaseLevels = 'final',
119 serial: int = 0, release: bool = True,
120 ) -> VersionInfo:
121 releaselevels = ('alpha', 'beta', 'candidate', 'final')
122 if releaselevel not in releaselevels:
123 raise ValueError('releaselevel must be one of %r.'
124 % (releaselevels, ))
125 if releaselevel == 'final':
126 if not release:
127 raise ValueError('releaselevel "final" must not be used '
128 'with development versions (leads to wrong '
129 'version ordering of the related __version__')
130 # cf. https://peps.python.org/pep-0440/#summary-of-permitted-suffixes-and-relative-ordering # NoQA: E501
131 if serial != 0:
132 raise ValueError('"serial" must be 0 for final releases')
133
134 return super().__new__(cls, major, minor, micro,
135 releaselevel, serial, release)
136
137 def __lt__(self, other: object) -> bool:
138 if isinstance(other, tuple):
139 other = VersionInfo(*other)
140 return tuple.__lt__(self, other)
141
142 def __gt__(self, other: object) -> bool:
143 if isinstance(other, tuple):
144 other = VersionInfo(*other)
145 return tuple.__gt__(self, other)
146
147 def __le__(self, other: object) -> bool:
148 if isinstance(other, tuple):
149 other = VersionInfo(*other)
150 return tuple.__le__(self, other)
151
152 def __ge__(self, other: object) -> bool:
153 if isinstance(other, tuple):
154 other = VersionInfo(*other)
155 return tuple.__ge__(self, other)
156
157
158__version_info__ = VersionInfo(
159 major=0,
160 minor=22,
161 micro=0,
162 releaselevel='beta', # one of 'alpha', 'beta', 'candidate', 'final'
163 serial=0, # pre-release number (0 for final releases and snapshots)
164 release=False # True for official releases and pre-releases
165 )
166"""Comprehensive version information tuple.
167
168https://docutils.sourceforge.io/docs/dev/policies.html#version-identification
169"""
170
171
172class ApplicationError(Exception): pass
173class DataError(ApplicationError): pass
174
175
176class SettingsSpec:
177
178 """
179 Runtime setting specification base class.
180
181 SettingsSpec subclass objects used by `docutils.frontend.OptionParser`.
182 """
183
184 # TODO: replace settings_specs with a new data structure
185 # Backwards compatiblity:
186 # Drop-in components:
187 # Sphinx supplies settings_spec in the current format in some places
188 # Myst parser provides a settings_spec tuple
189 #
190 # Sphinx reads a settings_spec in order to set a default value
191 # in writers/html.py:59
192 # https://github.com/sphinx-doc/sphinx/blob/4.x/sphinx/writers/html.py
193 # This should be changed (before retiring the old format)
194 # to use `settings_default_overrides` instead.
195 settings_spec: ClassVar[_SettingsSpecTuple] = ()
196 """Runtime settings specification. Override in subclasses.
197
198 Defines runtime settings and associated command-line options, as used by
199 `docutils.frontend.OptionParser`. This is a tuple of:
200
201 - Option group title (string or `None` which implies no group, just a list
202 of single options).
203
204 - Description (string or `None`).
205
206 - A sequence of option tuples. Each consists of:
207
208 - Help text (string)
209
210 - List of option strings (e.g. ``['-Q', '--quux']``).
211
212 - Dictionary of keyword arguments sent to the OptionParser/OptionGroup
213 ``add_option`` method.
214
215 Runtime setting names are derived implicitly from long option names
216 ('--a-setting' becomes ``settings.a_setting``) or explicitly from the
217 'dest' keyword argument.
218
219 Most settings will also have a 'validator' keyword & function. The
220 validator function validates setting values (from configuration files
221 and command-line option arguments) and converts them to appropriate
222 types. For example, the ``docutils.frontend.validate_boolean``
223 function, **required by all boolean settings**, converts true values
224 ('1', 'on', 'yes', and 'true') to 1 and false values ('0', 'off',
225 'no', 'false', and '') to 0. Validators need only be set once per
226 setting. See the `docutils.frontend.validate_*` functions.
227
228 See the optparse docs for more details.
229
230 - More triples of group title, description, options, as many times as
231 needed. Thus, `settings_spec` tuples can be simply concatenated.
232 """
233
234 settings_defaults: ClassVar[dict[str, Any] | None] = None
235 """A dictionary of defaults for settings not in `settings_spec` (internal
236 settings, intended to be inaccessible by command-line and config file).
237 Override in subclasses."""
238
239 settings_default_overrides: ClassVar[dict[str, Any] | None] = None
240 """A dictionary of auxiliary defaults, to override defaults for settings
241 defined in other components' `setting_specs`. Override in subclasses."""
242
243 relative_path_settings: ClassVar[tuple[str, ...]] = ()
244 """Settings containing filesystem paths. Override in subclasses.
245 Settings listed here are to be interpreted relative to the current working
246 directory."""
247
248 config_section: ClassVar[str | None] = None
249 """The name of the config file section specific to this component
250 (lowercase, no brackets). Override in subclasses."""
251
252 config_section_dependencies: ClassVar[tuple[str, ...] | None] = None
253 """A list of names of config file sections that are to be applied before
254 `config_section`, in order (from general to specific). In other words,
255 the settings in `config_section` are to be overlaid on top of the settings
256 from these sections. The "general" section is assumed implicitly.
257 Override in subclasses."""
258
259
260class TransformSpec:
261 """
262 Runtime transform specification base class.
263
264 Provides the interface to register "transforms" and helper functions
265 to resolve references with a `docutils.transforms.Transformer`.
266
267 https://docutils.sourceforge.io/docs/ref/transforms.html
268 """
269
270 def get_transforms(self) -> list[type[Transform]]:
271 """Transforms required by this class. Override in subclasses."""
272 if self.default_transforms != ():
273 import warnings
274 warnings.warn('TransformSpec: the "default_transforms" attribute '
275 'will be removed in Docutils 2.0.\n'
276 'Use get_transforms() method instead.',
277 DeprecationWarning)
278 return list(self.default_transforms)
279 return []
280
281 # Deprecated; for compatibility.
282 default_transforms: ClassVar[tuple[()]] = ()
283
284 unknown_reference_resolvers: Sequence[_UnknownReferenceResolver] = ()
285 """List of hook functions which assist in resolving references.
286
287 Override in subclasses to implement component-specific resolving of
288 unknown references.
289
290 Unknown references have a 'refname' attribute which doesn't correspond
291 to any target in the document. Called when the transforms in
292 `docutils.transforms.references` are unable to find a correct target.
293
294 The list should contain functions which will try to resolve unknown
295 references, with the following signature::
296
297 def reference_resolver(node: nodes.Element) -> bool:
298 '''Returns boolean: true if resolved, false if not.'''
299
300 If the function is able to resolve the reference, it should also remove
301 the 'refname' attribute and mark the node as resolved::
302
303 del node['refname']
304 node.resolved = True
305
306 Each function must have a "priority" attribute which will affect the order
307 the unknown_reference_resolvers are run
308 cf. ../docs/api/transforms.html#transform-priority-range-categories ::
309
310 reference_resolver.priority = 500
311
312 Examples:
313 `writers.latex2e.Writer` defines a resolver to mark citation references
314 as resolved by BibTeX if the "use_bibtex" configuration setting is set.
315
316 The `MoinMoin ReStructured Text Parser`__ provides a resolver for
317 "WikiWiki links" (currently only in the outdated 1.9 version).
318
319 __ https://github.com/moinwiki/moin-1.9/blob/1.9.11/MoinMoin/parser/
320 text_rst.py
321 """
322
323
324class Component(SettingsSpec, TransformSpec):
325
326 """Base class for Docutils components."""
327
328 component_type: ClassVar[_Components | None] = None
329 """Name of the component type ('reader', 'parser', 'writer').
330 Override in subclasses."""
331
332 supported: ClassVar[tuple[str, ...]] = ()
333 """Name and aliases for this component. Override in subclasses."""
334
335 def supports(self, format: str) -> bool:
336 """
337 Is `format` supported by this component?
338
339 To be used by transforms to ask the dependent component if it supports
340 a certain input context or output format.
341 """
342 return format in self.supported