Merge branch 'master' into 3606_load_mathjax_async

This commit is contained in:
Takeshi KOMIYA 2018-05-28 23:04:13 +09:00 committed by GitHub
commit 1deceda6da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 365 additions and 149 deletions

15
CHANGES
View File

@ -65,9 +65,14 @@ Deprecated
* ``sphinx.writers.latex.Table.caption_footnotetexts`` is deprecated * ``sphinx.writers.latex.Table.caption_footnotetexts`` is deprecated
* ``sphinx.writers.latex.Table.header_footnotetexts`` is deprecated * ``sphinx.writers.latex.Table.header_footnotetexts`` is deprecated
* ``sphinx.writers.latex.LaTeXWriter.footnotestack`` is deprecated * ``sphinx.writers.latex.LaTeXWriter.footnotestack`` is deprecated
* ``sphinx.writers.latex.LaTeXWriter.in_container_literal_block`` is deprecated
* ``sphinx.writers.latex.LaTeXWriter.next_section_ids`` is deprecated
* ``sphinx.writers.latex.LaTeXWriter.next_hyperlink_ids`` is deprecated
* ``sphinx.writers.latex.LaTeXWriter.restrict_footnote()`` is deprecated * ``sphinx.writers.latex.LaTeXWriter.restrict_footnote()`` is deprecated
* ``sphinx.writers.latex.LaTeXWriter.unrestrict_footnote()`` is deprecated * ``sphinx.writers.latex.LaTeXWriter.unrestrict_footnote()`` is deprecated
* ``LaTeXWriter.bibitems`` is deprecated * ``sphinx.writers.latex.LaTeXWriter.push_hyperlink_ids()`` is deprecated
* ``sphinx.writers.latex.LaTeXWriter.pop_hyperlink_ids()`` is deprecated
* ``sphinx.writers.latex.LaTeXWriter.bibitems`` is deprecated
* ``BuildEnvironment.load()`` is deprecated * ``BuildEnvironment.load()`` is deprecated
* ``BuildEnvironment.loads()`` is deprecated * ``BuildEnvironment.loads()`` is deprecated
* ``BuildEnvironment.frompickle()`` is deprecated * ``BuildEnvironment.frompickle()`` is deprecated
@ -117,6 +122,8 @@ Features added
* #4785: napoleon: Add strings to translation file for localisation * #4785: napoleon: Add strings to translation file for localisation
* #4927: Display a warning when invalid values are passed to linenothreshold * #4927: Display a warning when invalid values are passed to linenothreshold
option of highlight directive option of highlight directive
* C++, add a ``cpp:texpr`` role as a sibling to ``cpp:expr``.
* C++, add support for unions.
* #3606: MathJax should be loaded with async attribute * #3606: MathJax should be loaded with async attribute
Bugs fixed Bugs fixed
@ -175,6 +182,12 @@ Bugs fixed
mocked module mocked module
* #4973: latex: glossary directive adds whitespace to each item * #4973: latex: glossary directive adds whitespace to each item
* #4980: latex: Explicit labels on code blocks are duplicated * #4980: latex: Explicit labels on code blocks are duplicated
* #4919: node.asdom() crashes if toctree has :numbered: option
* #4914: autodoc: Parsing error when using dataclasses without default values
* #4931: autodoc: crashed when handler for autodoc-skip-member raises an error
* #4931: autodoc: crashed when subclass of mocked class are processed by
napoleon module
* #5007: sphinx-build crashes when error log contains a "%" character
Testing Testing
-------- --------

View File

@ -3,7 +3,7 @@
# You can set these variables from the command line. # You can set these variables from the command line.
SPHINXOPTS = SPHINXOPTS =
SPHINXBUILD = python ../sphinx/cmd/build.py SPHINXBUILD = python3 ../sphinx/cmd/build.py
SPHINXPROJ = sphinx SPHINXPROJ = sphinx
SOURCEDIR = . SOURCEDIR = .
BUILDDIR = _build BUILDDIR = _build

View File

@ -73,7 +73,7 @@ package.
.. automethod:: Sphinx.add_js_file(filename, **kwargs) .. automethod:: Sphinx.add_js_file(filename, **kwargs)
.. automethod:: Sphinx.add_stylesheet(filename, alternate=None, title=None) .. automethod:: Sphinx.add_css_file(filename, **kwargs)
.. automethod:: Sphinx.add_latex_package(packagename, options=None) .. automethod:: Sphinx.add_latex_package(packagename, options=None)

View File

@ -176,6 +176,21 @@ The following is a list of deprecated interface.
- 3.0 - 3.0
- N/A - N/A
* - ``sphinx.writers.latex.LaTeXWriter.in_container_literal_block``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXWriter.next_section_ids``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXWriter.next_hyperlink_ids``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXWriter.restrict_footnote()`` * - ``sphinx.writers.latex.LaTeXWriter.restrict_footnote()``
- 1.8 - 1.8
- 3.0 - 3.0
@ -186,6 +201,16 @@ The following is a list of deprecated interface.
- 3.0 - 3.0
- N/A - N/A
* - ``sphinx.writers.latex.LaTeXWriter.push_hyperlink_ids()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXWriter.pop_hyperlink_ids()``
- 1.8
- 3.0
- N/A
* - ``sphinx.writers.latex.LaTeXWriter.bibitems`` * - ``sphinx.writers.latex.LaTeXWriter.bibitems``
- 1.8 - 1.8
- 3.0 - 3.0

View File

