From cf7da1843c45a4c2df7a749f7886a2d2ba0ee92a Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 15 Apr 2024 19:25:40 +0200 Subject: Adding upstream version 7.2.6. Signed-off-by: Daniel Baumann --- sphinx/addnodes.py | 605 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 605 insertions(+) create mode 100644 sphinx/addnodes.py (limited to 'sphinx/addnodes.py') diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py new file mode 100644 index 0000000..d51d600 --- /dev/null +++ b/sphinx/addnodes.py @@ -0,0 +1,605 @@ +"""Document tree nodes that Sphinx defines on top of those in Docutils.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +from docutils import nodes + +if TYPE_CHECKING: + from collections.abc import Sequence + + from docutils.nodes import Element + + from sphinx.application import Sphinx + +# deprecated name -> (object to return, canonical path or empty string) +_DEPRECATED_OBJECTS = { + 'meta': (nodes.meta, 'docutils.nodes.meta'), # type: ignore[attr-defined] + 'docutils_meta': (nodes.meta, 'docutils.nodes.meta'), # type: ignore[attr-defined] +} + + +def __getattr__(name): + if name not in _DEPRECATED_OBJECTS: + msg = f'module {__name__!r} has no attribute {name!r}' + raise AttributeError(msg) + + from sphinx.deprecation import _deprecation_warning + + deprecated_object, canonical_name = _DEPRECATED_OBJECTS[name] + _deprecation_warning(__name__, name, canonical_name, remove=(7, 0)) + return deprecated_object + + +class document(nodes.document): + """The document root element patched by Sphinx. + + This fixes that document.set_id() does not support a node having multiple node Ids. + see https://sourceforge.net/p/docutils/patches/167/ + + .. important:: This is only for Sphinx internal use. Please don't use this + in your extensions. It will be removed without deprecation period. + """ + + def set_id(self, node: Element, msgnode: Element | None = None, + suggested_prefix: str = '') -> str: + return super().set_id(node, msgnode, suggested_prefix) # type: ignore[call-arg] + + +class translatable(nodes.Node): + """Node which supports translation. + + The translation goes forward with following steps: + + 1. Preserve original translatable messages + 2. Apply translated messages from message catalog + 3. Extract preserved messages (for gettext builder) + + The translatable nodes MUST preserve original messages. + And these messages should not be overridden at applying step. + Because they are used at final step; extraction. + """ + + def preserve_original_messages(self) -> None: + """Preserve original translatable messages.""" + raise NotImplementedError + + def apply_translated_message(self, original_message: str, translated_message: str) -> None: + """Apply translated message.""" + raise NotImplementedError + + def extract_original_messages(self) -> Sequence[str]: + """Extract translation messages. + + :returns: list of extracted messages or messages generator + """ + raise NotImplementedError + + +class not_smartquotable: + """A node which does not support smart-quotes.""" + support_smartquotes = False + + +class toctree(nodes.General, nodes.Element, translatable): + """Node for inserting a "TOC tree".""" + + def preserve_original_messages(self) -> None: + # toctree entries + rawentries = self.setdefault('rawentries', []) + for title, _docname in self['entries']: + if title: + rawentries.append(title) + + # :caption: option + if self.get('caption'): + self['rawcaption'] = self['caption'] + + def apply_translated_message(self, original_message: str, translated_message: str) -> None: + # toctree entries + for i, (title, docname) in enumerate(self['entries']): + if title == original_message: + self['entries'][i] = (translated_message, docname) + + # :caption: option + if self.get('rawcaption') == original_message: + self['caption'] = translated_message + + def extract_original_messages(self) -> list[str]: + messages: list[str] = [] + + # toctree entries + messages.extend(self.get('rawentries', [])) + + # :caption: option + if 'rawcaption' in self: + messages.append(self['rawcaption']) + return messages + + +############################################################# +# Domain-specific object descriptions (class, function etc.) +############################################################# + +class _desc_classes_injector(nodes.Element, not_smartquotable): + """Helper base class for injecting a fixed list of classes. + + Use as the first base class. + """ + + classes: list[str] = [] + + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + self['classes'].extend(self.classes) + + +# Top-level nodes +################# + +class desc(nodes.Admonition, nodes.Element): + """Node for a list of object signatures and a common description of them. + + Contains one or more :py:class:`desc_signature` nodes + and then a single :py:class:`desc_content` node. + + This node always has two classes: + + - The name of the domain it belongs to, e.g., ``py`` or ``cpp``. + - The name of the object type in the domain, e.g., ``function``. + """ + + # TODO: can we introduce a constructor + # that forces the specification of the domain and objtyp? + + +class desc_signature(_desc_classes_injector, nodes.Part, nodes.Inline, nodes.TextElement): + """Node for a single object signature. + + As default the signature is a single-line signature. + Set ``is_multiline = True`` to describe a multi-line signature. + In that case all child nodes must be :py:class:`desc_signature_line` nodes. + + This node always has the classes ``sig``, ``sig-object``, and the domain it belongs to. + """ + # Note: the domain name is being added through a post-transform DescSigAddDomainAsClass + classes = ['sig', 'sig-object'] + + @property + def child_text_separator(self): + if self.get('is_multiline'): + return ' ' + else: + return super().child_text_separator + + +class desc_signature_line(nodes.Part, nodes.Inline, nodes.FixedTextElement): + """Node for a line in a multi-line object signature. + + It should only be used as a child of a :py:class:`desc_signature` + with ``is_multiline`` set to ``True``. + Set ``add_permalink = True`` for the line that should get the permalink. + """ + sphinx_line_type = '' + + +class desc_content(nodes.General, nodes.Element): + """Node for object description content. + + Must be the last child node in a :py:class:`desc` node. + """ + + +class desc_inline(_desc_classes_injector, nodes.Inline, nodes.TextElement): + """Node for a signature fragment in inline text. + + This is for example used for roles like :rst:role:`cpp:expr`. + + This node always has the classes ``sig``, ``sig-inline``, + and the name of the domain it belongs to. + """ + classes = ['sig', 'sig-inline'] + + def __init__(self, domain: str, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs, domain=domain) + self['classes'].append(domain) + + +# Nodes for high-level structure in signatures +############################################## + +# nodes to use within a desc_signature or desc_signature_line + +class desc_name(_desc_classes_injector, nodes.Part, nodes.Inline, nodes.FixedTextElement): + """Node for the main object name. + + For example, in the declaration of a Python class ``MyModule.MyClass``, + the main name is ``MyClass``. + + This node always has the class ``sig-name``. + """ + classes = ['sig-name', 'descname'] # 'descname' is for backwards compatibility + + +class desc_addname(_desc_classes_injector, nodes.Part, nodes.Inline, nodes.FixedTextElement): + """Node for additional name parts for an object. + + For example, in the declaration of a Python class ``MyModule.MyClass``, + the additional name part is ``MyModule.``. + + This node always has the class ``sig-prename``. + """ + # 'descclassname' is for backwards compatibility + classes = ['sig-prename', 'descclassname'] + + +# compatibility alias +desc_classname = desc_addname + + +class desc_type(nodes.Part, nodes.Inline, nodes.FixedTextElement): + """Node for return types or object type names.""" + + +class desc_returns(desc_type): + """Node for a "returns" annotation (a la -> in Python).""" + + def astext(self) -> str: + return ' -> ' + super().astext() + + +class desc_parameterlist(nodes.Part, nodes.Inline, nodes.FixedTextElement): + """Node for a general parameter list. + + As default the parameter list is written in line with the rest of the signature. + Set ``multi_line_parameter_list = True`` to describe a multi-line parameter list. + In that case each parameter will then be written on its own, indented line. + """ + child_text_separator = ', ' + + def astext(self): + return f'({super().astext()})' + + +class desc_type_parameter_list(nodes.Part, nodes.Inline, nodes.FixedTextElement): + """Node for a general type parameter list. + + As default the type parameters list is written in line with the rest of the signature. + Set ``multi_line_parameter_list = True`` to describe a multi-line type parameters list. + In that case each type parameter will then be written on its own, indented line. + """ + child_text_separator = ', ' + + def astext(self): + return f'[{super().astext()}]' + + +class desc_parameter(nodes.Part, nodes.Inline, nodes.FixedTextElement): + """Node for a single parameter.""" + + +class desc_type_parameter(nodes.Part, nodes.Inline, nodes.FixedTextElement): + """Node for a single type parameter.""" + + +class desc_optional(nodes.Part, nodes.Inline, nodes.FixedTextElement): + """Node for marking optional parts of the parameter list.""" + child_text_separator = ', ' + + def astext(self) -> str: + return '[' + super().astext() + ']' + + +class desc_annotation(nodes.Part, nodes.Inline, nodes.FixedTextElement): + """Node for signature annotations (not Python 3-style annotations).""" + + +# Leaf nodes for markup of text fragments +######################################### + +#: A set of classes inheriting :class:`desc_sig_element`. Each node class +#: is expected to be handled by the builder's translator class if the latter +#: does not inherit from SphinxTranslator. +#: +#: This set can be extended manually by third-party extensions or +#: by subclassing :class:`desc_sig_element` and using the class +#: keyword argument `_sig_element=True`. +SIG_ELEMENTS: set[type[desc_sig_element]] = set() + + +# Signature text elements, generally translated to node.inline +# in SigElementFallbackTransform. +# When adding a new one, add it to SIG_ELEMENTS via the class +# keyword argument `_sig_element=True` (e.g., see `desc_sig_space`). + +class desc_sig_element(nodes.inline, _desc_classes_injector): + """Common parent class of nodes for inline text of a signature.""" + classes: list[str] = [] + + def __init__(self, rawsource: str = '', text: str = '', + *children: Element, **attributes: Any) -> None: + super().__init__(rawsource, text, *children, **attributes) + self['classes'].extend(self.classes) + + def __init_subclass__(cls, *, _sig_element=False, **kwargs): + super().__init_subclass__(**kwargs) + if _sig_element: + # add the class to the SIG_ELEMENTS set if asked + SIG_ELEMENTS.add(cls) + + +# to not reinvent the wheel, the classes in the following desc_sig classes +# are based on those used in Pygments + +class desc_sig_space(desc_sig_element, _sig_element=True): + """Node for a space in a signature.""" + classes = ["w"] + + def __init__(self, rawsource: str = '', text: str = ' ', + *children: Element, **attributes: Any) -> None: + super().__init__(rawsource, text, *children, **attributes) + + +class desc_sig_name(desc_sig_element, _sig_element=True): + """Node for an identifier in a signature.""" + classes = ["n"] + + +class desc_sig_operator(desc_sig_element, _sig_element=True): + """Node for an operator in a signature.""" + classes = ["o"] + + +class desc_sig_punctuation(desc_sig_element, _sig_element=True): + """Node for punctuation in a signature.""" + classes = ["p"] + + +class desc_sig_keyword(desc_sig_element, _sig_element=True): + """Node for a general keyword in a signature.""" + classes = ["k"] + + +class desc_sig_keyword_type(desc_sig_element, _sig_element=True): + """Node for a keyword which is a built-in type in a signature.""" + classes = ["kt"] + + +class desc_sig_literal_number(desc_sig_element, _sig_element=True): + """Node for a numeric literal in a signature.""" + classes = ["m"] + + +class desc_sig_literal_string(desc_sig_element, _sig_element=True): + """Node for a string literal in a signature.""" + classes = ["s"] + + +class desc_sig_literal_char(desc_sig_element, _sig_element=True): + """Node for a character literal in a signature.""" + classes = ["sc"] + + +############################################################### +# new admonition-like constructs + +class versionmodified(nodes.Admonition, nodes.TextElement): + """Node for version change entries. + + Currently used for "versionadded", "versionchanged" and "deprecated" + directives. + """ + + +class seealso(nodes.Admonition, nodes.Element): + """Custom "see also" admonition.""" + + +class productionlist(nodes.Admonition, nodes.Element): + """Node for grammar production lists. + + Contains ``production`` nodes. + """ + + +class production(nodes.Part, nodes.Inline, nodes.FixedTextElement): + """Node for a single grammar production rule.""" + + +# other directive-level nodes + +class index(nodes.Invisible, nodes.Inline, nodes.TextElement): + """Node for index entries. + + This node is created by the ``index`` directive and has one attribute, + ``entries``. Its value is a list of 5-tuples of ``(entrytype, entryname, + target, ignored, key)``. + + *entrytype* is one of "single", "pair", "double", "triple". + + *key* is categorization characters (usually a single character) for + general index page. For the details of this, please see also: + :rst:dir:`glossary` and issue #2320. + """ + + +class centered(nodes.Part, nodes.TextElement): + """This node is deprecated.""" + + +class acks(nodes.Element): + """Special node for "acks" lists.""" + + +class hlist(nodes.Element): + """Node for "horizontal lists", i.e. lists that should be compressed to + take up less vertical space. + """ + + +class hlistcol(nodes.Element): + """Node for one column in a horizontal list.""" + + +class compact_paragraph(nodes.paragraph): + """Node for a compact paragraph (which never makes a

