Merge branch '4.0.x' into 4.x

This commit is contained in:
Takeshi KOMIYA 2021-04-19 01:08:54 +09:00
commit 668bc9eec9
32 changed files with 1016 additions and 505 deletions

11
CHANGES
View File

@ -30,15 +30,26 @@ Dependencies
Incompatible changes Incompatible changes
-------------------- --------------------
* #9023: Change the CSS classes on :rst:role:`cpp:expr` and
:rst:role:`cpp:texpr`.
Deprecated Deprecated
---------- ----------
Features added Features added
-------------- --------------
* #8818: autodoc: Super class having ``Any`` arguments causes nit-picky warning
* #9103: LaTeX: imgconverter: conversion runs even if not needed
* #8127: py domain: Ellipsis in info-field-list causes nit-picky warning
* #9023: More CSS classes on domain descriptions, see :ref:`nodes` for details.
Bugs fixed Bugs fixed
---------- ----------
* C, C++, fix ``KeyError`` when an ``alias`` directive is the first C/C++
directive in a file with another C/C++ directive later.
Testing Testing
-------- --------

View File

@ -171,10 +171,26 @@ as metadata of the extension. Metadata keys currently recognized are:
source files can be used when the extension is loaded. It defaults to source files can be used when the extension is loaded. It defaults to
``False``, i.e. you have to explicitly specify your extension to be ``False``, i.e. you have to explicitly specify your extension to be
parallel-read-safe after checking that it is. parallel-read-safe after checking that it is.
.. note:: The *parallel-read-safe* extension must satisfy the following
conditions:
* The core logic of the extension is parallely executable during
the reading phase.
* It has event handlers for :event:`env-merge-info` and
:event:`env-purge-doc` events if it stores dataa to the build
environment object (env) during the reading phase.
* ``'parallel_write_safe'``: a boolean that specifies if parallel writing of * ``'parallel_write_safe'``: a boolean that specifies if parallel writing of
output files can be used when the extension is loaded. Since extensions output files can be used when the extension is loaded. Since extensions
usually don't negatively influence the process, this defaults to ``True``. usually don't negatively influence the process, this defaults to ``True``.
.. note:: The *parallel-write-safe* extension must satisfy the following
conditions:
* The core logic of the extension is parallely executable during
the writing phase.
APIs used for writing extensions APIs used for writing extensions
-------------------------------- --------------------------------

View File

@ -8,18 +8,32 @@ Doctree node classes added by Sphinx
Nodes for domain-specific object descriptions Nodes for domain-specific object descriptions
--------------------------------------------- ---------------------------------------------
Top-level nodes
...............
These nodes form the top-most levels of object descriptions.
.. autoclass:: desc .. autoclass:: desc
.. autoclass:: desc_signature .. autoclass:: desc_signature
.. autoclass:: desc_signature_line .. autoclass:: desc_signature_line
.. autoclass:: desc_content
.. autoclass:: desc_inline
Nodes for high-level structure in signatures
............................................
These nodes occur in in non-multiline :py:class:`desc_signature` nodes
and in :py:class:`desc_signature_line` nodes.
.. autoclass:: desc_name
.. autoclass:: desc_addname .. autoclass:: desc_addname
.. autoclass:: desc_type .. autoclass:: desc_type
.. autoclass:: desc_returns .. autoclass:: desc_returns
.. autoclass:: desc_name
.. autoclass:: desc_parameterlist .. autoclass:: desc_parameterlist
.. autoclass:: desc_parameter .. autoclass:: desc_parameter
.. autoclass:: desc_optional .. autoclass:: desc_optional
.. autoclass:: desc_annotation .. autoclass:: desc_annotation
.. autoclass:: desc_content
New admonition-like constructs New admonition-like constructs
------------------------------ ------------------------------

View File

@ -51,6 +51,10 @@ The extension adds a config value:
that generate links, i.e. ``:issue:`this issue <123>```. In this case, the that generate links, i.e. ``:issue:`this issue <123>```. In this case, the
*caption* is not relevant. *caption* is not relevant.
.. versionchanged:: 4.0
Support to substitute by '%s' in the caption.
.. note:: .. note::
Since links are generated from the role in the reading stage, they appear as Since links are generated from the role in the reading stage, they appear as

View File

@ -301,6 +301,7 @@ sure that "sphinx.ext.napoleon" is enabled in `conf.py`::
napoleon_use_ivar = False napoleon_use_ivar = False
napoleon_use_param = True napoleon_use_param = True
napoleon_use_rtype = True napoleon_use_rtype = True
napoleon_preprocess_types = False
napoleon_type_aliases = None napoleon_type_aliases = None
napoleon_attr_annotations = True napoleon_attr_annotations = True
@ -510,6 +511,16 @@ sure that "sphinx.ext.napoleon" is enabled in `conf.py`::
:returns: *bool* -- True if successful, False otherwise :returns: *bool* -- True if successful, False otherwise
.. confval:: napoleon_preprocess_types
True to convert the type definitions in the docstrings as references.
Defaults to *True*.
.. versionadded:: 3.2.1
.. versionchanged:: 3.5
Do preprocess the Google style docstrings also.
.. confval:: napoleon_type_aliases .. confval:: napoleon_type_aliases
A mapping to translate type names to other names or references. Works A mapping to translate type names to other names or references. Works
@ -570,4 +581,4 @@ sure that "sphinx.ext.napoleon" is enabled in `conf.py`::
.. versionadded:: 1.8 .. versionadded:: 1.8
.. versionchanged:: 3.5 .. versionchanged:: 3.5
Support ``params_style`` and ``returns_style`` Support ``params_style`` and ``returns_style``

View File

@ -115,25 +115,53 @@ class toctree(nodes.General, nodes.Element, translatable):
return messages return messages
# domain-specific object descriptions (class, function etc.) #############################################################
# Domain-specific object descriptions (class, function etc.)
#############################################################
class _desc_classes_injector(nodes.Element, not_smartquotable):
"""Helper base class for injecting a fixes 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): class desc(nodes.Admonition, nodes.Element):
"""Node for object descriptions. """Node for a list of object signatures and a common description of them.
This node is similar to a "definition list" with one definition. It Contains one or more :py:class:`desc_signature` nodes
contains one or more ``desc_signature`` and a ``desc_content``. 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(nodes.Part, nodes.Inline, nodes.TextElement):
"""Node for object signatures.
The "term" part of the custom Sphinx definition list. 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, As default the signature is a single-line signature.
but set ``is_multiline = True`` to describe a multi-line signature. Set ``is_multiline = True`` to describe a multi-line signature.
In that case all child nodes must be ``desc_signature_line`` nodes. 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 @property
def child_text_separator(self): def child_text_separator(self):
@ -144,18 +172,63 @@ class desc_signature(nodes.Part, nodes.Inline, nodes.TextElement):
class desc_signature_line(nodes.Part, nodes.Inline, nodes.FixedTextElement): class desc_signature_line(nodes.Part, nodes.Inline, nodes.FixedTextElement):
"""Node for a line in a multi-line object signatures. """Node for a line in a multi-line object signature.
It should only be used in a ``desc_signature`` with ``is_multiline`` set. 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. Set ``add_permalink = True`` for the line that should get the permalink.
""" """
sphinx_line_type = '' 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)
self['classes'].append(domain)
# Nodes for high-level structure in signatures
##############################################
# nodes to use within a desc_signature or desc_signature_line # nodes to use within a desc_signature or desc_signature_line
class desc_addname(nodes.Part, nodes.Inline, nodes.FixedTextElement): class desc_name(_desc_classes_injector, nodes.Part, nodes.Inline, nodes.FixedTextElement):
"""Node for additional name parts (module name, class name).""" """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 # compatibility alias
@ -168,14 +241,11 @@ class desc_type(nodes.Part, nodes.Inline, nodes.FixedTextElement):
class desc_returns(desc_type): class desc_returns(desc_type):
"""Node for a "returns" annotation (a la -> in Python).""" """Node for a "returns" annotation (a la -> in Python)."""
def astext(self) -> str: def astext(self) -> str:
return ' -> ' + super().astext() return ' -> ' + super().astext()
class desc_name(nodes.Part, nodes.Inline, nodes.FixedTextElement):
"""Node for the main object name."""
class desc_parameterlist(nodes.Part, nodes.Inline, nodes.FixedTextElement): class desc_parameterlist(nodes.Part, nodes.Inline, nodes.FixedTextElement):
"""Node for a general parameter list.""" """Node for a general parameter list."""
child_text_separator = ', ' child_text_separator = ', '
@ -200,14 +270,14 @@ class desc_annotation(nodes.Part, nodes.Inline, nodes.FixedTextElement):
"""Node for signature annotations (not Python 3-style annotations).""" """Node for signature annotations (not Python 3-style annotations)."""
class desc_content(nodes.General, nodes.Element): # Leaf nodes for markup of text fragments
"""Node for object description content. #########################################
This is the "definition" part of the custom Sphinx definition list. # Signature text elements, generally translated to node.inline
""" # in SigElementFallbackTransform.
# When adding a new one, add it to SIG_ELEMENTS.
class desc_sig_element(nodes.inline, _desc_classes_injector):
class desc_sig_element(nodes.inline):
"""Common parent class of nodes for inline text of a signature.""" """Common parent class of nodes for inline text of a signature."""
classes: List[str] = [] classes: List[str] = []
@ -217,8 +287,20 @@ class desc_sig_element(nodes.inline):
self['classes'].extend(self.classes) self['classes'].extend(self.classes)
# 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):
"""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): class desc_sig_name(desc_sig_element):
"""Node for a name in a signature.""" """Node for an identifier in a signature."""
classes = ["n"] classes = ["n"]
@ -228,10 +310,44 @@ class desc_sig_operator(desc_sig_element):
class desc_sig_punctuation(desc_sig_element): class desc_sig_punctuation(desc_sig_element):
"""Node for a punctuation in a signature.""" """Node for punctuation in a signature."""
classes = ["p"] classes = ["p"]
class desc_sig_keyword(desc_sig_element):
"""Node for a general keyword in a signature."""
classes = ["k"]
class desc_sig_keyword_type(desc_sig_element):
"""Node for a keyword which is a built-in type in a signature."""
classes = ["kt"]
class desc_sig_literal_number(desc_sig_element):
"""Node for a numeric literal in a signature."""
classes = ["m"]
class desc_sig_literal_string(desc_sig_element):
"""Node for a string literal in a signature."""
classes = ["s"]
class desc_sig_literal_char(desc_sig_element):
"""Node for a character literal in a signature."""
classes = ["sc"]
SIG_ELEMENTS = [desc_sig_space,
desc_sig_name,
desc_sig_operator,
desc_sig_punctuation,
desc_sig_keyword, desc_sig_keyword_type,
desc_sig_literal_number, desc_sig_literal_string, desc_sig_literal_char]
###############################################################
# new admonition-like constructs # new admonition-like constructs
class versionmodified(nodes.Admonition, nodes.TextElement): class versionmodified(nodes.Admonition, nodes.TextElement):
@ -336,6 +452,7 @@ class pending_xref(nodes.Inline, nodes.Element):
These nodes are resolved before writing output, in These nodes are resolved before writing output, in
BuildEnvironment.resolve_references. BuildEnvironment.resolve_references.
""" """
child_text_separator = ''
class pending_xref_condition(nodes.Inline, nodes.TextElement): class pending_xref_condition(nodes.Inline, nodes.TextElement):
@ -412,21 +529,25 @@ class manpage(nodes.Inline, nodes.FixedTextElement):
def setup(app: "Sphinx") -> Dict[str, Any]: def setup(app: "Sphinx") -> Dict[str, Any]:
app.add_node(toctree) app.add_node(toctree)
app.add_node(desc) app.add_node(desc)
app.add_node(desc_signature) app.add_node(desc_signature)
app.add_node(desc_signature_line) 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_addname)
app.add_node(desc_type) app.add_node(desc_type)
app.add_node(desc_returns) app.add_node(desc_returns)
app.add_node(desc_name)
app.add_node(desc_parameterlist) app.add_node(desc_parameterlist)
app.add_node(desc_parameter) app.add_node(desc_parameter)
app.add_node(desc_optional) app.add_node(desc_optional)
app.add_node(desc_annotation) app.add_node(desc_annotation)
app.add_node(desc_content)
app.add_node(desc_sig_name) for n in SIG_ELEMENTS:
app.add_node(desc_sig_operator) app.add_node(n)
app.add_node(desc_sig_punctuation)
app.add_node(versionmodified) app.add_node(versionmodified)
app.add_node(seealso) app.add_node(seealso)
app.add_node(productionlist) app.add_node(productionlist)