@ -702,6 +702,10 @@ visibility statement (``public``, ``private`` or ``protected``).
.. cpp:enumerator:: MyEnum::myOtherEnumerator = 42 .. cpp:enumerator:: MyEnum::myOtherEnumerator = 42
.. rst:directive:: .. cpp:union:: name
Describe a union.
.. rst:directive:: .. cpp:concept:: template-parameter-list name .. rst:directive:: .. cpp:concept:: template-parameter-list name
.. warning:: The support for concepts is experimental. It is based on the .. warning:: The support for concepts is experimental. It is based on the
@ -815,15 +819,19 @@ Inline Expressions and Tpes
~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. rst:role:: cpp:expr .. rst:role:: cpp:expr
cpp:texpr
A role for inserting a C++ expression or type as inline text. For example:: Insert a C++ expression or type either as inline code (``cpp:expr``)
or inline text (``cpp:texpr``). For example::
.. cpp:var:: int a = 42 .. cpp:var:: int a = 42
.. cpp:function:: int f(int i) .. cpp:function:: int f(int i)
An expression: :cpp:expr:`a * f(a)`. An expression: :cpp:expr:`a * f(a)` (or as text: :cpp:texpr:`a * f(a)`).
A type: :cpp:expr:`const MySortedContainer<int>&`.
A type: :cpp:expr:`const MySortedContainer<int>&`
(or as text :cpp:texpr:`const MySortedContainer<int>&`).
will be rendered as follows: will be rendered as follows:
@ -831,8 +839,11 @@ Inline Expressions and Tpes
.. cpp:function:: int f(int i) .. cpp:function:: int f(int i)
An expression: :cpp:expr:`a * f(a)`. A type: :cpp:expr:`const An expression: :cpp:expr:`a * f(a)` (or as text: :cpp:texpr:`a * f(a)`).
MySortedContainer<int>&`.
A type: :cpp:expr:`const MySortedContainer<int>&`
(or as text :cpp:texpr:`const MySortedContainer<int>&`).
Namespacing Namespacing
~~~~~~~~~~~ ~~~~~~~~~~~
@ -880,7 +891,7 @@ The ``cpp:namespace-pop`` directive undoes the most recent
.. cpp:function:: std::size_t size() const .. cpp:function:: std::size_t size() const
or::: or::
.. cpp:class:: template<typename T> \ .. cpp:class:: template<typename T> \
std::vector std::vector
@ -949,20 +960,23 @@ These roles link to the given declaration types:
.. admonition:: Note on References with Templates Parameters/Arguments .. admonition:: Note on References with Templates Parameters/Arguments
Sphinx's syntax to give references a custom title can interfere with linking These roles follow the Sphinx :ref:`xref-syntax` rules. This means care must be
to class templates, if nothing follows the closing angle bracket, i.e. if taken when referencing a (partial) template specialization, e.g. if the link looks like
the link looks like this: ``:cpp:class:`MyClass<int>```. This is this: ``:cpp:class:`MyClass<int>```.
interpreted as a link to ``int`` with a title of ``MyClass``. In this case, This is interpreted as a link to ``int`` with a title of ``MyClass``.
please escape the opening angle bracket with a backslash, like this: In this case, escape the opening angle bracket with a backslash,
``:cpp:class:`MyClass\<int>```. like this: ``:cpp:class:`MyClass\<int>```.
When a custom title is not needed it may be useful to use the roles for inline expressions,
:rst:role:`cpp:expr` and :rst:role:`cpp:texpr`, where angle brackets do not need escaping.
.. admonition:: Note on References to Overloaded Functions .. admonition:: Note on References to Overloaded Functions
It is currently impossible to link to a specific version of an overloaded It is currently impossible to link to a specific version of an overloaded
method. Currently the C++ domain is the first domain that has basic support function. Currently the C++ domain is the first domain that has basic
for overloaded methods and until there is more data for comparison we don't support for overloaded functions and until there is more data for comparison
want to select a bad syntax to reference a specific overload. Currently we don't want to select a bad syntax to reference a specific overload.
Sphinx will link to the first overloaded version of the method / function. Currently Sphinx will link to the first overloaded version of the function.
Declarations without template parameters and template arguments Declarations without template parameters and template arguments
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -983,13 +997,13 @@ Assume the following declarations.
.. cpp:class:: template<typename TInner> \ .. cpp:class:: template<typename TInner> \
Inner Inner
In general the reference must include the template paraemter declarations, In general the reference must include the template parameter declarations,
e.g., ``template\<typename TOuter> Wrapper::Outer`` e.g., ``template\<typename TOuter> Wrapper::Outer``
(:cpp:class:`template\<typename TOuter> Wrapper::Outer`). Currently the lookup (:cpp:class:`template\<typename TOuter> Wrapper::Outer`). Currently the lookup
only succeed if the template parameter identifiers are equal strings. That is, only succeed if the template parameter identifiers are equal strings. That is,
``template\<typename UOuter> Wrapper::Outer`` will not work. ``template\<typename UOuter> Wrapper::Outer`` will not work.
The inner class template can not be directly referenced, unless the current The inner class template cannot be directly referenced, unless the current
namespace is changed or the following shorthand is used. If a template namespace is changed or the following shorthand is used. If a template
parameter list is omitted, then the lookup will assume either a template or a parameter list is omitted, then the lookup will assume either a template or a
non-template, but not a partial template specialisation. This means the non-template, but not a partial template specialisation. This means the

View File

@ -20,7 +20,8 @@ from sphinx import package_dir, addnodes, highlighting
from sphinx.builders import Builder from sphinx.builders import Builder
from sphinx.builders.latex.transforms import ( from sphinx.builders.latex.transforms import (
BibliographyTransform, CitationReferenceTransform, MathReferenceTransform, BibliographyTransform, CitationReferenceTransform, MathReferenceTransform,
FootnoteDocnameUpdater, LaTeXFootnoteTransform, ShowUrlsTransform FootnoteDocnameUpdater, LaTeXFootnoteTransform, LiteralBlockTransform,
ShowUrlsTransform, DocumentTargetTransform,
) )
from sphinx.config import string_classes, ENUM from sphinx.config import string_classes, ENUM
from sphinx.environment import NoUri from sphinx.environment import NoUri
@ -223,7 +224,9 @@ class LaTeXBuilder(Builder):
transformer.set_environment(self.env) transformer.set_environment(self.env)
transformer.add_transforms([BibliographyTransform, transformer.add_transforms([BibliographyTransform,
ShowUrlsTransform, ShowUrlsTransform,
LaTeXFootnoteTransform]) LaTeXFootnoteTransform,
LiteralBlockTransform,
DocumentTargetTransform])
transformer.apply_transforms() transformer.apply_transforms()
def finish(self): def finish(self):

View File