node).""" + + +class glossary(nodes.Element): + """Node to insert a glossary.""" + + +class only(nodes.Element): + """Node for "only" directives (conditional inclusion based on tags).""" + + +# meta-information nodes + +class start_of_file(nodes.Element): + """Node to mark start of a new file, used in the LaTeX builder only.""" + + +class highlightlang(nodes.Element): + """Inserted to set the highlight language and line number options for + subsequent code blocks. + """ + + +class tabular_col_spec(nodes.Element): + """Node for specifying tabular columns, used for LaTeX output.""" + + +# inline nodes + +class pending_xref(nodes.Inline, nodes.Element): + """Node for cross-references that cannot be resolved without complete + information about all documents. + + These nodes are resolved before writing output, in + BuildEnvironment.resolve_references. + """ + child_text_separator = '' + + +class pending_xref_condition(nodes.Inline, nodes.TextElement): + """Node representing a potential way to create a cross-reference and the + condition in which this way should be used. + + This node is only allowed to be placed under a :py:class:`pending_xref` + node. A **pending_xref** node must contain either no **pending_xref_condition** + nodes or it must only contains **pending_xref_condition** nodes. + + The cross-reference resolver will replace a :py:class:`pending_xref` which + contains **pending_xref_condition** nodes by the content of exactly one of + those **pending_xref_condition** nodes' content. It uses the **condition** + attribute to decide which **pending_xref_condition** node's content to + use. For example, let us consider how the cross-reference resolver acts on:: + + + + StringIO + + + io.StringIO + + If the cross-reference resolver successfully resolves the cross-reference, + then it rewrites the **pending_xref** as:: + + + + StringIO + + Otherwise, if the cross-reference resolution failed, it rewrites the + **pending_xref** as:: + + + + io.StringIO + + The **pending_xref_condition** node should have **condition** attribute. + Domains can be store their individual conditions into the attribute to + filter contents on resolving phase. As a reserved condition name, + ``condition="*"`` is used for the fallback of resolution failure. + Additionally, as a recommended condition name, ``condition="resolved"`` + represents a resolution success in the intersphinx module. + + .. versionadded:: 4.0 + """ + + +class number_reference(nodes.reference): + """Node for number references, similar to pending_xref.""" + + +class download_reference(nodes.reference): + """Node for download references, similar to pending_xref.""" + + +class literal_emphasis(nodes.emphasis, not_smartquotable): + """Node that behaves like `emphasis`, but further text processors are not + applied (e.g. smartypants for HTML output). + """ + + +class literal_strong(nodes.strong, not_smartquotable): + """Node that behaves like `strong`, but further text processors are not + applied (e.g. smartypants for HTML output). + """ + + +class manpage(nodes.Inline, nodes.FixedTextElement): + """Node for references to manpages.""" + + +def setup(app: Sphinx) -> dict[str, Any]: + app.add_node(toctree) + + app.add_node(desc) + app.add_node(desc_signature) + app.add_node(desc_signature_line) + app.add_node(desc_content) + app.add_node(desc_inline) + + app.add_node(desc_name) + app.add_node(desc_addname) + app.add_node(desc_type) + app.add_node(desc_returns) + app.add_node(desc_parameterlist) + app.add_node(desc_type_parameter_list) + app.add_node(desc_parameter) + app.add_node(desc_type_parameter) + app.add_node(desc_optional) + app.add_node(desc_annotation) + + for n in SIG_ELEMENTS: + app.add_node(n) + + app.add_node(versionmodified) + app.add_node(seealso) + app.add_node(productionlist) + app.add_node(production) + app.add_node(index) + app.add_node(centered) + app.add_node(acks) + app.add_node(hlist) + app.add_node(hlistcol) + app.add_node(compact_paragraph) + app.add_node(glossary) + app.add_node(only) + app.add_node(start_of_file) + app.add_node(highlightlang) + app.add_node(tabular_col_spec) + app.add_node(pending_xref) + app.add_node(number_reference) + app.add_node(download_reference) + app.add_node(literal_emphasis) + app.add_node(literal_strong) + app.add_node(manpage) + + return { + 'version': 'builtin', + 'parallel_read_safe': True, + 'parallel_write_safe': True, + } -- cgit v1.2.3