View File

@ -172,6 +172,7 @@ class ObjectDescription(SphinxDirective, Generic[T]):
node['noindex'] = noindex = ('noindex' in self.options) node['noindex'] = noindex = ('noindex' in self.options)
if self.domain: if self.domain:
node['classes'].append(self.domain) node['classes'].append(self.domain)
node['classes'].append(node['objtype'])
self.names: List[T] = [] self.names: List[T] = []
signatures = self.get_signatures() signatures = self.get_signatures()

View File

@ -9,8 +9,7 @@
""" """
import re import re
from typing import (Any, Callable, Dict, Generator, Iterator, List, Tuple, Type, TypeVar, from typing import Any, Callable, Dict, Generator, Iterator, List, Tuple, TypeVar, Union, cast
Union, cast)
from docutils import nodes from docutils import nodes
from docutils.nodes import Element, Node, TextElement, system_message from docutils.nodes import Element, Node, TextElement, system_message
@ -132,6 +131,10 @@ class ASTIdentifier(ASTBaseBase):
prefix: str, symbol: "Symbol") -> None: prefix: str, symbol: "Symbol") -> None:
# note: slightly different signature of describe_signature due to the prefix # note: slightly different signature of describe_signature due to the prefix
verify_description_mode(mode) verify_description_mode(mode)
if self.is_anon():
node = addnodes.desc_sig_name(text="[anonymous]")
else:
node = addnodes.desc_sig_name(self.identifier, self.identifier)
if mode == 'markType': if mode == 'markType':
targetText = prefix + self.identifier targetText = prefix + self.identifier
pnode = addnodes.pending_xref('', refdomain='c', pnode = addnodes.pending_xref('', refdomain='c',
@ -139,21 +142,14 @@ class ASTIdentifier(ASTBaseBase):
reftarget=targetText, modname=None, reftarget=targetText, modname=None,
classname=None) classname=None)
pnode['c:parent_key'] = symbol.get_lookup_key() pnode['c:parent_key'] = symbol.get_lookup_key()
if self.is_anon(): pnode += node
pnode += nodes.strong(text="[anonymous]")
else:
pnode += nodes.Text(self.identifier)
signode += pnode signode += pnode
elif mode == 'lastIsName': elif mode == 'lastIsName':
if self.is_anon(): nameNode = addnodes.desc_name()
signode += nodes.strong(text="[anonymous]") nameNode += node
else: signode += nameNode
signode += addnodes.desc_name(self.identifier, self.identifier)
elif mode == 'noneIsName': elif mode == 'noneIsName':
if self.is_anon(): signode += node
signode += nodes.strong(text="[anonymous]")
else:
signode += nodes.Text(self.identifier)
else: else:
raise Exception('Unknown description mode: %s' % mode) raise Exception('Unknown description mode: %s' % mode)
@ -184,18 +180,18 @@ class ASTNestedName(ASTBase):
# just print the name part, with template args, not template params # just print the name part, with template args, not template params
if mode == 'noneIsName': if mode == 'noneIsName':
if self.rooted: if self.rooted:
assert False, "Can this happen?" # TODO
signode += nodes.Text('.') signode += nodes.Text('.')
for i in range(len(self.names)): for i in range(len(self.names)):
if i != 0: if i != 0:
assert False, "Can this happen?" # TODO
signode += nodes.Text('.') signode += nodes.Text('.')
n = self.names[i] n = self.names[i]
n.describe_signature(signode, mode, env, '', symbol) n.describe_signature(signode, mode, env, '', symbol)
elif mode == 'param': elif mode == 'param':
assert not self.rooted, str(self) assert not self.rooted, str(self)
assert len(self.names) == 1 assert len(self.names) == 1
node = nodes.emphasis() self.names[0].describe_signature(signode, 'noneIsName', env, '', symbol)
self.names[0].describe_signature(node, 'noneIsName', env, '', symbol)
signode += node
elif mode == 'markType' or mode == 'lastIsName' or mode == 'markName': elif mode == 'markType' or mode == 'lastIsName' or mode == 'markName':
# Each element should be a pending xref targeting the complete # Each element should be a pending xref targeting the complete
# prefix. # prefix.
@ -213,13 +209,13 @@ class ASTNestedName(ASTBase):
if self.rooted: if self.rooted:
prefix += '.' prefix += '.'
if mode == 'lastIsName' and len(names) == 0: if mode == 'lastIsName' and len(names) == 0:
signode += nodes.Text('.') signode += addnodes.desc_sig_punctuation('.', '.')
else: else:
dest += nodes.Text('.') dest += addnodes.desc_sig_punctuation('.', '.')
for i in range(len(names)): for i in range(len(names)):
ident = names[i] ident = names[i]
if not first: if not first:
dest += nodes.Text('.') dest += addnodes.desc_sig_punctuation('.', '.')
prefix += '.' prefix += '.'
first = False first = False
txt_ident = str(ident) txt_ident = str(ident)
@ -228,7 +224,7 @@ class ASTNestedName(ASTBase):
prefix += txt_ident prefix += txt_ident
if mode == 'lastIsName': if mode == 'lastIsName':
if len(self.names) > 1: if len(self.names) > 1:
dest += addnodes.desc_addname('.', '.') dest += addnodes.desc_sig_punctuation('.', '.')
signode += dest signode += dest
self.names[-1].describe_signature(signode, mode, env, '', symbol) self.names[-1].describe_signature(signode, mode, env, '', symbol)
else: else:
@ -262,7 +258,8 @@ class ASTBooleanLiteral(ASTLiteral):
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
signode.append(nodes.Text(str(self))) txt = str(self)
signode += addnodes.desc_sig_keyword(txt, txt)
class ASTNumberLiteral(ASTLiteral): class ASTNumberLiteral(ASTLiteral):
@ -275,7 +272,7 @@ class ASTNumberLiteral(ASTLiteral):
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
txt = str(self) txt = str(self)
signode.append(nodes.Text(txt, txt)) signode += addnodes.desc_sig_literal_number(txt, txt)
class ASTCharLiteral(ASTLiteral): class ASTCharLiteral(ASTLiteral):
@ -297,7 +294,7 @@ class ASTCharLiteral(ASTLiteral):
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
txt = str(self) txt = str(self)
signode.append(nodes.Text(txt, txt)) signode += addnodes.desc_sig_literal_char(txt, txt)
class ASTStringLiteral(ASTLiteral): class ASTStringLiteral(ASTLiteral):
@ -310,7 +307,7 @@ class ASTStringLiteral(ASTLiteral):
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
txt = str(self) txt = str(self)
signode.append(nodes.Text(txt, txt)) signode += addnodes.desc_sig_literal_string(txt, txt)
class ASTIdExpression(ASTExpression): class ASTIdExpression(ASTExpression):
@ -341,9 +338,9 @@ class ASTParenExpr(ASTExpression):
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
signode.append(nodes.Text('(', '(')) signode += addnodes.desc_sig_punctuation('(', '(')
self.expr.describe_signature(signode, mode, env, symbol) self.expr.describe_signature(signode, mode, env, symbol)
signode.append(nodes.Text(')', ')')) signode += addnodes.desc_sig_punctuation(')', ')')
# Postfix expressions # Postfix expressions
@ -374,9 +371,9 @@ class ASTPostfixArray(ASTPostfixOp):
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
signode.append(nodes.Text('[')) signode += addnodes.desc_sig_punctuation('[', '[')
self.expr.describe_signature(signode, mode, env, symbol) self.expr.describe_signature(signode, mode, env, symbol)
signode.append(nodes.Text(']')) signode += addnodes.desc_sig_punctuation(']', ']')
class ASTPostfixInc(ASTPostfixOp): class ASTPostfixInc(ASTPostfixOp):
@ -385,7 +382,7 @@ class ASTPostfixInc(ASTPostfixOp):
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
signode.append(nodes.Text('++')) signode += addnodes.desc_sig_operator('++', '++')
class ASTPostfixDec(ASTPostfixOp): class ASTPostfixDec(ASTPostfixOp):
@ -394,7 +391,7 @@ class ASTPostfixDec(ASTPostfixOp):
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
signode.append(nodes.Text('--')) signode += addnodes.desc_sig_operator('--', '--')
class ASTPostfixMemberOfPointer(ASTPostfixOp): class ASTPostfixMemberOfPointer(ASTPostfixOp):
@ -406,7 +403,7 @@ class ASTPostfixMemberOfPointer(ASTPostfixOp):
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
signode.append(nodes.Text('->')) signode += addnodes.desc_sig_operator('->', '->')
self.name.describe_signature(signode, 'noneIsName', env, symbol) self.name.describe_signature(signode, 'noneIsName', env, symbol)
@ -444,9 +441,11 @@ class ASTUnaryOpExpr(ASTExpression):
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
signode.append(nodes.Text(self.op))
if self.op[0] in 'cn': if self.op[0] in 'cn':
signode.append(nodes.Text(" ")) signode += addnodes.desc_sig_keyword(self.op, self.op)
signode += addnodes.desc_sig_space()
else:
signode += addnodes.desc_sig_operator(self.op, self.op)
self.expr.describe_signature(signode, mode, env, symbol) self.expr.describe_signature(signode, mode, env, symbol)
@ -459,9 +458,10 @@ class ASTSizeofType(ASTExpression):
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
signode.append(nodes.Text('sizeof(')) signode += addnodes.desc_sig_keyword('sizeof', 'sizeof')
signode += addnodes.desc_sig_punctuation('(', '(')
self.typ.describe_signature(signode, mode, env, symbol) self.typ.describe_signature(signode, mode, env, symbol)
signode.append(nodes.Text(')')) signode += addnodes.desc_sig_punctuation(')', ')')
class ASTSizeofExpr(ASTExpression): class ASTSizeofExpr(ASTExpression):
@ -473,7 +473,8 @@ class ASTSizeofExpr(ASTExpression):
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
signode.append(nodes.Text('sizeof ')) signode += addnodes.desc_sig_keyword('sizeof', 'sizeof')
signode += addnodes.desc_sig_space()
self.expr.describe_signature(signode, mode, env, symbol) self.expr.describe_signature(signode, mode, env, symbol)
@ -486,9 +487,10 @@ class ASTAlignofExpr(ASTExpression):
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
signode.append(nodes.Text('alignof(')) signode += addnodes.desc_sig_keyword('alignof', 'alignof')
signode += addnodes.desc_sig_punctuation('(', '(')
self.typ.describe_signature(signode, mode, env, symbol) self.typ.describe_signature(signode, mode, env, symbol)
signode.append(nodes.Text(')')) signode += addnodes.desc_sig_punctuation(')', ')')
# Other expressions # Other expressions
@ -508,9 +510,9 @@ class ASTCastExpr(ASTExpression):
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
signode.append(nodes.Text('(')) signode += addnodes.desc_sig_punctuation('(', '(')
self.typ.describe_signature(signode, mode, env, symbol) self.typ.describe_signature(signode, mode, env, symbol)
signode.append(nodes.Text(')')) signode += addnodes.desc_sig_punctuation(')', ')')
self.expr.describe_signature(signode, mode, env, symbol) self.expr.describe_signature(signode, mode, env, symbol)
@ -535,9 +537,13 @@ class ASTBinOpExpr(ASTBase):
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
self.exprs[0].describe_signature(signode, mode, env, symbol) self.exprs[0].describe_signature(signode, mode, env, symbol)
for i in range(1, len(self.exprs)): for i in range(1, len(self.exprs)):
signode.append(nodes.Text(' ')) signode += addnodes.desc_sig_space()
signode.append(nodes.Text(self.ops[i - 1])) op = self.ops[i - 1]
signode.append(nodes.Text(' ')) if ord(op[0]) >= ord('a') and ord(op[0]) <= ord('z'):
signode += addnodes.desc_sig_keyword(op, op)
else:
signode += addnodes.desc_sig_operator(op, op)
signode += addnodes.desc_sig_space()
self.exprs[i].describe_signature(signode, mode, env, symbol) self.exprs[i].describe_signature(signode, mode, env, symbol)
@ -562,9 +568,13 @@ class ASTAssignmentExpr(ASTExpression):
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
self.exprs[0].describe_signature(signode, mode, env, symbol) self.exprs[0].describe_signature(signode, mode, env, symbol)
for i in range(1, len(self.exprs)): for i in range(1, len(self.exprs)):
signode.append(nodes.Text(' ')) signode += addnodes.desc_sig_space()
signode.append(nodes.Text(self.ops[i - 1])) op = self.ops[i - 1]
signode.append(nodes.Text(' ')) if ord(op[0]) >= ord('a') and ord(op[0]) <= ord('z'):
signode += addnodes.desc_sig_keyword(op, op)
else:
signode += addnodes.desc_sig_operator(op, op)
signode += addnodes.desc_sig_space()
self.exprs[i].describe_signature(signode, mode, env, symbol) self.exprs[i].describe_signature(signode, mode, env, symbol)
@ -580,7 +590,7 @@ class ASTFallbackExpr(ASTExpression):
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
signode += nodes.Text(self.expr) signode += nodes.literal(self.expr, self.expr)
################################################################################ ################################################################################
@ -600,7 +610,7 @@ class ASTTrailingTypeSpecFundamental(ASTTrailingTypeSpec):
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
signode += nodes.Text(str(self.name)) signode += addnodes.desc_sig_keyword_type(self.name, self.name)
class ASTTrailingTypeSpecName(ASTTrailingTypeSpec): class ASTTrailingTypeSpecName(ASTTrailingTypeSpec):
@ -623,8 +633,8 @@ class ASTTrailingTypeSpecName(ASTTrailingTypeSpec):
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
if self.prefix: if self.prefix:
signode += addnodes.desc_annotation(self.prefix, self.prefix) signode += addnodes.desc_sig_keyword(self.prefix, self.prefix)
signode += nodes.Text(' ') signode += addnodes.desc_sig_space()
self.nestedName.describe_signature(signode, mode, env, symbol=symbol) self.nestedName.describe_signature(signode, mode, env, symbol=symbol)
@ -647,7 +657,7 @@ class ASTFunctionParameter(ASTBase):
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
verify_description_mode(mode) verify_description_mode(mode)
if self.ellipsis: if self.ellipsis:
signode += nodes.Text('...') signode += addnodes.desc_sig_punctuation('...', '...')
else: else:
self.arg.describe_signature(signode, mode, env, symbol=symbol) self.arg.describe_signature(signode, mode, env, symbol=symbol)
@ -688,17 +698,18 @@ class ASTParameters(ASTBase):
paramlist += param paramlist += param
signode += paramlist signode += paramlist
else: else:
signode += nodes.Text('(', '(') signode += addnodes.desc_sig_punctuation('(', '(')
first = True first = True
for arg in self.args: for arg in self.args:
if not first: if not first:
signode += nodes.Text(', ', ', ') signode += addnodes.desc_sig_punctuation(',', ',')
signode += addnodes.desc_sig_space()
first = False first = False
arg.describe_signature(signode, 'markType', env, symbol=symbol) arg.describe_signature(signode, 'markType', env, symbol=symbol)
signode += nodes.Text(')', ')') signode += addnodes.desc_sig_punctuation(')', ')')
for attr in self.attrs: for attr in self.attrs:
signode += nodes.Text(' ') signode += addnodes.desc_sig_space()
attr.describe_signature(signode) attr.describe_signature(signode)
@ -744,12 +755,12 @@ class ASTDeclSpecsSimple(ASTBaseBase):
def describe_signature(self, modifiers: List[Node]) -> None: def describe_signature(self, modifiers: List[Node]) -> None:
def _add(modifiers: List[Node], text: str) -> None: def _add(modifiers: List[Node], text: str) -> None:
if len(modifiers) > 0: if len(modifiers) > 0:
modifiers.append(nodes.Text(' ')) modifiers.append(addnodes.desc_sig_space())
modifiers.append(addnodes.desc_annotation(text, text)) modifiers.append(addnodes.desc_sig_keyword(text, text))
for attr in self.attrs: for attr in self.attrs:
if len(modifiers) > 0: if len(modifiers) > 0:
modifiers.append(nodes.Text(' ')) modifiers.append(addnodes.desc_sig_space())
modifiers.append(attr.describe_signature(modifiers)) modifiers.append(attr.describe_signature(modifiers))
if self.storage: if self.storage:
_add(modifiers, self.storage) _add(modifiers, self.storage)
@ -799,24 +810,19 @@ class ASTDeclSpecs(ASTBase):
verify_description_mode(mode) verify_description_mode(mode)
modifiers: List[Node] = [] modifiers: List[Node] = []
def _add(modifiers: List[Node], text: str) -> None:
if len(modifiers) > 0:
modifiers.append(nodes.Text(' '))
modifiers.append(addnodes.desc_annotation(text, text))
self.leftSpecs.describe_signature(modifiers) self.leftSpecs.describe_signature(modifiers)
for m in modifiers: for m in modifiers:
signode += m signode += m
if self.trailingTypeSpec: if self.trailingTypeSpec:
if len(modifiers) > 0: if len(modifiers) > 0:
signode += nodes.Text(' ') signode += addnodes.desc_sig_space()
self.trailingTypeSpec.describe_signature(signode, mode, env, self.trailingTypeSpec.describe_signature(signode, mode, env,
symbol=symbol) symbol=symbol)
modifiers = [] modifiers = []
self.rightSpecs.describe_signature(modifiers) self.rightSpecs.describe_signature(modifiers)
if len(modifiers) > 0: if len(modifiers) > 0:
signode += nodes.Text(' ') signode += addnodes.desc_sig_space()
for m in modifiers: for m in modifiers:
signode += m signode += m
@ -857,13 +863,13 @@ class ASTArray(ASTBase):
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
verify_description_mode(mode) verify_description_mode(mode)
signode.append(nodes.Text("[")) signode += addnodes.desc_sig_punctuation('[', '[')
addSpace = False addSpace = False
def _add(signode: TextElement, text: str) -> bool: def _add(signode: TextElement, text: str) -> bool:
if addSpace: if addSpace:
signode += nodes.Text(' ') signode += addnodes.desc_sig_space()
signode += addnodes.desc_annotation(text, text) signode += addnodes.desc_sig_keyword(text, text)
return True return True
if self.static: if self.static:
@ -875,12 +881,12 @@ class ASTArray(ASTBase):
if self.const: if self.const:
addSpace = _add(signode, 'const') addSpace = _add(signode, 'const')
if self.vla: if self.vla:
signode.append(nodes.Text('*')) signode += addnodes.desc_sig_punctuation('*', '*')
elif self.size: elif self.size:
if addSpace: if addSpace:
signode += nodes.Text(' ') signode += addnodes.desc_sig_space()
self.size.describe_signature(signode, 'markType', env, symbol) self.size.describe_signature(signode, 'markType', env, symbol)
signode.append(nodes.Text("]")) signode += addnodes.desc_sig_punctuation(']', ']')
class ASTDeclarator(ASTBase): class ASTDeclarator(ASTBase):
@ -964,7 +970,9 @@ class ASTDeclaratorNameBitField(ASTDeclarator):
verify_description_mode(mode) verify_description_mode(mode)
if self.declId: if self.declId:
self.declId.describe_signature(signode, mode, env, symbol) self.declId.describe_signature(signode, mode, env, symbol)
signode += nodes.Text(' : ', ' : ') signode += addnodes.desc_sig_space()
signode += addnodes.desc_sig_punctuation(':', ':')
signode += addnodes.desc_sig_space()
self.size.describe_signature(signode, mode, env, symbol) self.size.describe_signature(signode, mode, env, symbol)
@ -1016,28 +1024,28 @@ class ASTDeclaratorPtr(ASTDeclarator):
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
verify_description_mode(mode) verify_description_mode(mode)
signode += nodes.Text("*") signode += addnodes.desc_sig_punctuation('*', '*')
for a in self.attrs: for a in self.attrs:
a.describe_signature(signode) a.describe_signature(signode)
if len(self.attrs) > 0 and (self.restrict or self.volatile or self.const): if len(self.attrs) > 0 and (self.restrict or self.volatile or self.const):
signode += nodes.Text(' ') signode += addnodes.desc_sig_space()
def _add_anno(signode: TextElement, text: str) -> None: def _add_anno(signode: TextElement, text: str) -> None:
signode += addnodes.desc_annotation(text, text) signode += addnodes.desc_sig_keyword(text, text)
if self.restrict: if self.restrict:
_add_anno(signode, 'restrict') _add_anno(signode, 'restrict')
if self.volatile: if self.volatile:
if self.restrict: if self.restrict:
signode += nodes.Text(' ') signode += addnodes.desc_sig_space()
_add_anno(signode, 'volatile') _add_anno(signode, 'volatile')
if self.const: if self.const:
if self.restrict or self.volatile: if self.restrict or self.volatile:
signode += nodes.Text(' ') signode += addnodes.desc_sig_space()
_add_anno(signode, 'const') _add_anno(signode, 'const')
if self.const or self.volatile or self.restrict or len(self.attrs) > 0: if self.const or self.volatile or self.restrict or len(self.attrs) > 0:
if self.next.require_space_after_declSpecs(): if self.next.require_space_after_declSpecs():
signode += nodes.Text(' ') signode += addnodes.desc_sig_space()
self.next.describe_signature(signode, mode, env, symbol) self.next.describe_signature(signode, mode, env, symbol)
@ -1070,9 +1078,9 @@ class ASTDeclaratorParen(ASTDeclarator):
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
verify_description_mode(mode) verify_description_mode(mode)
signode += nodes.Text('(') signode += addnodes.desc_sig_punctuation('(', '(')
self.inner.describe_signature(signode, mode, env, symbol) self.inner.describe_signature(signode, mode, env, symbol)
signode += nodes.Text(')') signode += addnodes.desc_sig_punctuation(')', ')')
self.next.describe_signature(signode, "noneIsName", env, symbol) self.next.describe_signature(signode, "noneIsName", env, symbol)
@ -1090,15 +1098,16 @@ class ASTParenExprList(ASTBaseParenExprList):
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
verify_description_mode(mode) verify_description_mode(mode)
signode.append(nodes.Text('(')) signode += addnodes.desc_sig_punctuation('(', '(')
first = True first = True
for e in self.exprs: for e in self.exprs:
if not first: if not first:
signode.append(nodes.Text(', ')) signode += addnodes.desc_sig_punctuation(',', ',')
signode += addnodes.desc_sig_space()
else: else:
first = False first = False
e.describe_signature(signode, mode, env, symbol) e.describe_signature(signode, mode, env, symbol)
signode.append(nodes.Text(')')) signode += addnodes.desc_sig_punctuation(')', ')')
class ASTBracedInitList(ASTBase): class ASTBracedInitList(ASTBase):
@ -1114,17 +1123,18 @@ class ASTBracedInitList(ASTBase):
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
verify_description_mode(mode) verify_description_mode(mode)
signode.append(nodes.Text('{')) signode += addnodes.desc_sig_punctuation('{', '{')
first = True first = True
for e in self.exprs: for e in self.exprs:
if not first: if not first:
signode.append(nodes.Text(', ')) signode += addnodes.desc_sig_punctuation(',', ',')
signode += addnodes.desc_sig_space()
else: else:
first = False first = False
e.describe_signature(signode, mode, env, symbol) e.describe_signature(signode, mode, env, symbol)
if self.trailingComma: if self.trailingComma:
signode.append(nodes.Text(',')) signode += addnodes.desc_sig_punctuation(',', ',')
signode.append(nodes.Text('}')) signode += addnodes.desc_sig_punctuation('}', '}')
class ASTInitializer(ASTBase): class ASTInitializer(ASTBase):
@ -1144,7 +1154,9 @@ class ASTInitializer(ASTBase):
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
verify_description_mode(mode) verify_description_mode(mode)
if self.hasAssign: if self.hasAssign:
signode.append(nodes.Text(' = ')) signode += addnodes.desc_sig_space()
signode += addnodes.desc_sig_punctuation('=', '=')
signode += addnodes.desc_sig_space()
self.value.describe_signature(signode, 'markType', env, symbol) self.value.describe_signature(signode, 'markType', env, symbol)
@ -1187,7 +1199,7 @@ class ASTType(ASTBase):
self.declSpecs.describe_signature(signode, 'markType', env, symbol) self.declSpecs.describe_signature(signode, 'markType', env, symbol)
if (self.decl.require_space_after_declSpecs() and if (self.decl.require_space_after_declSpecs() and
len(str(self.declSpecs)) > 0): len(str(self.declSpecs)) > 0):
signode += nodes.Text(' ') signode += addnodes.desc_sig_space()
# for parameters that don't really declare new names we get 'markType', # for parameters that don't really declare new names we get 'markType',
# this should not be propagated, but be 'noneIsName'. # this should not be propagated, but be 'noneIsName'.
if mode == 'markType': if mode == 'markType':
@ -1241,10 +1253,10 @@ class ASTMacroParameter(ASTBase):
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
verify_description_mode(mode) verify_description_mode(mode)
if self.ellipsis: if self.ellipsis:
signode += nodes.Text('...') signode += addnodes.desc_sig_punctuation('...', '...')
elif self.variadic: elif self.variadic:
name = str(self) name = str(self)
signode += nodes.emphasis(name, name) signode += addnodes.desc_sig_name(name, name)
else: else:
self.arg.describe_signature(signode, mode, env, symbol=symbol) self.arg.describe_signature(signode, mode, env, symbol=symbol)
@ -1427,23 +1439,27 @@ class ASTDeclaration(ASTBaseBase):
elif self.objectType == 'macro': elif self.objectType == 'macro':
pass pass
elif self.objectType == 'struct': elif self.objectType == 'struct':
mainDeclNode += addnodes.desc_annotation('struct ', 'struct ') mainDeclNode += addnodes.desc_sig_keyword('struct', 'struct')
mainDeclNode += addnodes.desc_sig_space()
elif self.objectType == 'union': elif self.objectType == 'union':
mainDeclNode += addnodes.desc_annotation('union ', 'union ') mainDeclNode += addnodes.desc_sig_keyword('union', 'union')
mainDeclNode += addnodes.desc_sig_space()
elif self.objectType == 'enum': elif self.objectType == 'enum':
mainDeclNode += addnodes.desc_annotation('enum ', 'enum ') mainDeclNode += addnodes.desc_sig_keyword('enum', 'enum')
mainDeclNode += addnodes.desc_sig_space()
elif self.objectType == 'enumerator': elif self.objectType == 'enumerator':
mainDeclNode += addnodes.desc_annotation('enumerator ', 'enumerator ') mainDeclNode += addnodes.desc_sig_keyword('enumerator', 'enumerator')
mainDeclNode += addnodes.desc_sig_space()
elif self.objectType == 'type': elif self.objectType == 'type':
decl = cast(ASTType, self.declaration) decl = cast(ASTType, self.declaration)
prefix = decl.get_type_declaration_prefix() prefix = decl.get_type_declaration_prefix()
prefix += ' ' mainDeclNode += addnodes.desc_sig_keyword(prefix, prefix)
mainDeclNode += addnodes.desc_annotation(prefix, prefix) mainDeclNode += addnodes.desc_sig_space()
else: else:
assert False assert False
self.declaration.describe_signature(mainDeclNode, mode, env, self.symbol) self.declaration.describe_signature(mainDeclNode, mode, env, self.symbol)
if self.semicolon: if self.semicolon:
mainDeclNode += nodes.Text(';') mainDeclNode += addnodes.desc_sig_punctuation(';', ';')
class SymbolLookupResult: class SymbolLookupResult:
@ -3438,7 +3454,8 @@ class AliasNode(nodes.Element):
if 'c:parent_symbol' not in env.temp_data: if 'c:parent_symbol' not in env.temp_data:
root = env.domaindata['c']['root_symbol'] root = env.domaindata['c']['root_symbol']
env.temp_data['c:parent_symbol'] = root env.temp_data['c:parent_symbol'] = root
self.parentKey = env.temp_data['c:parent_symbol'].get_lookup_key() env.ref_context['c:parent_key'] = root.get_lookup_key()
self.parentKey = env.ref_context['c:parent_key']
else: else:
assert parentKey is not None assert parentKey is not None
self.parentKey = parentKey self.parentKey = parentKey
@ -3661,31 +3678,28 @@ class CExprRole(SphinxRole):
if asCode: if asCode:
# render the expression as inline code # render the expression as inline code
self.class_type = 'c-expr' self.class_type = 'c-expr'
self.node_type: Type[TextElement] = nodes.literal
else: else:
# render the expression as inline text # render the expression as inline text
self.class_type = 'c-texpr' self.class_type = 'c-texpr'
self.node_type = nodes.inline
def run(self) -> Tuple[List[Node], List[system_message]]: def run(self) -> Tuple[List[Node], List[system_message]]:
text = self.text.replace('\n', ' ') text = self.text.replace('\n', ' ')
parser = DefinitionParser(text, location=self.get_source_info(), parser = DefinitionParser(text, location=self.get_source_info(),
config=self.env.config) config=self.env.config)
# attempt to mimic XRefRole classes, except that... # attempt to mimic XRefRole classes, except that...
classes = ['xref', 'c', self.class_type]
try: try:
ast = parser.parse_expression() ast = parser.parse_expression()
except DefinitionError as ex: except DefinitionError as ex:
logger.warning('Unparseable C expression: %r\n%s', text, ex, logger.warning('Unparseable C expression: %r\n%s', text, ex,
location=self.get_source_info()) location=self.get_source_info())
# see below # see below
return [self.node_type(text, text, classes=classes)], [] return [addnodes.desc_inline('c', text, text, classes=[self.class_type])], []
parentSymbol = self.env.temp_data.get('c:parent_symbol', None) parentSymbol = self.env.temp_data.get('c:parent_symbol', None)
if parentSymbol is None: if parentSymbol is None:
parentSymbol = self.env.domaindata['c']['root_symbol'] parentSymbol = self.env.domaindata['c']['root_symbol']
# ...most if not all of these classes should really apply to the individual references, # ...most if not all of these classes should really apply to the individual references,
# not the container node # not the container node
signode = self.node_type(classes=classes) signode = addnodes.desc_inline('c', classes=[self.class_type])
ast.describe_signature(signode, 'markType', self.env, parentSymbol) ast.describe_signature(signode, 'markType', self.env, parentSymbol)
return [signode], [] return [signode], []