@ -12,6 +12,11 @@
from docutils import nodes from docutils import nodes
class captioned_literal_block(nodes.container):
"""A node for a container of literal_block having a caption."""
pass
class footnotemark(nodes.Inline, nodes.Referential, nodes.TextElement): class footnotemark(nodes.Inline, nodes.Referential, nodes.TextElement):
"""A node represents ``\footnotemark``.""" """A node represents ``\footnotemark``."""
pass pass

View File

@ -13,7 +13,7 @@ from docutils import nodes
from sphinx import addnodes from sphinx import addnodes
from sphinx.builders.latex.nodes import ( from sphinx.builders.latex.nodes import (
footnotemark, footnotetext, math_reference, thebibliography captioned_literal_block, footnotemark, footnotetext, math_reference, thebibliography
) )
from sphinx.transforms import SphinxTransform from sphinx.transforms import SphinxTransform
@ -566,3 +566,33 @@ class MathReferenceTransform(SphinxTransform):
if docname: if docname:
refnode = math_reference('', docname=docname, target=node['reftarget']) refnode = math_reference('', docname=docname, target=node['reftarget'])
node.replace_self(refnode) node.replace_self(refnode)
class LiteralBlockTransform(SphinxTransform):
"""Replace container nodes for literal_block by captioned_literal_block."""
default_priority = 400
def apply(self):
# type: () -> None
if self.app.builder.name != 'latex':
return
for node in self.document.traverse(nodes.container):
if node['literal_block'] is True:
newnode = captioned_literal_block('', *node.children, **node.attributes)
node.replace_self(newnode)
class DocumentTargetTransform(SphinxTransform):
"""Add :doc label to the first section of each document."""
default_priority = 400
def apply(self):
# type: () -> None
if self.app.builder.name != 'latex':
return
for node in self.document.traverse(addnodes.start_of_file):
section = node.next_node(nodes.section)
if section:
section['ids'].append(':doc') # special label for :doc:

View File