File diff suppressed because it is too large Load Diff

View File

@ -304,7 +304,7 @@ class PyXrefMixin:
def make_xrefs(self, rolename: str, domain: str, target: str, def make_xrefs(self, rolename: str, domain: str, target: str,
innernode: Type[TextlikeNode] = nodes.emphasis, innernode: Type[TextlikeNode] = nodes.emphasis,
contnode: Node = None, env: BuildEnvironment = None) -> List[Node]: contnode: Node = None, env: BuildEnvironment = None) -> List[Node]:
delims = r'(\s*[\[\]\(\),](?:\s*or\s)?\s*|\s+or\s+)' delims = r'(\s*[\[\]\(\),](?:\s*or\s)?\s*|\s+or\s+|\.\.\.)'
delims_re = re.compile(delims) delims_re = re.compile(delims)
sub_targets = re.split(delims, target) sub_targets = re.split(delims, target)

View File

@ -240,7 +240,7 @@ class Config:
:returns: *bool* -- True if successful, False otherwise :returns: *bool* -- True if successful, False otherwise
napoleon_preprocess_types : :obj:`bool` (Defaults to False) napoleon_preprocess_types : :obj:`bool` (Defaults to False)
Enable the type preprocessor for numpy style docstrings. Enable the type preprocessor.
napoleon_type_aliases : :obj:`dict` (Defaults to None) napoleon_type_aliases : :obj:`dict` (Defaults to None)
Add a mapping of strings to string, translating types in numpy Add a mapping of strings to string, translating types in numpy