@ -3196,6 +3196,27 @@ class ASTClass(ASTBase):
signode.pop() signode.pop()
class ASTUnion(ASTBase):
def __init__(self, name):
# type: (Any) -> None
self.name = name
def get_id(self, version, objectType, symbol):
# type: (int, unicode, Symbol) -> unicode
if version == 1:
raise NoOldIdError()
return symbol.get_full_nested_name().get_id(version)
def __unicode__(self):
# type: () -> unicode
return text_type(self.name)
def describe_signature(self, signode, mode, env, symbol):
# type: (addnodes.desc_signature, unicode, BuildEnvironment, Symbol) -> None
_verify_description_mode(mode)
self.name.describe_signature(signode, mode, env, symbol=symbol)
class ASTEnum(ASTBase): class ASTEnum(ASTBase):
def __init__(self, name, scoped, underlyingType): def __init__(self, name, scoped, underlyingType):
# type: (Any, unicode, Any) -> None # type: (Any, unicode, Any) -> None
@ -3361,6 +3382,8 @@ class ASTDeclaration(ASTBase):
pass pass
elif self.objectType == 'class': elif self.objectType == 'class':
mainDeclNode += addnodes.desc_annotation('class ', 'class ') mainDeclNode += addnodes.desc_annotation('class ', 'class ')
elif self.objectType == 'union':
mainDeclNode += addnodes.desc_annotation('union ', 'union ')
elif self.objectType == 'enum': elif self.objectType == 'enum':
prefix = 'enum ' prefix = 'enum '
if self.scoped: # type: ignore if self.scoped: # type: ignore
@ -5259,6 +5282,11 @@ class DefinitionParser(object):
break break
return ASTClass(name, final, bases) return ASTClass(name, final, bases)
def _parse_union(self):
# type: () -> ASTUnion
name = self._parse_nested_name()
return ASTUnion(name)
def _parse_enum(self): def _parse_enum(self):
# type: () -> ASTEnum # type: () -> ASTEnum
scoped = None # type: unicode # is set by CPPEnumObject scoped = None # type: unicode # is set by CPPEnumObject
@ -5466,7 +5494,7 @@ class DefinitionParser(object):
def parse_declaration(self, objectType): def parse_declaration(self, objectType):
# type: (unicode) -> ASTDeclaration # type: (unicode) -> ASTDeclaration
if objectType not in ('type', 'concept', 'member', if objectType not in ('type', 'concept', 'member',
'function', 'class', 'enum', 'enumerator'): 'function', 'class', 'union', 'enum', 'enumerator'):
raise Exception('Internal error, unknown objectType "%s".' % objectType) raise Exception('Internal error, unknown objectType "%s".' % objectType)
visibility = None visibility = None
templatePrefix = None templatePrefix = None
@ -5505,6 +5533,8 @@ class DefinitionParser(object):
declaration = self._parse_type(named=True, outer='function') declaration = self._parse_type(named=True, outer='function')
elif objectType == 'class': elif objectType == 'class':
declaration = self._parse_class() declaration = self._parse_class()
elif objectType == 'union':
declaration = self._parse_union()
elif objectType == 'enum': elif objectType == 'enum':
declaration = self._parse_enum() declaration = self._parse_enum()
elif objectType == 'enumerator': elif objectType == 'enumerator':
@ -5807,6 +5837,16 @@ class CPPClassObject(CPPObject):
return parser.parse_declaration("class") return parser.parse_declaration("class")
class CPPUnionObject(CPPObject):
def get_index_text(self, name):
# type: (unicode) -> unicode
return _('%s (C++ union)') % name
def parse_definition(self, parser):
# type: (Any) -> Any
return parser.parse_declaration("union")
class CPPEnumObject(CPPObject): class CPPEnumObject(CPPObject):
def get_index_text(self, name): def get_index_text(self, name):
# type: (unicode) -> unicode # type: (unicode) -> unicode
@ -5965,6 +6005,16 @@ class CPPXRefRole(XRefRole):
class CPPExprRole(object): class CPPExprRole(object):
def __init__(self, asCode):
if asCode:
# render the expression as inline code
self.class_type = 'cpp-expr'
self.node_type = nodes.literal
else:
# render the expression as inline text
self.class_type = 'cpp-texpr'
self.node_type = nodes.inline
def __call__(self, typ, rawtext, text, lineno, inliner, options={}, content=[]): def __call__(self, typ, rawtext, text, lineno, inliner, options={}, content=[]):
class Warner(object): class Warner(object):
def warn(self, msg): def warn(self, msg):
@ -5972,18 +6022,23 @@ class CPPExprRole(object):
text = utils.unescape(text).replace('\n', ' ') text = utils.unescape(text).replace('\n', ' ')
env = inliner.document.settings.env env = inliner.document.settings.env
parser = DefinitionParser(text, Warner(), env.config) parser = DefinitionParser(text, Warner(), env.config)
# attempt to mimic XRefRole classes, except that...
classes = ['xref', 'cpp', self.class_type]
try: try:
ast = parser.parse_expression() ast = parser.parse_expression()
except DefinitionError as ex: except DefinitionError as ex:
Warner().warn('Unparseable C++ expression: %r\n%s' Warner().warn('Unparseable C++ expression: %r\n%s'
% (text, text_type(ex.description))) % (text, text_type(ex.description)))
return [nodes.literal(text)], [] # see below
return [self.node_type(text, text, classes=classes)], []
parentSymbol = env.temp_data.get('cpp:parent_symbol', None) parentSymbol = env.temp_data.get('cpp:parent_symbol', None)
if parentSymbol is None: if parentSymbol is None:
parentSymbol = env.domaindata['cpp']['root_symbol'] parentSymbol = env.domaindata['cpp']['root_symbol']
p = nodes.literal() # ...most if not all of these classes should really apply to the individual references,
ast.describe_signature(p, 'markType', env, parentSymbol) # not the container node
return [p], [] signode = self.node_type(classes=classes)
ast.describe_signature(signode, 'markType', env, parentSymbol)
return [signode], []
class CPPDomain(Domain): class CPPDomain(Domain):
@ -5992,6 +6047,7 @@ class CPPDomain(Domain):
label = 'C++' label = 'C++'
object_types = { object_types = {
'class': ObjType(_('class'), 'class', 'type', 'identifier'), 'class': ObjType(_('class'), 'class', 'type', 'identifier'),
'union': ObjType(_('union'), 'union', 'type', 'identifier'),
'function': ObjType(_('function'), 'function', 'func', 'type', 'identifier'), 'function': ObjType(_('function'), 'function', 'func', 'type', 'identifier'),
'member': ObjType(_('member'), 'member', 'var'), 'member': ObjType(_('member'), 'member', 'var'),
'type': ObjType(_('type'), 'type', 'identifier'), 'type': ObjType(_('type'), 'type', 'identifier'),
@ -6002,6 +6058,7 @@ class CPPDomain(Domain):
directives = { directives = {
'class': CPPClassObject, 'class': CPPClassObject,
'union': CPPUnionObject,
'function': CPPFunctionObject, 'function': CPPFunctionObject,
'member': CPPMemberObject, 'member': CPPMemberObject,
'var': CPPMemberObject, 'var': CPPMemberObject,
@ -6018,6 +6075,7 @@ class CPPDomain(Domain):
roles = { roles = {
'any': CPPXRefRole(), 'any': CPPXRefRole(),
'class': CPPXRefRole(), 'class': CPPXRefRole(),
'union': CPPXRefRole(),
'func': CPPXRefRole(fix_parens=True), 'func': CPPXRefRole(fix_parens=True),
'member': CPPXRefRole(), 'member': CPPXRefRole(),
'var': CPPXRefRole(), 'var': CPPXRefRole(),
@ -6025,7 +6083,8 @@ class CPPDomain(Domain):
'concept': CPPXRefRole(), 'concept': CPPXRefRole(),
'enum': CPPXRefRole(), 'enum': CPPXRefRole(),
'enumerator': CPPXRefRole(), 'enumerator': CPPXRefRole(),
'expr': CPPExprRole() 'expr': CPPExprRole(asCode=True),
'texpr': CPPExprRole(asCode=False)
} }
initial_data = { initial_data = {
'root_symbol': Symbol(None, None, None, None, None, None), 'root_symbol': Symbol(None, None, None, None, None, None),

View File

@ -313,19 +313,6 @@ class BuildEnvironment(object):
"""Like :meth:`warn`, but with source information taken from *node*.""" """Like :meth:`warn`, but with source information taken from *node*."""
self._warnfunc(msg, '%s:%s' % get_source_line(node), **kwargs) self._warnfunc(msg, '%s:%s' % get_source_line(node), **kwargs)
def need_refresh(self, app):
# type: (Sphinx) -> Tuple[bool, unicode]
"""Check refresh environment is needed.
If needed, this method returns the reason for refresh.
"""
if self.version != app.registry.get_envversion(app):
return True, __('build environment version not current')
elif self.srcdir != app.srcdir:
return True, __('source directory has changed')
else:
return False, None
def clear_doc(self, docname): def clear_doc(self, docname):
# type: (unicode) -> None # type: (unicode) -> None
"""Remove all traces of a source file in the inventory.""" """Remove all traces of a source file in the inventory."""

View File

@ -172,10 +172,10 @@ class TocTreeCollector(EnvironmentCollector):
number = tuple(numstack) number = tuple(numstack)
else: else:
number = None number = None
secnums[subnode[0]['anchorname']] = \ secnums[subnode[0]['anchorname']] = number
subnode[0]['secnumber'] = number subnode[0]['secnumber'] = list(number)
if titlenode: if titlenode:
titlenode['secnumber'] = number titlenode['secnumber'] = list(number)
titlenode = None titlenode = None
elif isinstance(subnode, addnodes.toctree): elif isinstance(subnode, addnodes.toctree):
_walk_toctree(subnode, depth) _walk_toctree(subnode, depth)

View File

@ -643,11 +643,17 @@ class Documenter(object):
# should be skipped # should be skipped
if self.env.app: if self.env.app:
# let extensions preprocess docstrings # let extensions preprocess docstrings
try:
skip_user = self.env.app.emit_firstresult( skip_user = self.env.app.emit_firstresult(
'autodoc-skip-member', self.objtype, membername, member, 'autodoc-skip-member', self.objtype, membername, member,
not keep, self.options) not keep, self.options)
if skip_user is not None: if skip_user is not None:
keep = not skip_user keep = not skip_user
except Exception as exc:
logger.warning(__('autodoc: failed to determine %r to be documented.'
'the following exception was raised:\n%s'),
member, exc)
keep = False
if keep: if keep:
ret.append((membername, member, isattr)) ret.append((membername, member, isattr))

View File

@ -23,7 +23,7 @@ from sphinx.util.inspect import isenumclass, safe_getattr
if False: if False:
# For type annotation # For type annotation
from typing import Any, Callable, Dict, Generator, List, Optional, Tuple # NOQA from typing import Any, Callable, Dict, Generator, Iterator, List, Optional, Tuple # NOQA
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -41,7 +41,7 @@ class _MockObject(object):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
# type: (Any, Any) -> None # type: (Any, Any) -> None
pass self.__qualname__ = ''
def __len__(self): def __len__(self):
# type: () -> int # type: () -> int
@ -52,8 +52,8 @@ class _MockObject(object):
return False return False
def __iter__(self): def __iter__(self):
# type: () -> None # type: () -> Iterator
pass return iter([])
def __mro_entries__(self, bases): def __mro_entries__(self, bases):
# type: (Tuple) -> Tuple # type: (Tuple) -> Tuple

View File

@ -225,11 +225,12 @@ class AfterCommentParser(TokenProcessor):
def parse(self): def parse(self):
# type: () -> None # type: () -> None
"""Parse the code and obtain comment after assignment.""" """Parse the code and obtain comment after assignment."""
# skip lvalue (until '=' operator) # skip lvalue (or whole of AnnAssign)
while self.fetch_token() != [OP, '=']: while not self.fetch_token().match([OP, '='], NEWLINE, COMMENT):
assert self.current assert self.current
# skip rvalue # skip rvalue (if exists)
if self.current == [OP, '=']:
self.fetch_rvalue() self.fetch_rvalue()
if self.current == COMMENT: if self.current == COMMENT:

View File

@ -392,7 +392,7 @@ class WarningIsErrorFilter(logging.Filter):
location = getattr(record, 'location', '') location = getattr(record, 'location', '')
try: try:
message = record.msg % record.args message = record.msg % record.args
except TypeError: except (TypeError, ValueError):
message = record.msg # use record.msg itself message = record.msg # use record.msg itself
if location: if location:

View File

@ -56,8 +56,7 @@ def repr_domxml(node, length=80):
returns full of DOM XML representation. returns full of DOM XML representation.
:return: DOM XML representation :return: DOM XML representation
""" """
# text = node.asdom().toxml() # #4919 crush if node has secnumber with tuple value text = node.asdom().toxml()
text = text_type(node) # workaround for #4919
if length and len(text) > length: if length and len(text) > length:
text = text[:length] + '...' text = text[:length] + '...'
return text return text
@ -82,9 +81,8 @@ def apply_source_workaround(node):
get_full_module_name(node), repr_domxml(node)) get_full_module_name(node), repr_domxml(node))
node.source, node.line = node.parent.source, node.parent.line node.source, node.line = node.parent.source, node.parent.line
if isinstance(node, nodes.title) and node.source is None: if isinstance(node, nodes.title) and node.source is None:
# Uncomment these lines after merging into master(1.8) logger.debug('[i18n] PATCH: %r to have source: %s',
# logger.debug('[i18n] PATCH: %r to have source: %s', get_full_module_name(node), repr_domxml(node))
# get_full_module_name(node), repr_domxml(node))
node.source, node.line = node.parent.source, node.parent.line node.source, node.line = node.parent.source, node.parent.line
if isinstance(node, nodes.term): if isinstance(node, nodes.term):
logger.debug('[i18n] PATCH: %r to have rawsource: %s', logger.debug('[i18n] PATCH: %r to have rawsource: %s',

View File

@ -25,7 +25,7 @@ from six import itervalues, text_type
from sphinx import addnodes from sphinx import addnodes
from sphinx import highlighting from sphinx import highlighting
from sphinx.builders.latex.nodes import footnotetext from sphinx.builders.latex.nodes import captioned_literal_block, footnotetext
from sphinx.deprecation import RemovedInSphinx30Warning from sphinx.deprecation import RemovedInSphinx30Warning
from sphinx.errors import SphinxError from sphinx.errors import SphinxError
from sphinx.locale import admonitionlabels, _, __ from sphinx.locale import admonitionlabels, _, __
@ -58,6 +58,7 @@ HYPERLINK_SUPPORT_NODES = (
nodes.literal_block, nodes.literal_block,
nodes.table, nodes.table,
nodes.section, nodes.section,
captioned_literal_block,
) )
DEFAULT_SETTINGS = { DEFAULT_SETTINGS = {
@ -465,7 +466,6 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.in_production_list = 0 self.in_production_list = 0
self.in_footnote = 0 self.in_footnote = 0
self.in_caption = 0 self.in_caption = 0
self.in_container_literal_block = 0
self.in_term = 0 self.in_term = 0
self.needs_linetrimming = 0 self.needs_linetrimming = 0
self.in_minipage = 0 self.in_minipage = 0
@ -691,8 +691,6 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.pending_footnotes = [] # type: List[nodes.footnote_reference] self.pending_footnotes = [] # type: List[nodes.footnote_reference]
self.curfilestack = [] # type: List[unicode] self.curfilestack = [] # type: List[unicode]
self.handled_abbrs = set() # type: Set[unicode] self.handled_abbrs = set() # type: Set[unicode]
self.next_hyperlink_ids = {} # type: Dict[unicode, Set[unicode]]
self.next_section_ids = set() # type: Set[unicode]
def pushbody(self, newbody): def pushbody(self, newbody):
# type: (List[unicode]) -> None # type: (List[unicode]) -> None
@ -705,15 +703,6 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body = self.bodystack.pop() self.body = self.bodystack.pop()
return body return body
def push_hyperlink_ids(self, figtype, ids):
# type: (unicode, Set[unicode]) -> None
hyperlink_ids = self.next_hyperlink_ids.setdefault(figtype, set())
hyperlink_ids.update(ids)
def pop_hyperlink_ids(self, figtype):
# type: (unicode) -> Set[unicode]
return self.next_hyperlink_ids.pop(figtype, set())
def check_latex_elements(self): def check_latex_elements(self):
# type: () -> None # type: () -> None
for key in self.builder.config.latex_elements: for key in self.builder.config.latex_elements:
@ -931,8 +920,6 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_start_of_file(self, node): def visit_start_of_file(self, node):
# type: (nodes.Node) -> None # type: (nodes.Node) -> None
# also add a document target
self.next_section_ids.add(':doc')
self.curfilestack.append(node['docname']) self.curfilestack.append(node['docname'])
# use default highlight settings for new file # use default highlight settings for new file
self.hlsettingstack.append(self.hlsettingstack[0]) self.hlsettingstack.append(self.hlsettingstack[0])
@ -1067,11 +1054,6 @@ class LaTeXTranslator(nodes.NodeVisitor):
# just use "subparagraph", it's not numbered anyway # just use "subparagraph", it's not numbered anyway
self.body.append(r'\%s%s{' % (self.sectionnames[-1], short)) self.body.append(r'\%s%s{' % (self.sectionnames[-1], short))
self.context.append('}\n' + self.hypertarget_to(node.parent)) self.context.append('}\n' + self.hypertarget_to(node.parent))
if self.next_section_ids:
for id in self.next_section_ids:
self.context[-1] += self.hypertarget(id, anchor=False)
self.next_section_ids.clear()
elif isinstance(parent, nodes.topic): elif isinstance(parent, nodes.topic):
self.body.append(r'\sphinxstyletopictitle{') self.body.append(r'\sphinxstyletopictitle{')
self.context.append('}\n') self.context.append('}\n')
@ -1790,7 +1772,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_caption(self, node): def visit_caption(self, node):
# type: (nodes.Node) -> None # type: (nodes.Node) -> None
self.in_caption += 1 self.in_caption += 1
if self.in_container_literal_block: if isinstance(node.parent, captioned_literal_block):
self.body.append('\\sphinxSetupCaptionForVerbatim{') self.body.append('\\sphinxSetupCaptionForVerbatim{')
elif self.in_minipage and isinstance(node.parent, nodes.figure): elif self.in_minipage and isinstance(node.parent, nodes.figure):
self.body.append('\\captionof{figure}{') self.body.append('\\captionof{figure}{')
@ -1883,35 +1865,13 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append(self.hypertarget(id, anchor=anchor)) self.body.append(self.hypertarget(id, anchor=anchor))
# skip if visitor for next node supports hyperlink # skip if visitor for next node supports hyperlink
domain = self.builder.env.get_domain('std')
next_node = node.next_node(ascend=True) next_node = node.next_node(ascend=True)
if isinstance(next_node, HYPERLINK_SUPPORT_NODES): if isinstance(next_node, HYPERLINK_SUPPORT_NODES):
return return
elif domain.get_enumerable_node_type(next_node) and domain.get_numfig_title(next_node):
# postpone the labels until after the sectioning command
parindex = node.parent.index(node)
try:
try:
next = node.parent[parindex + 1]
except IndexError:
# last node in parent, look at next after parent
# (for section of equal level) if it exists
if node.parent.parent is not None:
next = node.parent.parent[
node.parent.parent.index(node.parent)]
else:
raise
domain = self.builder.env.get_domain('std')
figtype = domain.get_enumerable_node_type(next)
if figtype and domain.get_numfig_title(next):
ids = set()
# labels for figures go in the figure body, not before
if node.get('refid'):
ids.add(node['refid'])
ids.update(node['ids'])
self.push_hyperlink_ids(figtype, ids)
return return
except IndexError:
pass
if 'refuri' in node: if 'refuri' in node:
return return
if node.get('refid'): if node.get('refid'):
@ -2221,6 +2181,14 @@ class LaTeXTranslator(nodes.NodeVisitor):
# the \ignorespaces in particular for after table header use # the \ignorespaces in particular for after table header use
self.body.append('%\n\\end{footnotetext}\\ignorespaces ') self.body.append('%\n\\end{footnotetext}\\ignorespaces ')
def visit_captioned_literal_block(self, node):
# type: (nodes.Node) -> None
pass
def depart_captioned_literal_block(self, node):
# type: (nodes.Node) -> None
pass
def visit_literal_block(self, node): def visit_literal_block(self, node):
# type: (nodes.Node) -> None # type: (nodes.Node) -> None
if node.rawsource != node.astext(): if node.rawsource != node.astext():
@ -2229,9 +2197,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append('\\begin{sphinxalltt}\n') self.body.append('\\begin{sphinxalltt}\n')
else: else:
labels = self.hypertarget_to(node) labels = self.hypertarget_to(node)
# LaTeX code will insert \phantomsection prior to \label if isinstance(node.parent, captioned_literal_block):
labels += self.hypertarget_to(node.parent)
if labels and not self.in_footnote: if labels and not self.in_footnote:
self.body.append('\n\\def\\sphinxLiteralBlockLabel{' + labels + '}') self.body.append('\n\\def\\sphinxLiteralBlockLabel{' + labels + '}')
code = node.astext() code = node.astext()
lang = self.hlsettingstack[-1][0] lang = self.hlsettingstack[-1][0]
linenos = code.count('\n') >= self.hlsettingstack[-1][1] - 1 linenos = code.count('\n') >= self.hlsettingstack[-1][1] - 1
@ -2458,22 +2428,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_container(self, node): def visit_container(self, node):
# type: (nodes.Node) -> None # type: (nodes.Node) -> None
if node.get('literal_block'): pass
self.in_container_literal_block += 1
ids = '' # type: unicode
for id in self.pop_hyperlink_ids('code-block'):
ids += self.hypertarget(id, anchor=False)
if node['ids']:
# suppress with anchor=False \phantomsection insertion
ids += self.hypertarget(node['ids'][0], anchor=False)
# define label for use in caption.
if ids:
self.body.append('\n\\def\\sphinxLiteralBlockLabel{' + ids + '}\n')
def depart_container(self, node): def depart_container(self, node):
# type: (nodes.Node) -> None # type: (nodes.Node) -> None
if node.get('literal_block'): pass
self.in_container_literal_block -= 1
def visit_decoration(self, node): def visit_decoration(self, node):
# type: (nodes.Node) -> None # type: (nodes.Node) -> None
@ -2603,6 +2562,39 @@ class LaTeXTranslator(nodes.NodeVisitor):
RemovedInSphinx30Warning) RemovedInSphinx30Warning)
return [] return []
@property
def in_container_literal_block(self):
# type: () -> int
warnings.warn('LaTeXTranslator.in_container_literal_block is deprecated.',
RemovedInSphinx30Warning)
return 0
@property
def next_section_ids(self):
# type: () -> Set[unicode]
warnings.warn('LaTeXTranslator.next_section_ids is deprecated.',
RemovedInSphinx30Warning)
return set()
@property
def next_hyperlink_ids(self):
# type: () -> Dict
warnings.warn('LaTeXTranslator.next_hyperlink_ids is deprecated.',
RemovedInSphinx30Warning)
return {}
def push_hyperlink_ids(self, figtype, ids):
# type: (unicode, Set[unicode]) -> None
warnings.warn('LaTeXTranslator.push_hyperlink_ids() is deprecated.',
RemovedInSphinx30Warning)
pass
def pop_hyperlink_ids(self, figtype):
# type: (unicode) -> Set[unicode]
warnings.warn('LaTeXTranslator.pop_hyperlink_ids() is deprecated.',
RemovedInSphinx30Warning)
return set()
# Import old modules here for compatibility # Import old modules here for compatibility
# They should be imported after `LaTeXTranslator` to avoid recursive import. # They should be imported after `LaTeXTranslator` to avoid recursive import.

View File

@ -0,0 +1,12 @@
xref consistency
----------------
.. cpp:namespace:: xref_consistency
.. cpp:class:: item
code-role: :code:`item`
any-role: :any:`item`
cpp-any-role: :cpp:any:`item`
cpp-expr-role: :cpp:expr:`item`
cpp-texpr-role: :cpp:texpr:`item`

View File

@ -491,6 +491,10 @@ def test_class_definitions():
{2: 'I0E7has_varI1TNSt6void_tIDTadN1T3varEEEEE'}) {2: 'I0E7has_varI1TNSt6void_tIDTadN1T3varEEEEE'})
def test_union_definitions():
check('union', 'A', {2: "1A"})
def test_enum_definitions(): def test_enum_definitions():
check('enum', 'A', {2: "1A"}) check('enum', 'A', {2: "1A"})
check('enum', 'A : std::underlying_type<B>::type', {2: "1A"}) check('enum', 'A : std::underlying_type<B>::type', {2: "1A"})
@ -735,3 +739,68 @@ def test_build_domain_cpp_with_add_function_parentheses_is_False(app, status, wa
t = (app.outdir / f).text() t = (app.outdir / f).text()
for s in parenPatterns: for s in parenPatterns:
check(s, t, f) check(s, t, f)
@pytest.mark.sphinx(testroot='domain-cpp')
def test_xref_consistency(app, status, warning):
app.builder.build_all()
test = 'xref_consistency.html'
output = (app.outdir / test).text()
def classes(role, tag):
pattern = (r'{role}-role:.*?'
'<(?P<tag>{tag}) .*?class=["\'](?P<classes>.*?)["\'].*?>'
'.*'
'</(?P=tag)>').format(role=role, tag=tag)
result = re.search(pattern, output)
expect = '''\
Pattern for role `{role}` with tag `{tag}`
\t{pattern}
not found in `{test}`
'''.format(role=role, tag=tag, pattern=pattern, test=test)
assert result, expect
return set(result.group('classes').split())
class RoleClasses(object):
"""Collect the classes from the layout that was generated for a given role."""
def __init__(self, role, root, contents):
self.name = role
self.classes = classes(role, root)
self.content_classes = dict()
for tag in contents:
self.content_classes[tag] = classes(role, tag)
# not actually used as a reference point
#code_role = RoleClasses('code', 'code', [])
any_role = RoleClasses('any', 'a', ['code'])
cpp_any_role = RoleClasses('cpp-any', 'a', ['code'])
# NYI: consistent looks
#texpr_role = RoleClasses('cpp-texpr', 'span', ['a', 'code'])
expr_role = RoleClasses('cpp-expr', 'code', ['a'])
texpr_role = RoleClasses('cpp-texpr', 'span', ['a', 'span'])
# XRefRole-style classes
## any and cpp:any do not put these classes at the root
# n.b. the generic any machinery finds the specific 'cpp-class' object type
expect = 'any uses XRefRole classes'
assert {'xref', 'any', 'cpp', 'cpp-class'} <= any_role.content_classes['code'], expect
expect = 'cpp:any uses XRefRole classes'
assert {'xref', 'cpp-any', 'cpp'} <= cpp_any_role.content_classes['code'], expect
for role in (expr_role, texpr_role):
name = role.name
expect = '`{name}` puts the domain and role classes at its root'.format(name=name)
# NYI: xref should go in the references
assert {'xref', 'cpp', name} <= role.classes, expect
# reference classes
expect = 'the xref roles use the same reference classes'
assert any_role.classes == cpp_any_role.classes, expect
assert any_role.classes == expr_role.content_classes['a'], expect
assert any_role.classes == texpr_role.content_classes['a'], expect

View File

@ -227,11 +227,11 @@ def test_get_toctree_for(app):
[list_item, compact_paragraph, reference, "foo.1"], [list_item, compact_paragraph, reference, "foo.1"],
[list_item, compact_paragraph, reference, "foo.2"])) [list_item, compact_paragraph, reference, "foo.2"]))
assert_node(toctree[1][0][0][0], reference, refuri="foo", secnumber=(1,)) assert_node(toctree[1][0][0][0], reference, refuri="foo", secnumber=[1])
assert_node(toctree[1][0][1][0][0][0], reference, refuri="quux", secnumber=(1, 1)) assert_node(toctree[1][0][1][0][0][0], reference, refuri="quux", secnumber=[1, 1])
assert_node(toctree[1][0][1][1][0][0], reference, refuri="foo#foo-1", secnumber=(1, 2)) assert_node(toctree[1][0][1][1][0][0], reference, refuri="foo#foo-1", secnumber=[1, 2])
assert_node(toctree[1][0][1][2][0][0], reference, refuri="foo#foo-2", secnumber=(1, 3)) assert_node(toctree[1][0][1][2][0][0], reference, refuri="foo#foo-2", secnumber=[1, 3])
assert_node(toctree[1][1][0][0], reference, refuri="bar", secnumber=(2,)) assert_node(toctree[1][1][0][0], reference, refuri="bar", secnumber=[2])
assert_node(toctree[1][2][0][0], reference, refuri="http://sphinx-doc.org/") assert_node(toctree[1][2][0][0], reference, refuri="http://sphinx-doc.org/")
assert_node(toctree[2], assert_node(toctree[2],
@ -258,8 +258,8 @@ def test_get_toctree_for_collapse(app):
([list_item, compact_paragraph, reference, "foo"], ([list_item, compact_paragraph, reference, "foo"],
[list_item, compact_paragraph, reference, "bar"], [list_item, compact_paragraph, reference, "bar"],
[list_item, compact_paragraph, reference, "http://sphinx-doc.org/"])) [list_item, compact_paragraph, reference, "http://sphinx-doc.org/"]))
assert_node(toctree[1][0][0][0], reference, refuri="foo", secnumber=(1,)) assert_node(toctree[1][0][0][0], reference, refuri="foo", secnumber=[1])
assert_node(toctree[1][1][0][0], reference, refuri="bar", secnumber=(2,)) assert_node(toctree[1][1][0][0], reference, refuri="bar", secnumber=[2])
assert_node(toctree[1][2][0][0], reference, refuri="http://sphinx-doc.org/") assert_node(toctree[1][2][0][0], reference, refuri="http://sphinx-doc.org/")
assert_node(toctree[2], assert_node(toctree[2],
@ -296,13 +296,13 @@ def test_get_toctree_for_maxdepth(app):
assert_node(toctree[1][0][1][1][1], assert_node(toctree[1][0][1][1][1],
[bullet_list, list_item, compact_paragraph, reference, "foo.1-1"]) [bullet_list, list_item, compact_paragraph, reference, "foo.1-1"])
assert_node(toctree[1][0][0][0], reference, refuri="foo", secnumber=(1,)) assert_node(toctree[1][0][0][0], reference, refuri="foo", secnumber=[1])
assert_node(toctree[1][0][1][0][0][0], reference, refuri="quux", secnumber=(1, 1)) assert_node(toctree[1][0][1][0][0][0], reference, refuri="quux", secnumber=[1, 1])
assert_node(toctree[1][0][1][1][0][0], reference, refuri="foo#foo-1", secnumber=(1, 2)) assert_node(toctree[1][0][1][1][0][0], reference, refuri="foo#foo-1", secnumber=[1, 2])
assert_node(toctree[1][0][1][1][1][0][0][0], assert_node(toctree[1][0][1][1][1][0][0][0],
reference, refuri="foo#foo-1-1", secnumber=(1, 2, 1)) reference, refuri="foo#foo-1-1", secnumber=[1, 2, 1])
assert_node(toctree[1][0][1][2][0][0], reference, refuri="foo#foo-2", secnumber=(1, 3)) assert_node(toctree[1][0][1][2][0][0], reference, refuri="foo#foo-2", secnumber=[1, 3])
assert_node(toctree[1][1][0][0], reference, refuri="bar", secnumber=(2,)) assert_node(toctree[1][1][0][0], reference, refuri="bar", secnumber=[2])
assert_node(toctree[1][2][0][0], reference, refuri="http://sphinx-doc.org/") assert_node(toctree[1][2][0][0], reference, refuri="http://sphinx-doc.org/")
assert_node(toctree[2], assert_node(toctree[2],
@ -335,11 +335,11 @@ def test_get_toctree_for_includehidden(app):
[list_item, compact_paragraph, reference, "foo.1"], [list_item, compact_paragraph, reference, "foo.1"],
[list_item, compact_paragraph, reference, "foo.2"])) [list_item, compact_paragraph, reference, "foo.2"]))
assert_node(toctree[1][0][0][0], reference, refuri="foo", secnumber=(1,)) assert_node(toctree[1][0][0][0], reference, refuri="foo", secnumber=[1])
assert_node(toctree[1][0][1][0][0][0], reference, refuri="quux", secnumber=(1, 1)) assert_node(toctree[1][0][1][0][0][0], reference, refuri="quux", secnumber=[1, 1])
assert_node(toctree[1][0][1][1][0][0], reference, refuri="foo#foo-1", secnumber=(1, 2)) assert_node(toctree[1][0][1][1][0][0], reference, refuri="foo#foo-1", secnumber=[1, 2])
assert_node(toctree[1][0][1][2][0][0], reference, refuri="foo#foo-2", secnumber=(1, 3)) assert_node(toctree[1][0][1][2][0][0], reference, refuri="foo#foo-2", secnumber=[1, 3])
assert_node(toctree[1][1][0][0], reference, refuri="bar", secnumber=(2,)) assert_node(toctree[1][1][0][0], reference, refuri="bar", secnumber=[2])
assert_node(toctree[1][2][0][0], reference, refuri="http://sphinx-doc.org/") assert_node(toctree[1][2][0][0], reference, refuri="http://sphinx-doc.org/")
assert_node(toctree[2], assert_node(toctree[2],

View File

@ -100,11 +100,13 @@ def test_comment_picker_location():
def test_annotated_assignment_py36(): def test_annotated_assignment_py36():
source = ('a: str = "Sphinx" #: comment\n' source = ('a: str = "Sphinx" #: comment\n'
'b: int = 1\n' 'b: int = 1\n'
'"""string on next line"""') '"""string on next line"""\n'
'c: int #: comment')
parser = Parser(source) parser = Parser(source)
parser.parse() parser.parse()
assert parser.comments == {('', 'a'): 'comment', assert parser.comments == {('', 'a'): 'comment',
('', 'b'): 'string on next line'} ('', 'b'): 'string on next line',
('', 'c'): 'comment'}
assert parser.definitions == {} assert parser.definitions == {}