View File

@ -508,6 +508,63 @@ table.hlist td {
vertical-align: top; vertical-align: top;
} }
/* -- object description styles --------------------------------------------- */
.sig {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
}
.sig-name, code.descname {
background-color: transparent;
font-weight: bold;
}
.sig-name {
font-size: 1.1em;
}
code.descname {
font-size: 1.2em;
}
.sig-prename, code.descclassname {
background-color: transparent;
}
.optional {
font-size: 1.3em;
}
.sig-paren {
font-size: larger;
}
.sig-param.n {
font-style: italic;
}
/* C++ specific styling */
.sig-inline.c-texpr,
.sig-inline.cpp-texpr {
font-family: unset;
}
.sig.c .k, .sig.c .kt,
.sig.cpp .k, .sig.cpp .kt {
color: #0033B3;
}
.sig.c .m,
.sig.cpp .m {
color: #1750EB;
}
.sig.c .s, .sig.c .sc,
.sig.cpp .s, .sig.cpp .sc {
color: #067D17;
}
/* -- other body styles ----------------------------------------------------- */ /* -- other body styles ----------------------------------------------------- */
@ -634,14 +691,6 @@ dl.glossary dt {
font-size: 1.1em; font-size: 1.1em;
} }
.optional {
font-size: 1.3em;
}
.sig-paren {
font-size: larger;
}
.versionmodified { .versionmodified {
font-style: italic; font-style: italic;
} }
@ -786,16 +835,6 @@ div.literal-block-wrapper {
margin: 1em 0; margin: 1em 0;
} }
code.descname {
background-color: transparent;
font-weight: bold;
font-size: 1.2em;
}
code.descclassname {
background-color: transparent;
}
code.xref, a code { code.xref, a code {
background-color: transparent; background-color: transparent;
font-weight: bold; font-weight: bold;

View File

@ -206,13 +206,9 @@ class OnlyNodeTransform(SphinxPostTransform):
class SigElementFallbackTransform(SphinxPostTransform): class SigElementFallbackTransform(SphinxPostTransform):
"""Fallback desc_sig_element nodes to inline if translator does not supported them.""" """Fallback various desc_* nodes to inline if translator does not supported them."""
default_priority = 200 default_priority = 200
SIG_ELEMENTS = [addnodes.desc_sig_name,
addnodes.desc_sig_operator,
addnodes.desc_sig_punctuation]
def run(self, **kwargs: Any) -> None: def run(self, **kwargs: Any) -> None:
def has_visitor(translator: Type[nodes.NodeVisitor], node: Type[Element]) -> bool: def has_visitor(translator: Type[nodes.NodeVisitor], node: Type[Element]) -> bool:
return hasattr(translator, "visit_%s" % node.__name__) return hasattr(translator, "visit_%s" % node.__name__)
@ -222,24 +218,35 @@ class SigElementFallbackTransform(SphinxPostTransform):
# subclass of SphinxTranslator supports desc_sig_element nodes automatically. # subclass of SphinxTranslator supports desc_sig_element nodes automatically.
return return
if all(has_visitor(translator, node) for node in self.SIG_ELEMENTS): # for the leaf elements (desc_sig_element), the translator should support _all_
# the translator supports all desc_sig_element nodes if not all(has_visitor(translator, node) for node in addnodes.SIG_ELEMENTS):
return self.fallback(addnodes.desc_sig_element)
else:
self.fallback()
def fallback(self) -> None: if not has_visitor(translator, addnodes.desc_inline):
for node in self.document.traverse(addnodes.desc_sig_element): self.fallback(addnodes.desc_inline)
def fallback(self, nodeType: Any) -> None:
for node in self.document.traverse(nodeType):
newnode = nodes.inline() newnode = nodes.inline()
newnode.update_all_atts(node) newnode.update_all_atts(node)
newnode.extend(node) newnode.extend(node)
node.replace_self(newnode) node.replace_self(newnode)
class PropagateDescDomain(SphinxPostTransform):
"""Add the domain name of the parent node as a class in each desc_signature node."""
default_priority = 200
def run(self, **kwargs: Any) -> None:
for node in self.document.traverse(addnodes.desc_signature):
node['classes'].append(node.parent['domain'])
def setup(app: Sphinx) -> Dict[str, Any]: def setup(app: Sphinx) -> Dict[str, Any]:
app.add_post_transform(ReferencesResolver) app.add_post_transform(ReferencesResolver)
app.add_post_transform(OnlyNodeTransform) app.add_post_transform(OnlyNodeTransform)
app.add_post_transform(SigElementFallbackTransform) app.add_post_transform(SigElementFallbackTransform)
app.add_post_transform(PropagateDescDomain)
return { return {
'version': 'builtin', 'version': 'builtin',

View File

@ -197,7 +197,7 @@ class ImageConverter(BaseImageConverter):
def match(self, node: nodes.image) -> bool: def match(self, node: nodes.image) -> bool:
if not self.app.builder.supported_image_types: if not self.app.builder.supported_image_types:
return False return False
elif set(node['candidates']) & set(self.app.builder.supported_image_types): elif set(self.guess_mimetypes(node)) & set(self.app.builder.supported_image_types):
# builder supports the image; no need to convert # builder supports the image; no need to convert
return False return False
elif self.available is None: elif self.available is None:

View File

@ -138,16 +138,16 @@ def _restify_py37(cls: Optional[Type]) -> str:
if len(cls.__args__) > 1 and cls.__args__[-1] is NoneType: if len(cls.__args__) > 1 and cls.__args__[-1] is NoneType:
if len(cls.__args__) > 2: if len(cls.__args__) > 2:
args = ', '.join(restify(a) for a in cls.__args__[:-1]) args = ', '.join(restify(a) for a in cls.__args__[:-1])
return ':obj:`Optional`\\ [:obj:`Union`\\ [%s]]' % args return ':obj:`~typing.Optional`\\ [:obj:`~typing.Union`\\ [%s]]' % args
else: else:
return ':obj:`Optional`\\ [%s]' % restify(cls.__args__[0]) return ':obj:`~typing.Optional`\\ [%s]' % restify(cls.__args__[0])
else: else:
args = ', '.join(restify(a) for a in cls.__args__) args = ', '.join(restify(a) for a in cls.__args__)
return ':obj:`Union`\\ [%s]' % args return ':obj:`~typing.Union`\\ [%s]' % args
elif inspect.isgenericalias(cls): elif inspect.isgenericalias(cls):
if getattr(cls, '_name', None): if getattr(cls, '_name', None):
if cls.__module__ == 'typing': if cls.__module__ == 'typing':
text = ':class:`%s`' % cls._name text = ':class:`~%s.%s`' % (cls.__module__, cls._name)
else: else:
text = ':class:`%s.%s`' % (cls.__module__, cls._name) text = ':class:`%s.%s`' % (cls.__module__, cls._name)
else: else:
@ -167,20 +167,23 @@ def _restify_py37(cls: Optional[Type]) -> str:
return text return text
elif hasattr(cls, '__qualname__'): elif hasattr(cls, '__qualname__'):
if cls.__module__ == 'typing': if cls.__module__ == 'typing':
return ':class:`%s`' % cls.__qualname__ return ':class:`~%s.%s`' % (cls.__module__, cls.__qualname__)
else: else:
return ':class:`%s.%s`' % (cls.__module__, cls.__qualname__) return ':class:`%s.%s`' % (cls.__module__, cls.__qualname__)
elif hasattr(cls, '_name'): elif hasattr(cls, '_name'):
# SpecialForm # SpecialForm
if cls.__module__ == 'typing': if cls.__module__ == 'typing':
return ':obj:`%s`' % cls._name return ':obj:`~%s.%s`' % (cls.__module__, cls._name)
else: else:
return ':obj:`%s.%s`' % (cls.__module__, cls._name) return ':obj:`%s.%s`' % (cls.__module__, cls._name)
elif isinstance(cls, ForwardRef): elif isinstance(cls, ForwardRef):
return ':class:`%s`' % cls.__forward_arg__ return ':class:`%s`' % cls.__forward_arg__
else: else:
# not a class (ex. TypeVar) # not a class (ex. TypeVar)
return ':obj:`%s.%s`' % (cls.__module__, cls.__name__) if cls.__module__ == 'typing':
return ':obj:`~%s.%s`' % (cls.__module__, cls.__name__)
else:
return ':obj:`%s.%s`' % (cls.__module__, cls.__name__)
def _restify_py36(cls: Optional[Type]) -> str: def _restify_py36(cls: Optional[Type]) -> str:
@ -203,13 +206,23 @@ def _restify_py36(cls: Optional[Type]) -> str:
if (isinstance(cls, typing.TupleMeta) and # type: ignore if (isinstance(cls, typing.TupleMeta) and # type: ignore
not hasattr(cls, '__tuple_params__')): not hasattr(cls, '__tuple_params__')):
if module == 'typing':
reftext = ':class:`~typing.%s`' % qualname
else:
reftext = ':class:`%s`' % qualname
params = cls.__args__ params = cls.__args__
if params: if params:
param_str = ', '.join(restify(p) for p in params) param_str = ', '.join(restify(p) for p in params)
return ':class:`%s`\\ [%s]' % (qualname, param_str) return reftext + '\\ [%s]' % param_str
else: else:
return ':class:`%s`' % qualname return reftext
elif isinstance(cls, typing.GenericMeta): elif isinstance(cls, typing.GenericMeta):
if module == 'typing':
reftext = ':class:`~typing.%s`' % qualname
else:
reftext = ':class:`%s`' % qualname
if cls.__args__ is None or len(cls.__args__) <= 2: # type: ignore # NOQA if cls.__args__ is None or len(cls.__args__) <= 2: # type: ignore # NOQA
params = cls.__args__ # type: ignore params = cls.__args__ # type: ignore
elif cls.__origin__ == Generator: # type: ignore elif cls.__origin__ == Generator: # type: ignore
@ -217,13 +230,13 @@ def _restify_py36(cls: Optional[Type]) -> str:
else: # typing.Callable else: # typing.Callable
args = ', '.join(restify(arg) for arg in cls.__args__[:-1]) # type: ignore args = ', '.join(restify(arg) for arg in cls.__args__[:-1]) # type: ignore
result = restify(cls.__args__[-1]) # type: ignore result = restify(cls.__args__[-1]) # type: ignore
return ':class:`%s`\\ [[%s], %s]' % (qualname, args, result) return reftext + '\\ [[%s], %s]' % (args, result)
if params: if params:
param_str = ', '.join(restify(p) for p in params) param_str = ', '.join(restify(p) for p in params)
return ':class:`%s`\\ [%s]' % (qualname, param_str) return reftext + '\\ [%s]' % (param_str)
else: else:
return ':class:`%s`' % qualname return reftext
elif (hasattr(cls, '__origin__') and elif (hasattr(cls, '__origin__') and
cls.__origin__ is typing.Union): cls.__origin__ is typing.Union):
params = cls.__args__ params = cls.__args__
@ -231,32 +244,36 @@ def _restify_py36(cls: Optional[Type]) -> str:
if len(params) > 1 and params[-1] is NoneType: if len(params) > 1 and params[-1] is NoneType:
if len(params) > 2: if len(params) > 2:
param_str = ", ".join(restify(p) for p in params[:-1]) param_str = ", ".join(restify(p) for p in params[:-1])
return ':obj:`Optional`\\ [:obj:`Union`\\ [%s]]' % param_str return (':obj:`~typing.Optional`\\ '
'[:obj:`~typing.Union`\\ [%s]]' % param_str)
else: else:
return ':obj:`Optional`\\ [%s]' % restify(params[0]) return ':obj:`~typing.Optional`\\ [%s]' % restify(params[0])
else: else:
param_str = ', '.join(restify(p) for p in params) param_str = ', '.join(restify(p) for p in params)
return ':obj:`Union`\\ [%s]' % param_str return ':obj:`~typing.Union`\\ [%s]' % param_str
else: else:
return ':obj:`Union`' return ':obj:`Union`'
elif hasattr(cls, '__qualname__'): elif hasattr(cls, '__qualname__'):
if cls.__module__ == 'typing': if cls.__module__ == 'typing':
return ':class:`%s`' % cls.__qualname__ return ':class:`~%s.%s`' % (cls.__module__, cls.__qualname__)
else: else:
return ':class:`%s.%s`' % (cls.__module__, cls.__qualname__) return ':class:`%s.%s`' % (cls.__module__, cls.__qualname__)
elif hasattr(cls, '_name'): elif hasattr(cls, '_name'):
# SpecialForm # SpecialForm
if cls.__module__ == 'typing': if cls.__module__ == 'typing':
return ':obj:`%s`' % cls._name return ':obj:`~%s.%s`' % (cls.__module__, cls._name)
else: else:
return ':obj:`%s.%s`' % (cls.__module__, cls._name) return ':obj:`%s.%s`' % (cls.__module__, cls._name)
elif hasattr(cls, '__name__'): elif hasattr(cls, '__name__'):
# not a class (ex. TypeVar) # not a class (ex. TypeVar)
return ':obj:`%s.%s`' % (cls.__module__, cls.__name__) if cls.__module__ == 'typing':
return ':obj:`~%s.%s`' % (cls.__module__, cls.__name__)
else:
return ':obj:`%s.%s`' % (cls.__module__, cls.__name__)
else: else:
# others (ex. Any) # others (ex. Any)
if cls.__module__ == 'typing': if cls.__module__ == 'typing':
return ':obj:`%s`' % qualname return ':obj:`~%s.%s`' % (cls.__module__, qualname)
else: else:
return ':obj:`%s.%s`' % (cls.__module__, qualname) return ':obj:`%s.%s`' % (cls.__module__, qualname)

View File

@ -107,8 +107,15 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
def depart_start_of_file(self, node: Element) -> None: def depart_start_of_file(self, node: Element) -> None:
self.docnames.pop() self.docnames.pop()
#############################################################
# Domain-specific object descriptions
#############################################################
# Top-level nodes for descriptions
##################################
def visit_desc(self, node: Element) -> None: def visit_desc(self, node: Element) -> None:
self.body.append(self.starttag(node, 'dl', CLASS=node['objtype'])) self.body.append(self.starttag(node, 'dl'))
def depart_desc(self, node: Element) -> None: def depart_desc(self, node: Element) -> None:
self.body.append('</dl>\n\n') self.body.append('</dl>\n\n')
@ -133,8 +140,29 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
self.add_permalink_ref(node.parent, _('Permalink to this definition')) self.add_permalink_ref(node.parent, _('Permalink to this definition'))
self.body.append('<br />') self.body.append('<br />')
def visit_desc_content(self, node: Element) -> None:
self.body.append(self.starttag(node, 'dd', ''))
def depart_desc_content(self, node: Element) -> None:
self.body.append('</dd>')
def visit_desc_inline(self, node: Element) -> None:
self.body.append(self.starttag(node, 'span', ''))
def depart_desc_inline(self, node: Element) -> None:
self.body.append('</span>')
# Nodes for high-level structure in signatures
##############################################
def visit_desc_name(self, node: Element) -> None:
self.body.append(self.starttag(node, 'code', ''))
def depart_desc_name(self, node: Element) -> None:
self.body.append('</code>')
def visit_desc_addname(self, node: Element) -> None: def visit_desc_addname(self, node: Element) -> None:
self.body.append(self.starttag(node, 'code', '', CLASS='descclassname')) self.body.append(self.starttag(node, 'code', ''))
def depart_desc_addname(self, node: Element) -> None: def depart_desc_addname(self, node: Element) -> None:
self.body.append('</code>') self.body.append('</code>')
@ -151,12 +179,6 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
def depart_desc_returns(self, node: Element) -> None: def depart_desc_returns(self, node: Element) -> None:
pass pass
def visit_desc_name(self, node: Element) -> None:
self.body.append(self.starttag(node, 'code', '', CLASS='descname'))
def depart_desc_name(self, node: Element) -> None:
self.body.append('</code>')
def visit_desc_parameterlist(self, node: Element) -> None: def visit_desc_parameterlist(self, node: Element) -> None:
self.body.append('<span class="sig-paren">(</span>') self.body.append('<span class="sig-paren">(</span>')
self.first_param = 1 self.first_param = 1
@ -205,11 +227,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
def depart_desc_annotation(self, node: Element) -> None: def depart_desc_annotation(self, node: Element) -> None:
self.body.append('</em>') self.body.append('</em>')
def visit_desc_content(self, node: Element) -> None: ##############################################
self.body.append(self.starttag(node, 'dd', ''))
def depart_desc_content(self, node: Element) -> None:
self.body.append('</dd>')
def visit_versionmodified(self, node: Element) -> None: def visit_versionmodified(self, node: Element) -> None:
self.body.append(self.starttag(node, 'div', CLASS=node['type'])) self.body.append(self.starttag(node, 'div', CLASS=node['type']))

View File

@ -78,8 +78,15 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
def depart_start_of_file(self, node: Element) -> None: def depart_start_of_file(self, node: Element) -> None:
self.docnames.pop() self.docnames.pop()
#############################################################
# Domain-specific object descriptions
#############################################################
# Top-level nodes for descriptions
##################################
def visit_desc(self, node: Element) -> None: def visit_desc(self, node: Element) -> None:
self.body.append(self.starttag(node, 'dl', CLASS=node['objtype'])) self.body.append(self.starttag(node, 'dl'))
def depart_desc(self, node: Element) -> None: def depart_desc(self, node: Element) -> None:
self.body.append('</dl>\n\n') self.body.append('</dl>\n\n')
@ -104,11 +111,32 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
self.add_permalink_ref(node.parent, _('Permalink to this definition')) self.add_permalink_ref(node.parent, _('Permalink to this definition'))
self.body.append('<br />') self.body.append('<br />')
def visit_desc_content(self, node: Element) -> None:
self.body.append(self.starttag(node, 'dd', ''))
def depart_desc_content(self, node: Element) -> None:
self.body.append('</dd>')
def visit_desc_inline(self, node: Element) -> None:
self.body.append(self.starttag(node, 'span', ''))
def depart_desc_inline(self, node: Element) -> None:
self.body.append('</span>')
# Nodes for high-level structure in signatures
##############################################
def visit_desc_name(self, node: Element) -> None:
self.body.append(self.starttag(node, 'span', ''))
def depart_desc_name(self, node: Element) -> None:
self.body.append('</span>')
def visit_desc_addname(self, node: Element) -> None: def visit_desc_addname(self, node: Element) -> None:
self.body.append(self.starttag(node, 'code', '', CLASS='sig-prename descclassname')) self.body.append(self.starttag(node, 'span', ''))
def depart_desc_addname(self, node: Element) -> None: def depart_desc_addname(self, node: Element) -> None:
self.body.append('</code>') self.body.append('</span>')
def visit_desc_type(self, node: Element) -> None: def visit_desc_type(self, node: Element) -> None:
pass pass
@ -122,12 +150,6 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
def depart_desc_returns(self, node: Element) -> None: def depart_desc_returns(self, node: Element) -> None:
pass pass
def visit_desc_name(self, node: Element) -> None:
self.body.append(self.starttag(node, 'code', '', CLASS='sig-name descname'))
def depart_desc_name(self, node: Element) -> None:
self.body.append('</code>')
def visit_desc_parameterlist(self, node: Element) -> None: def visit_desc_parameterlist(self, node: Element) -> None:
self.body.append('<span class="sig-paren">(</span>') self.body.append('<span class="sig-paren">(</span>')
self.first_param = 1 self.first_param = 1
@ -176,11 +198,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
def depart_desc_annotation(self, node: Element) -> None: def depart_desc_annotation(self, node: Element) -> None:
self.body.append('</em>') self.body.append('</em>')
def visit_desc_content(self, node: Element) -> None: ##############################################
self.body.append(self.starttag(node, 'dd', ''))
def depart_desc_content(self, node: Element) -> None:
self.body.append('</dd>')
def visit_versionmodified(self, node: Element) -> None: def visit_versionmodified(self, node: Element) -> None:
self.body.append(self.starttag(node, 'div', CLASS=node['type'])) self.body.append(self.starttag(node, 'div', CLASS=node['type']))

View File

@ -698,6 +698,13 @@ class LaTeXTranslator(SphinxTranslator):
def depart_subtitle(self, node: Element) -> None: def depart_subtitle(self, node: Element) -> None:
self.body.append(self.context.pop()) self.body.append(self.context.pop())
#############################################################
# Domain-specific object descriptions
#############################################################
# Top-level nodes for descriptions
##################################
def visit_desc(self, node: Element) -> None: def visit_desc(self, node: Element) -> None:
if self.config.latex_show_urls == 'footnote': if self.config.latex_show_urls == 'footnote':
self.body.append(BLANKLINE) self.body.append(BLANKLINE)
@ -750,6 +757,31 @@ class LaTeXTranslator(SphinxTranslator):
def depart_desc_signature_line(self, node: Element) -> None: def depart_desc_signature_line(self, node: Element) -> None:
self._depart_signature_line(node) self._depart_signature_line(node)
def visit_desc_content(self, node: Element) -> None:
if node.children and not isinstance(node.children[0], nodes.paragraph):
# avoid empty desc environment which causes a formatting bug
self.body.append('~')
def depart_desc_content(self, node: Element) -> None:
pass
def visit_desc_inline(self, node: Element) -> None:
self.body.append(r'\sphinxcode{\sphinxupquote{')
def depart_desc_inline(self, node: Element) -> None:
self.body.append('}}')
# Nodes for high-level structure in signatures
##############################################
def visit_desc_name(self, node: Element) -> None:
self.body.append(r'\sphinxbfcode{\sphinxupquote{')
self.literal_whitespace += 1
def depart_desc_name(self, node: Element) -> None:
self.body.append('}}')
self.literal_whitespace -= 1
def visit_desc_addname(self, node: Element) -> None: def visit_desc_addname(self, node: Element) -> None:
self.body.append(r'\sphinxcode{\sphinxupquote{') self.body.append(r'\sphinxcode{\sphinxupquote{')
self.literal_whitespace += 1 self.literal_whitespace += 1
@ -770,14 +802,6 @@ class LaTeXTranslator(SphinxTranslator):
def depart_desc_returns(self, node: Element) -> None: def depart_desc_returns(self, node: Element) -> None:
self.body.append(r'}') self.body.append(r'}')
def visit_desc_name(self, node: Element) -> None:
self.body.append(r'\sphinxbfcode{\sphinxupquote{')
self.literal_whitespace += 1
def depart_desc_name(self, node: Element) -> None:
self.body.append('}}')
self.literal_whitespace -= 1
def visit_desc_parameterlist(self, node: Element) -> None: def visit_desc_parameterlist(self, node: Element) -> None:
# close name, open parameterlist # close name, open parameterlist
self.body.append('}{') self.body.append('}{')
@ -811,13 +835,7 @@ class LaTeXTranslator(SphinxTranslator):
def depart_desc_annotation(self, node: Element) -> None: def depart_desc_annotation(self, node: Element) -> None:
self.body.append('}}') self.body.append('}}')
def visit_desc_content(self, node: Element) -> None: ##############################################
if node.children and not isinstance(node.children[0], nodes.paragraph):
# avoid empty desc environment which causes a formatting bug
self.body.append('~')
def depart_desc_content(self, node: Element) -> None:
pass
def visit_seealso(self, node: Element) -> None: def visit_seealso(self, node: Element) -> None:
self.body.append(BLANKLINE) self.body.append(BLANKLINE)

View File

@ -120,6 +120,13 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator):
def depart_start_of_file(self, node: Element) -> None: def depart_start_of_file(self, node: Element) -> None:
pass pass
#############################################################
# Domain-specific object descriptions
#############################################################
# Top-level nodes for descriptions
##################################
def visit_desc(self, node: Element) -> None: def visit_desc(self, node: Element) -> None:
self.visit_definition_list(node) self.visit_definition_list(node)
@ -139,6 +146,27 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator):
def depart_desc_signature_line(self, node: Element) -> None: def depart_desc_signature_line(self, node: Element) -> None:
self.body.append(' ') self.body.append(' ')
def visit_desc_content(self, node: Element) -> None:
self.visit_definition(node)
def depart_desc_content(self, node: Element) -> None:
self.depart_definition(node)
def visit_desc_inline(self, node: Element) -> None:
pass
def depart_desc_inline(self, node: Element) -> None:
pass
# Nodes for high-level structure in signatures
##############################################
def visit_desc_name(self, node: Element) -> None:
pass
def depart_desc_name(self, node: Element) -> None:
pass
def visit_desc_addname(self, node: Element) -> None: def visit_desc_addname(self, node: Element) -> None:
pass pass
@ -157,12 +185,6 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator):
def depart_desc_returns(self, node: Element) -> None: def depart_desc_returns(self, node: Element) -> None:
pass pass
def visit_desc_name(self, node: Element) -> None:
pass
def depart_desc_name(self, node: Element) -> None:
pass
def visit_desc_parameterlist(self, node: Element) -> None: def visit_desc_parameterlist(self, node: Element) -> None:
self.body.append('(') self.body.append('(')
self.first_param = 1 self.first_param = 1
@ -191,11 +213,7 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator):
def depart_desc_annotation(self, node: Element) -> None: def depart_desc_annotation(self, node: Element) -> None:
pass pass
def visit_desc_content(self, node: Element) -> None: ##############################################
self.visit_definition(node)
def depart_desc_content(self, node: Element) -> None:
self.depart_definition(node)
def visit_versionmodified(self, node: Element) -> None: def visit_versionmodified(self, node: Element) -> None:
self.visit_paragraph(node) self.visit_paragraph(node)

View File

@ -1367,7 +1367,12 @@ class TexinfoTranslator(SphinxTranslator):
self.body.append('\n\n') self.body.append('\n\n')
raise nodes.SkipNode raise nodes.SkipNode
# -- Desc #############################################################
# Domain-specific object descriptions
#############################################################
# Top-level nodes for descriptions
##################################
def visit_desc(self, node: addnodes.desc) -> None: def visit_desc(self, node: addnodes.desc) -> None:
self.descs.append(node) self.descs.append(node)
@ -1408,6 +1413,21 @@ class TexinfoTranslator(SphinxTranslator):
def depart_desc_signature_line(self, node: Element) -> None: def depart_desc_signature_line(self, node: Element) -> None:
pass pass
def visit_desc_content(self, node: Element) -> None:
pass
def depart_desc_content(self, node: Element) -> None:
pass
def visit_desc_inline(self, node: Element) -> None:
pass
def depart_desc_inline(self, node: Element) -> None:
pass
# Nodes for high-level structure in signatures
##############################################
def visit_desc_name(self, node: Element) -> None: def visit_desc_name(self, node: Element) -> None:
pass pass
@ -1470,11 +1490,7 @@ class TexinfoTranslator(SphinxTranslator):
def depart_desc_annotation(self, node: Element) -> None: def depart_desc_annotation(self, node: Element) -> None:
pass pass
def visit_desc_content(self, node: Element) -> None: ##############################################
pass
def depart_desc_content(self, node: Element) -> None:
pass
def visit_inline(self, node: Element) -> None: def visit_inline(self, node: Element) -> None:
pass pass

View File

@ -536,6 +536,13 @@ class TextTranslator(SphinxTranslator):
def depart_attribution(self, node: Element) -> None: def depart_attribution(self, node: Element) -> None:
pass pass
#############################################################
# Domain-specific object descriptions
#############################################################
# Top-level nodes
#################
def visit_desc(self, node: Element) -> None: def visit_desc(self, node: Element) -> None:
pass pass
@ -555,6 +562,22 @@ class TextTranslator(SphinxTranslator):
def depart_desc_signature_line(self, node: Element) -> None: def depart_desc_signature_line(self, node: Element) -> None:
self.add_text('\n') self.add_text('\n')
def visit_desc_content(self, node: Element) -> None:
self.new_state()
self.add_text(self.nl)
def depart_desc_content(self, node: Element) -> None:
self.end_state()
def visit_desc_inline(self, node: Element) -> None:
pass
def depart_desc_inline(self, node: Element) -> None:
pass
# Nodes for high-level structure in signatures
##############################################
def visit_desc_name(self, node: Element) -> None: def visit_desc_name(self, node: Element) -> None:
pass pass
@ -606,12 +629,7 @@ class TextTranslator(SphinxTranslator):
def depart_desc_annotation(self, node: Element) -> None: def depart_desc_annotation(self, node: Element) -> None:
pass pass
def visit_desc_content(self, node: Element) -> None: ##############################################
self.new_state()
self.add_text(self.nl)
def depart_desc_content(self, node: Element) -> None:
self.end_state()
def visit_figure(self, node: Element) -> None: def visit_figure(self, node: Element) -> None:
self.new_state() self.new_state()

Binary file not shown.

View File

@ -2,3 +2,4 @@ test-ext-imgconverter
===================== =====================
.. image:: svgimg.svg .. image:: svgimg.svg
.. image:: img.pdf

View File

@ -213,3 +213,9 @@ CPP domain
.. cpp:function:: T& operator[]( unsigned j ) .. cpp:function:: T& operator[]( unsigned j )
const T& operator[]( unsigned j ) const const T& operator[]( unsigned j ) const
.. cpp:function:: template<typename T1, typename T2> \
requires A<T1, T2> \
void f()
- :cpp:expr:`a + b`

View File

@ -285,10 +285,10 @@ def test_html4_output(app, status, warning):
'objects.html': [ 'objects.html': [
(".//dt[@id='mod.Cls.meth1']", ''), (".//dt[@id='mod.Cls.meth1']", ''),
(".//dt[@id='errmod.Error']", ''), (".//dt[@id='errmod.Error']", ''),
(".//dt/code/span", r'long\(parameter,'), (".//dt/span[@class='sig-name descname']/span[@class='pre']", r'long\(parameter,'),
(".//dt/code/span", r'list\)'), (".//dt/span[@class='sig-name descname']/span[@class='pre']", r'list\)'),
(".//dt/code/span", 'another'), (".//dt/span[@class='sig-name descname']/span[@class='pre']", 'another'),
(".//dt/code/span", 'one'), (".//dt/span[@class='sig-name descname']/span[@class='pre']", 'one'),
(".//a[@href='#mod.Cls'][@class='reference internal']", ''), (".//a[@href='#mod.Cls'][@class='reference internal']", ''),
(".//dl[@class='std userdesc']", ''), (".//dl[@class='std userdesc']", ''),
(".//dt[@id='userdesc-myobj']", ''), (".//dt[@id='userdesc-myobj']", ''),

View File

@ -73,6 +73,7 @@ def _check(name, input, idDict, output, key, asTextOutput):
print("Input: ", input) print("Input: ", input)
print("astext(): ", resAsText) print("astext(): ", resAsText)
print("Expected: ", outputAsText) print("Expected: ", outputAsText)
print("Node:", parentNode)
raise DefinitionError("") raise DefinitionError("")
idExpected = [None] idExpected = [None]
@ -743,6 +744,9 @@ def test_anon_definitions():
check('class', '@1', {3: "Ut1_1"}, asTextOutput='class [anonymous]') check('class', '@1', {3: "Ut1_1"}, asTextOutput='class [anonymous]')
check('class', '@a::A', {3: "NUt1_a1AE"}, asTextOutput='class [anonymous]::A') check('class', '@a::A', {3: "NUt1_a1AE"}, asTextOutput='class [anonymous]::A')
check('function', 'int f(int @a)', {1: 'f__i', 2: '1fi'},
asTextOutput='int f(int [anonymous])')
def test_templates(): def test_templates():
check('class', "A<T>", {2: "IE1AI1TE"}, output="template<> {key}A<T>") check('class', "A<T>", {2: "IE1AI1TE"}, output="template<> {key}A<T>")
@ -1206,7 +1210,7 @@ not found in `{test}`
cpp_any_role = RoleClasses('cpp-any', 'a', ['code']) cpp_any_role = RoleClasses('cpp-any', 'a', ['code'])
# NYI: consistent looks # NYI: consistent looks
# texpr_role = RoleClasses('cpp-texpr', 'span', ['a', 'code']) # texpr_role = RoleClasses('cpp-texpr', 'span', ['a', 'code'])
expr_role = RoleClasses('cpp-expr', 'code', ['a']) expr_role = RoleClasses('cpp-expr', 'span', ['a'])
texpr_role = RoleClasses('cpp-texpr', 'span', ['a', 'span']) texpr_role = RoleClasses('cpp-texpr', 'span', ['a', 'span'])
# XRefRole-style classes # XRefRole-style classes
@ -1223,8 +1227,7 @@ not found in `{test}`
for role in (expr_role, texpr_role): for role in (expr_role, texpr_role):
name = role.name name = role.name
expect = '`{name}` puts the domain and role classes at its root'.format(name=name) expect = '`{name}` puts the domain and role classes at its root'.format(name=name)
# NYI: xref should go in the references assert {'sig', 'sig-inline', 'cpp', name} <= role.classes, expect
assert {'xref', 'cpp', name} <= role.classes, expect
# reference classes # reference classes

View File

@ -876,7 +876,9 @@ def test_info_field_list(app):
"\n" "\n"
" :param str name: blah blah\n" " :param str name: blah blah\n"
" :param age: blah blah\n" " :param age: blah blah\n"
" :type age: int\n") " :type age: int\n"
" :param items: blah blah\n"
" :type items: Tuple[str, ...]\n")
doctree = restructuredtext.parse(app, text) doctree = restructuredtext.parse(app, text)
print(doctree) print(doctree)
@ -890,6 +892,7 @@ def test_info_field_list(app):
assert_node(doctree[3][1][0][0], assert_node(doctree[3][1][0][0],
([nodes.field_name, "Parameters"], ([nodes.field_name, "Parameters"],
[nodes.field_body, nodes.bullet_list, ([nodes.list_item, nodes.paragraph], [nodes.field_body, nodes.bullet_list, ([nodes.list_item, nodes.paragraph],
[nodes.list_item, nodes.paragraph],
[nodes.list_item, nodes.paragraph])])) [nodes.list_item, nodes.paragraph])]))
# :param str name: # :param str name:
@ -916,6 +919,26 @@ def test_info_field_list(app):
refdomain="py", reftype="class", reftarget="int", refdomain="py", reftype="class", reftarget="int",
**{"py:module": "example", "py:class": "Class"}) **{"py:module": "example", "py:class": "Class"})
# :param items: + :type items:
assert_node(doctree[3][1][0][0][1][0][2][0],
([addnodes.literal_strong, "items"],
" (",
[pending_xref, addnodes.literal_emphasis, "Tuple"],
[addnodes.literal_emphasis, "["],
[pending_xref, addnodes.literal_emphasis, "str"],
[addnodes.literal_emphasis, ", "],
[addnodes.literal_emphasis, "..."],
[addnodes.literal_emphasis, "]"],
")",
" -- ",
"blah blah"))
assert_node(doctree[3][1][0][0][1][0][2][0][2], pending_xref,
refdomain="py", reftype="class", reftarget="Tuple",
**{"py:module": "example", "py:class": "Class"})
assert_node(doctree[3][1][0][0][1][0][2][0][4], pending_xref,
refdomain="py", reftype="class", reftarget="str",
**{"py:module": "example", "py:class": "Class"})
def test_info_field_list_var(app): def test_info_field_list_var(app):
text = (".. py:class:: Class\n" text = (".. py:class:: Class\n"

View File

@ -1893,12 +1893,12 @@ def test_autodoc_GenericAlias(app):
' .. py:attribute:: Class.T', ' .. py:attribute:: Class.T',
' :module: target.genericalias', ' :module: target.genericalias',
'', '',
' alias of :class:`List`\\ [:class:`int`]', ' alias of :class:`~typing.List`\\ [:class:`int`]',
'', '',
'.. py:attribute:: T', '.. py:attribute:: T',
' :module: target.genericalias', ' :module: target.genericalias',
'', '',
' alias of :class:`List`\\ [:class:`int`]', ' alias of :class:`~typing.List`\\ [:class:`int`]',
] ]
else: else:
assert list(actual) == [ assert list(actual) == [

View File

@ -256,7 +256,8 @@ def test_show_inheritance_for_subclass_of_generic_type(app):
'.. py:class:: Quux(iterable=(), /)', '.. py:class:: Quux(iterable=(), /)',
' :module: target.classes', ' :module: target.classes',
'', '',
' Bases: :class:`List`\\ [:obj:`Union`\\ [:class:`int`, :class:`float`]]', ' Bases: :class:`~typing.List`\\ '
'[:obj:`~typing.Union`\\ [:class:`int`, :class:`float`]]',
'', '',
' A subclass of List[Union[int, float]]', ' A subclass of List[Union[int, float]]',
'', '',

View File

@ -19,6 +19,11 @@ def test_ext_imgconverter(app, status, warning):
app.builder.build_all() app.builder.build_all()
content = (app.outdir / 'python.tex').read_text() content = (app.outdir / 'python.tex').read_text()
# supported image (not converted)
assert '\\sphinxincludegraphics{{img}.pdf}' in content
# non supported image (converted)
assert '\\sphinxincludegraphics{{svgimg}.png}' in content assert '\\sphinxincludegraphics{{svgimg}.png}' in content
assert not (app.outdir / 'svgimg.svg').exists() assert not (app.outdir / 'svgimg.svg').exists()
assert (app.outdir / 'svgimg.png').exists() assert (app.outdir / 'svgimg.png').exists()

View File

@ -258,10 +258,10 @@ def test_missing_reference_cppdomain(tempdir, app, status, warning):
'<span class="pre">Bar</span></code></a>' in html) '<span class="pre">Bar</span></code></a>' in html)
assert ('<a class="reference external"' assert ('<a class="reference external"'
' href="https://docs.python.org/index.html#foons"' ' href="https://docs.python.org/index.html#foons"'
' title="(in foo v2.0)"><span class="pre">foons</span></a>' in html) ' title="(in foo v2.0)"><span class="n"><span class="pre">foons</span></span></a>' in html)
assert ('<a class="reference external"' assert ('<a class="reference external"'
' href="https://docs.python.org/index.html#foons_bartype"' ' href="https://docs.python.org/index.html#foons_bartype"'
' title="(in foo v2.0)"><span class="pre">bartype</span></a>' in html) ' title="(in foo v2.0)"><span class="n"><span class="pre">bartype</span></span></a>' in html)
def test_missing_reference_jsdomain(tempdir, app, status, warning): def test_missing_reference_jsdomain(tempdir, app, status, warning):

View File

@ -47,42 +47,56 @@ def test_restify():
assert restify(Integral) == ":class:`numbers.Integral`" assert restify(Integral) == ":class:`numbers.Integral`"
assert restify(Struct) == ":class:`struct.Struct`" assert restify(Struct) == ":class:`struct.Struct`"
assert restify(TracebackType) == ":class:`types.TracebackType`" assert restify(TracebackType) == ":class:`types.TracebackType`"
assert restify(Any) == ":obj:`Any`" assert restify(Any) == ":obj:`~typing.Any`"
def test_restify_type_hints_containers(): def test_restify_type_hints_containers():
assert restify(List) == ":class:`List`" assert restify(List) == ":class:`~typing.List`"
assert restify(Dict) == ":class:`Dict`" assert restify(Dict) == ":class:`~typing.Dict`"
assert restify(List[int]) == ":class:`List`\\ [:class:`int`]" assert restify(List[int]) == ":class:`~typing.List`\\ [:class:`int`]"
assert restify(List[str]) == ":class:`List`\\ [:class:`str`]" assert restify(List[str]) == ":class:`~typing.List`\\ [:class:`str`]"
assert restify(Dict[str, float]) == ":class:`Dict`\\ [:class:`str`, :class:`float`]" assert restify(Dict[str, float]) == (":class:`~typing.Dict`\\ "
assert restify(Tuple[str, str, str]) == ":class:`Tuple`\\ [:class:`str`, :class:`str`, :class:`str`]" "[:class:`str`, :class:`float`]")
assert restify(Tuple[str, ...]) == ":class:`Tuple`\\ [:class:`str`, ...]" assert restify(Tuple[str, str, str]) == (":class:`~typing.Tuple`\\ "
assert restify(List[Dict[str, Tuple]]) == ":class:`List`\\ [:class:`Dict`\\ [:class:`str`, :class:`Tuple`]]" "[:class:`str`, :class:`str`, :class:`str`]")
assert restify(MyList[Tuple[int, int]]) == ":class:`tests.test_util_typing.MyList`\\ [:class:`Tuple`\\ [:class:`int`, :class:`int`]]" assert restify(Tuple[str, ...]) == ":class:`~typing.Tuple`\\ [:class:`str`, ...]"
assert restify(Generator[None, None, None]) == ":class:`Generator`\\ [:obj:`None`, :obj:`None`, :obj:`None`]" assert restify(List[Dict[str, Tuple]]) == (":class:`~typing.List`\\ "
"[:class:`~typing.Dict`\\ "
"[:class:`str`, :class:`~typing.Tuple`]]")
assert restify(MyList[Tuple[int, int]]) == (":class:`tests.test_util_typing.MyList`\\ "
"[:class:`~typing.Tuple`\\ "
"[:class:`int`, :class:`int`]]")
assert restify(Generator[None, None, None]) == (":class:`~typing.Generator`\\ "
"[:obj:`None`, :obj:`None`, :obj:`None`]")
def test_restify_type_hints_Callable(): def test_restify_type_hints_Callable():
assert restify(Callable) == ":class:`Callable`" assert restify(Callable) == ":class:`~typing.Callable`"
if sys.version_info >= (3, 7): if sys.version_info >= (3, 7):
assert restify(Callable[[str], int]) == ":class:`Callable`\\ [[:class:`str`], :class:`int`]" assert restify(Callable[[str], int]) == (":class:`~typing.Callable`\\ "
assert restify(Callable[..., int]) == ":class:`Callable`\\ [[...], :class:`int`]" "[[:class:`str`], :class:`int`]")
assert restify(Callable[..., int]) == (":class:`~typing.Callable`\\ "
"[[...], :class:`int`]")
else: else:
assert restify(Callable[[str], int]) == ":class:`Callable`\\ [:class:`str`, :class:`int`]" assert restify(Callable[[str], int]) == (":class:`~typing.Callable`\\ "
assert restify(Callable[..., int]) == ":class:`Callable`\\ [..., :class:`int`]" "[:class:`str`, :class:`int`]")
assert restify(Callable[..., int]) == (":class:`~typing.Callable`\\ "
"[..., :class:`int`]")
def test_restify_type_hints_Union(): def test_restify_type_hints_Union():
assert restify(Optional[int]) == ":obj:`Optional`\\ [:class:`int`]" assert restify(Optional[int]) == ":obj:`~typing.Optional`\\ [:class:`int`]"
assert restify(Union[str, None]) == ":obj:`Optional`\\ [:class:`str`]" assert restify(Union[str, None]) == ":obj:`~typing.Optional`\\ [:class:`str`]"
assert restify(Union[int, str]) == ":obj:`Union`\\ [:class:`int`, :class:`str`]" assert restify(Union[int, str]) == ":obj:`~typing.Union`\\ [:class:`int`, :class:`str`]"
if sys.version_info >= (3, 7): if sys.version_info >= (3, 7):
assert restify(Union[int, Integral]) == ":obj:`Union`\\ [:class:`int`, :class:`numbers.Integral`]" assert restify(Union[int, Integral]) == (":obj:`~typing.Union`\\ "
"[:class:`int`, :class:`numbers.Integral`]")
assert (restify(Union[MyClass1, MyClass2]) == assert (restify(Union[MyClass1, MyClass2]) ==
":obj:`Union`\\ [:class:`tests.test_util_typing.MyClass1`, :class:`tests.test_util_typing.<MyClass2>`]") (":obj:`~typing.Union`\\ "
"[:class:`tests.test_util_typing.MyClass1`, "
":class:`tests.test_util_typing.<MyClass2>`]"))
else: else:
assert restify(Union[int, Integral]) == ":class:`numbers.Integral`" assert restify(Union[int, Integral]) == ":class:`numbers.Integral`"
assert restify(Union[MyClass1, MyClass2]) == ":class:`tests.test_util_typing.MyClass1`" assert restify(Union[MyClass1, MyClass2]) == ":class:`tests.test_util_typing.MyClass1`"
@ -97,7 +111,7 @@ def test_restify_type_hints_typevars():
assert restify(T) == ":obj:`tests.test_util_typing.T`" assert restify(T) == ":obj:`tests.test_util_typing.T`"
assert restify(T_co) == ":obj:`tests.test_util_typing.T_co`" assert restify(T_co) == ":obj:`tests.test_util_typing.T_co`"
assert restify(T_contra) == ":obj:`tests.test_util_typing.T_contra`" assert restify(T_contra) == ":obj:`tests.test_util_typing.T_contra`"
assert restify(List[T]) == ":class:`List`\\ [:obj:`tests.test_util_typing.T`]" assert restify(List[T]) == ":class:`~typing.List`\\ [:obj:`tests.test_util_typing.T`]"
assert restify(MyInt) == ":class:`MyInt`" assert restify(MyInt) == ":class:`MyInt`"
@ -110,7 +124,7 @@ def test_restify_type_hints_alias():
MyStr = str MyStr = str
MyTuple = Tuple[str, str] MyTuple = Tuple[str, str]
assert restify(MyStr) == ":class:`str`" assert restify(MyStr) == ":class:`str`"
assert restify(MyTuple) == ":class:`Tuple`\\ [:class:`str`, :class:`str`]" # type: ignore assert restify(MyTuple) == ":class:`~typing.Tuple`\\ [:class:`str`, :class:`str`]"
@pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.') @pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.')