Merge branch '3.x'

This commit is contained in:
Takeshi KOMIYA
2021-01-16 21:51:46 +09:00
36 changed files with 596 additions and 119 deletions

8
.readthedocs.yml Normal file
View File

@@ -0,0 +1,8 @@
version: 2
python:
version: 3
install:
- method: pip
path: .
extra_requirements:
- docs

13
CHANGES
View File

@@ -70,6 +70,7 @@ Deprecated
---------- ----------
* ``sphinx.ext.autodoc.AttributeDocumenter.isinstanceattribute()`` * ``sphinx.ext.autodoc.AttributeDocumenter.isinstanceattribute()``
* ``sphinx.ext.autodoc.directive.DocumenterBridge.reporter``
* ``sphinx.ext.autodoc.importer.get_module_members()`` * ``sphinx.ext.autodoc.importer.get_module_members()``
Features added Features added
@@ -87,7 +88,11 @@ Features added
* #8649: imgconverter: Skip availability check if builder supports the image * #8649: imgconverter: Skip availability check if builder supports the image
type type
* #6241: mathjax: Include mathjax.js only on the document using equations * #6241: mathjax: Include mathjax.js only on the document using equations
* #8651: std domain: cross-reference for a rubric having inline item is broken
* #8132: Add :confval:`project_copyright` as an alias of :confval:`copyright` * #8132: Add :confval:`project_copyright` as an alias of :confval:`copyright`
* #207: Now :confval:`highlight_language` supports multiple languages
* #2030: :rst:dir:`code-block` and :rst:dir:`literalinclude` supports automatic
dedent via no-argument ``:dedent:`` option
Bugs fixed Bugs fixed
---------- ----------
@@ -97,13 +102,21 @@ Bugs fixed
* #8592: autodoc: ``:meta public:`` does not effect to variables * #8592: autodoc: ``:meta public:`` does not effect to variables
* #8594: autodoc: empty __all__ attribute is ignored * #8594: autodoc: empty __all__ attribute is ignored
* #8315: autodoc: Failed to resolve struct.Struct type annotation * #8315: autodoc: Failed to resolve struct.Struct type annotation
* #8652: autodoc: All variable comments in the module are ignored if the module
contains invalid type comments
* #8306: autosummary: mocked modules are documented as empty page when using * #8306: autosummary: mocked modules are documented as empty page when using
:recursive: option :recursive: option
* #8618: html: kbd role produces incorrect HTML when compound-key separators (-, * #8618: html: kbd role produces incorrect HTML when compound-key separators (-,
+ or ^) are used as keystrokes + or ^) are used as keystrokes
* #8629: html: A type warning for html_use_opensearch is shown twice * #8629: html: A type warning for html_use_opensearch is shown twice
* #8665: html theme: Could not override globaltoc_maxdepth in theme.conf
* #8094: texinfo: image files on the different directory with document are not * #8094: texinfo: image files on the different directory with document are not
copied copied
* #8671: :confval:`highlight_options` is not working
* #8341: C, fix intersphinx lookup types for names in declarations.
* C, C++: in general fix intersphinx and role lookup types.
* #8683: :confval:`html_last_updated_fmt` does not support UTC offset (%z)
* #8683: :confval:`html_last_updated_fmt` generates wrong time zone for %Z
Testing Testing
-------- --------

View File

@@ -107,6 +107,11 @@ The following is a list of deprecated interfaces.
- 5.0 - 5.0
- ``sphinx.ext.autodoc.DataDocumenter`` - ``sphinx.ext.autodoc.DataDocumenter``
* - ``sphinx.ext.autodoc.directive.DocumenterBridge.reporter``
- 3.5
- 5.0
- ``sphinx.util.logging``
* - ``sphinx.ext.autodoc.importer._getannotations()`` * - ``sphinx.ext.autodoc.importer._getannotations()``
- 3.4 - 3.4
- 4.0 - 4.0

View File

@@ -581,12 +581,27 @@ General configuration
.. confval:: highlight_options .. confval:: highlight_options
A dictionary of options that modify how the lexer specified by A dictionary that maps language names to options for the lexer modules of
:confval:`highlight_language` generates highlighted source code. These are Pygments. These are lexer-specific; for the options understood by each,
lexer-specific; for the options understood by each, see the see the `Pygments documentation <https://pygments.org/docs/lexers>`_.
`Pygments documentation <https://pygments.org/docs/lexers>`_.
Example::
highlight_options = {
'default': {'stripall': True},
'php': {'startinline': True},
}
A single dictionary of options are also allowed. Then it is recognized
as options to the lexer specified by :confval:`highlight_language`::
# configuration for the ``highlight_language``
highlight_options = {'stripall': True}
.. versionadded:: 1.3 .. versionadded:: 1.3
.. versionchanged:: 3.5
Allow to configure highlight options for multiple languages
.. confval:: pygments_style .. confval:: pygments_style
@@ -944,8 +959,11 @@ that use Sphinx's HTMLWriter class.
.. confval:: html_baseurl .. confval:: html_baseurl
The URL which points to the root of the HTML documentation. It is used to The base URL which points to the root of the HTML documentation. It is used
indicate the location of document like ``canonical_url``. to indicate the location of document using `The Canonical Link Relation`_.
Default: ``''``.
.. _The Canonical Link Relation: https://tools.ietf.org/html/rfc6596
.. versionadded:: 1.8 .. versionadded:: 1.8

View File

@@ -157,7 +157,7 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
``:meta private:`` in its :ref:`info-field-lists`. ``:meta private:`` in its :ref:`info-field-lists`.
For example: For example:
.. code-block:: rst .. code-block:: python
def my_function(my_arg, my_other_arg): def my_function(my_arg, my_other_arg):
"""blah blah blah """blah blah blah
@@ -172,7 +172,7 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
an underscore. an underscore.
For example: For example:
.. code-block:: rst .. code-block:: python
def _my_function(my_arg, my_other_arg): def _my_function(my_arg, my_other_arg):
"""blah blah blah """blah blah blah
@@ -186,7 +186,7 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
docstring contains ``:meta hide-value:`` in its :ref:`info-field-lists`. docstring contains ``:meta hide-value:`` in its :ref:`info-field-lists`.
Example: Example:
.. code-block:: rst .. code-block:: python
var1 = None #: :meta hide-value: var1 = None #: :meta hide-value:

View File

@@ -572,9 +572,11 @@ __ http://pygments.org/docs/lexers
.. versionadded:: 1.3 .. versionadded:: 1.3
.. rst:directive:option:: dedent: number .. rst:directive:option:: dedent: number
:type: number :type: number or no value
Strip indentation characters from the code block. For example:: Strip indentation characters from the code block. When number given,
leading N characters are removed. When no argument given, leading spaces
are removed via :func:`textwrap.dedent()`. For example::
.. code-block:: ruby .. code-block:: ruby
:dedent: 4 :dedent: 4
@@ -582,6 +584,8 @@ __ http://pygments.org/docs/lexers
some ruby code some ruby code
.. versionadded:: 1.3 .. versionadded:: 1.3
.. versionchanged:: 3.5
Support automatic dedent.
.. rst:directive:option:: force .. rst:directive:option:: force
:type: no value :type: no value
@@ -742,6 +746,9 @@ __ http://pygments.org/docs/lexers
.. versionchanged:: 2.1 .. versionchanged:: 2.1
Added the ``force`` option. Added the ``force`` option.
.. versionchanged:: 3.5
Support automatic dedent.
.. _glossary-directive: .. _glossary-directive:
Glossary Glossary

View File

@@ -362,6 +362,18 @@ def convert_source_suffix(app: "Sphinx", config: Config) -> None:
"But `%r' is given." % source_suffix)) "But `%r' is given." % source_suffix))
def convert_highlight_options(app: "Sphinx", config: Config) -> None:
"""Convert old styled highlight_options to new styled one.
* old style: options
* new style: dict that maps language names to options
"""
options = config.highlight_options
if options and not all(isinstance(v, dict) for v in options.values()):
# old styled option detected because all values are not dictionary.
config.highlight_options = {config.highlight_language: options} # type: ignore
def init_numfig_format(app: "Sphinx", config: Config) -> None: def init_numfig_format(app: "Sphinx", config: Config) -> None:
"""Initialize :confval:`numfig_format`.""" """Initialize :confval:`numfig_format`."""
numfig_format = {'section': _('Section %s'), numfig_format = {'section': _('Section %s'),
@@ -466,6 +478,7 @@ def check_master_doc(app: "Sphinx", env: "BuildEnvironment", added: Set[str],
def setup(app: "Sphinx") -> Dict[str, Any]: def setup(app: "Sphinx") -> Dict[str, Any]:
app.connect('config-inited', convert_source_suffix, priority=800) app.connect('config-inited', convert_source_suffix, priority=800)
app.connect('config-inited', convert_highlight_options, priority=800)
app.connect('config-inited', init_numfig_format, priority=800) app.connect('config-inited', init_numfig_format, priority=800)
app.connect('config-inited', correct_copyright_year, priority=800) app.connect('config-inited', correct_copyright_year, priority=800)
app.connect('config-inited', check_confval_types, priority=800) app.connect('config-inited', check_confval_types, priority=800)

View File

@@ -7,6 +7,7 @@
""" """
import sys import sys
import textwrap
from difflib import unified_diff from difflib import unified_diff
from typing import TYPE_CHECKING, Any, Dict, List, Tuple from typing import TYPE_CHECKING, Any, Dict, List, Tuple
@@ -17,6 +18,7 @@ from docutils.statemachine import StringList
from sphinx import addnodes from sphinx import addnodes
from sphinx.config import Config from sphinx.config import Config
from sphinx.directives import optional_int
from sphinx.locale import __ from sphinx.locale import __
from sphinx.util import logging, parselinenos from sphinx.util import logging, parselinenos
from sphinx.util.docutils import SphinxDirective from sphinx.util.docutils import SphinxDirective
@@ -55,7 +57,7 @@ class Highlight(SphinxDirective):
def dedent_lines(lines: List[str], dedent: int, location: Tuple[str, int] = None) -> List[str]: def dedent_lines(lines: List[str], dedent: int, location: Tuple[str, int] = None) -> List[str]:
if not dedent: if not dedent:
return lines return textwrap.dedent(''.join(lines)).splitlines(True)
if any(s[:dedent].strip() for s in lines): if any(s[:dedent].strip() for s in lines):
logger.warning(__('non-whitespace stripped by dedent'), location=location) logger.warning(__('non-whitespace stripped by dedent'), location=location)
@@ -104,7 +106,7 @@ class CodeBlock(SphinxDirective):
option_spec = { option_spec = {
'force': directives.flag, 'force': directives.flag,
'linenos': directives.flag, 'linenos': directives.flag,
'dedent': int, 'dedent': optional_int,
'lineno-start': int, 'lineno-start': int,
'emphasize-lines': directives.unchanged_required, 'emphasize-lines': directives.unchanged_required,
'caption': directives.unchanged_required, 'caption': directives.unchanged_required,
@@ -378,7 +380,7 @@ class LiteralInclude(SphinxDirective):
optional_arguments = 0 optional_arguments = 0
final_argument_whitespace = True final_argument_whitespace = True
option_spec = { option_spec = {
'dedent': int, 'dedent': optional_int,
'linenos': directives.flag, 'linenos': directives.flag,
'lineno-start': int, 'lineno-start': int,
'lineno-match': directives.flag, 'lineno-match': directives.flag,

View File

@@ -3657,15 +3657,18 @@ class CDomain(Domain):
name = 'c' name = 'c'
label = 'C' label = 'C'
object_types = { object_types = {
'function': ObjType(_('function'), 'func'), # 'identifier' is the one used for xrefs generated in signatures, not in roles
'member': ObjType(_('member'), 'member'), 'member': ObjType(_('member'), 'var', 'member', 'data', 'identifier'),
'macro': ObjType(_('macro'), 'macro'), 'var': ObjType(_('variable'), 'var', 'member', 'data', 'identifier'),
'type': ObjType(_('type'), 'type'), 'function': ObjType(_('function'), 'func', 'identifier', 'type'),
'var': ObjType(_('variable'), 'data'), 'macro': ObjType(_('macro'), 'macro', 'identifier'),
'enum': ObjType(_('enum'), 'enum'), 'struct': ObjType(_('struct'), 'struct', 'identifier', 'type'),
'enumerator': ObjType(_('enumerator'), 'enumerator'), 'union': ObjType(_('union'), 'union', 'identifier', 'type'),
'struct': ObjType(_('struct'), 'struct'), 'enum': ObjType(_('enum'), 'enum', 'identifier', 'type'),
'union': ObjType(_('union'), 'union'), 'enumerator': ObjType(_('enumerator'), 'enumerator', 'identifier'),
'type': ObjType(_('type'), 'identifier', 'type'),
# generated object types
'functionParam': ObjType(_('function parameter'), 'identifier', 'var', 'member', 'data'), # noqa
} }
directives = { directives = {

View File

@@ -7251,14 +7251,18 @@ class CPPDomain(Domain):
name = 'cpp' name = 'cpp'
label = 'C++' label = 'C++'
object_types = { object_types = {
'class': ObjType(_('class'), 'class', 'type', 'identifier'), 'class': ObjType(_('class'), 'class', 'struct', 'identifier', 'type'),
'union': ObjType(_('union'), 'union', 'type', 'identifier'), 'union': ObjType(_('union'), 'union', 'identifier', 'type'),
'function': ObjType(_('function'), 'function', 'func', 'type', 'identifier'), 'function': ObjType(_('function'), 'func', 'identifier', 'type'),
'member': ObjType(_('member'), 'member', 'var'), 'member': ObjType(_('member'), 'member', 'var', 'identifier'),
'type': ObjType(_('type'), 'type', 'identifier'), 'type': ObjType(_('type'), 'identifier', 'type'),
'concept': ObjType(_('concept'), 'concept', 'identifier'), 'concept': ObjType(_('concept'), 'concept', 'identifier'),
'enum': ObjType(_('enum'), 'enum', 'type', 'identifier'), 'enum': ObjType(_('enum'), 'enum', 'identifier', 'type'),
'enumerator': ObjType(_('enumerator'), 'enumerator') 'enumerator': ObjType(_('enumerator'), 'enumerator', 'identifier'),
# generated object types
'functionParam': ObjType(_('function parameter'), 'identifier', 'member', 'var'), # noqa
'templateParam': ObjType(_('template parameter'),
'identifier', 'class', 'struct', 'union', 'member', 'var', 'type'), # noqa
} }
directives = { directives = {
@@ -7435,30 +7439,19 @@ class CPPDomain(Domain):
if typ.startswith('cpp:'): if typ.startswith('cpp:'):
typ = typ[4:] typ = typ[4:]
origTyp = typ
if typ == 'func':
typ = 'function'
if typ == 'struct':
typ = 'class'
declTyp = s.declaration.objectType declTyp = s.declaration.objectType
def checkType() -> bool: def checkType() -> bool:
if typ == 'any' or typ == 'identifier': if typ == 'any':
return True
if declTyp == 'templateParam':
# TODO: perhaps this should be strengthened one day
return True
if declTyp == 'functionParam':
if typ == 'var' or typ == 'member':
return True return True
objtypes = self.objtypes_for_role(typ) objtypes = self.objtypes_for_role(typ)
if objtypes: if objtypes:
return declTyp in objtypes return declTyp in objtypes
print("Type is %s (originally: %s), declType is %s" % (typ, origTyp, declTyp)) print("Type is %s, declaration type is %s" % (typ, declTyp))
assert False assert False
if not checkType(): if not checkType():
logger.warning("cpp:%s targets a %s (%s).", logger.warning("cpp:%s targets a %s (%s).",
origTyp, s.declaration.objectType, typ, s.declaration.objectType,
s.get_full_nested_name(), s.get_full_nested_name(),
location=node) location=node)
@@ -7488,10 +7481,10 @@ class CPPDomain(Domain):
if env.config.add_function_parentheses and typ == 'any': if env.config.add_function_parentheses and typ == 'any':
addParen += 1 addParen += 1
# and now this stuff for operator() # and now this stuff for operator()
if (env.config.add_function_parentheses and typ == 'function' and if (env.config.add_function_parentheses and typ == 'func' and
title.endswith('operator()')): title.endswith('operator()')):
addParen += 1 addParen += 1
if ((typ == 'any' or typ == 'function') and if ((typ == 'any' or typ == 'func') and
title.endswith('operator') and title.endswith('operator') and
displayName.endswith('operator()')): displayName.endswith('operator()')):
addParen += 1 addParen += 1
@@ -7500,7 +7493,7 @@ class CPPDomain(Domain):
if env.config.add_function_parentheses: if env.config.add_function_parentheses:
if typ == 'any' and displayName.endswith('()'): if typ == 'any' and displayName.endswith('()'):
addParen += 1 addParen += 1
elif typ == 'function': elif typ == 'func':
if title.endswith('()') and not displayName.endswith('()'): if title.endswith('()') and not displayName.endswith('()'):
title = title[:-2] title = title[:-2]
else: else:

View File

@@ -730,9 +730,11 @@ class StandardDomain(Domain):
name, env.doc2path(self.labels[name][0]), name, env.doc2path(self.labels[name][0]),
location=node) location=node)
self.anonlabels[name] = docname, labelid self.anonlabels[name] = docname, labelid
if node.tagname in ('section', 'rubric'): if node.tagname == 'section':
title = cast(nodes.title, node[0]) title = cast(nodes.title, node[0])
sectname = clean_astext(title) sectname = clean_astext(title)
elif node.tagname == 'rubric':
sectname = clean_astext(node)
elif self.is_enumerable_node(node): elif self.is_enumerable_node(node):
sectname = self.get_numfig_title(node) sectname = self.get_numfig_title(node)
if not sectname: if not sectname:

View File

@@ -319,8 +319,10 @@ class TocTree:
toctrees = [] # type: List[Element] toctrees = [] # type: List[Element]
if 'includehidden' not in kwargs: if 'includehidden' not in kwargs:
kwargs['includehidden'] = True kwargs['includehidden'] = True
if 'maxdepth' not in kwargs: if 'maxdepth' not in kwargs or not kwargs['maxdepth']:
kwargs['maxdepth'] = 0 kwargs['maxdepth'] = 0
else:
kwargs['maxdepth'] = int(kwargs['maxdepth'])
kwargs['collapse'] = collapse kwargs['collapse'] = collapse
for toctreenode in doctree.traverse(addnodes.toctree): for toctreenode in doctree.traverse(addnodes.toctree):
toctree = self.resolve(docname, builder, toctreenode, prune=True, **kwargs) toctree = self.resolve(docname, builder, toctreenode, prune=True, **kwargs)

View File

@@ -6,6 +6,7 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import warnings
from typing import Any, Callable, Dict, List, Set, Type from typing import Any, Callable, Dict, List, Set, Type
from docutils import nodes from docutils import nodes
@@ -15,6 +16,7 @@ from docutils.statemachine import StringList
from docutils.utils import Reporter, assemble_option_dict from docutils.utils import Reporter, assemble_option_dict
from sphinx.config import Config from sphinx.config import Config
from sphinx.deprecation import RemovedInSphinx50Warning
from sphinx.environment import BuildEnvironment from sphinx.environment import BuildEnvironment
from sphinx.ext.autodoc import Documenter, Options from sphinx.ext.autodoc import Documenter, Options
from sphinx.util import logging from sphinx.util import logging
@@ -48,7 +50,7 @@ class DocumenterBridge:
def __init__(self, env: BuildEnvironment, reporter: Reporter, options: Options, def __init__(self, env: BuildEnvironment, reporter: Reporter, options: Options,
lineno: int, state: Any) -> None: lineno: int, state: Any) -> None:
self.env = env self.env = env
self.reporter = reporter self._reporter = reporter
self.genopt = options self.genopt = options
self.lineno = lineno self.lineno = lineno
self.filename_set = set() # type: Set[str] self.filename_set = set() # type: Set[str]
@@ -58,6 +60,12 @@ class DocumenterBridge:
def warn(self, msg: str) -> None: def warn(self, msg: str) -> None:
logger.warning(msg, location=(self.env.docname, self.lineno)) logger.warning(msg, location=(self.env.docname, self.lineno))
@property
def reporter(self) -> Reporter:
warnings.warn('DocumenterBridge.reporter is deprecated.',
RemovedInSphinx50Warning, stacklevel=2)
return self._reporter
def process_documenter_options(documenter: "Type[Documenter]", config: Config, options: Dict def process_documenter_options(documenter: "Type[Documenter]", config: Config, options: Dict
) -> Options: ) -> Options:

View File

@@ -52,6 +52,10 @@ def parse(code: str, mode: str = 'exec') -> "ast.AST":
try: try:
# type_comments parameter is available on py38+ # type_comments parameter is available on py38+
return ast.parse(code, mode=mode, type_comments=True) # type: ignore return ast.parse(code, mode=mode, type_comments=True) # type: ignore
except SyntaxError:
# Some syntax error found. To ignore invalid type comments, retry parsing without
# type_comments parameter (refs: https://github.com/sphinx-doc/sphinx/issues/8652).
return ast.parse(code, mode=mode)
except TypeError: except TypeError:
# fallback to ast module. # fallback to ast module.
# typed_ast is used to parse type_comments if installed. # typed_ast is used to parse type_comments if installed.

View File

@@ -161,7 +161,9 @@ date_format_mappings = {
'%X': 'medium', # Locales appropriate time representation. '%X': 'medium', # Locales appropriate time representation.
'%y': 'YY', # Year without century as a zero-padded decimal number. '%y': 'YY', # Year without century as a zero-padded decimal number.
'%Y': 'yyyy', # Year with century as a decimal number. '%Y': 'yyyy', # Year with century as a decimal number.
'%Z': 'zzzz', # Time zone name (no characters if no time zone exists). '%Z': 'zzz', # Time zone name (no characters if no time zone exists).
'%z': 'ZZZ', # UTC offset in the form ±HHMM[SS[.ffffff]]
# (empty string if the object is naive).
'%%': '%', '%%': '%',
} }

View File

@@ -305,7 +305,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
if figure_id in self.builder.fignumbers.get(key, {}): if figure_id in self.builder.fignumbers.get(key, {}):
self.body.append('<span class="caption-number">') self.body.append('<span class="caption-number">')
prefix = self.builder.config.numfig_format.get(figtype) prefix = self.config.numfig_format.get(figtype)
if prefix is None: if prefix is None:
msg = __('numfig_format is not defined for %s') % figtype msg = __('numfig_format is not defined for %s') % figtype
logger.warning(msg) logger.warning(msg)
@@ -429,14 +429,10 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
linenos = node.get('linenos', False) linenos = node.get('linenos', False)
highlight_args = node.get('highlight_args', {}) highlight_args = node.get('highlight_args', {})
highlight_args['force'] = node.get('force', False) highlight_args['force'] = node.get('force', False)
if lang is self.builder.config.highlight_language: opts = self.config.highlight_options.get(lang, {})
# only pass highlighter options for original language
opts = self.builder.config.highlight_options
else:
opts = {}
if linenos and self.builder.config.html_codeblock_linenos_style: if linenos and self.config.html_codeblock_linenos_style:
linenos = self.builder.config.html_codeblock_linenos_style linenos = self.config.html_codeblock_linenos_style
highlighted = self.highlighter.highlight_block( highlighted = self.highlighter.highlight_block(
node.rawsource, lang, opts=opts, linenos=linenos, node.rawsource, lang, opts=opts, linenos=linenos,

View File

@@ -278,7 +278,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
if figure_id in self.builder.fignumbers.get(key, {}): if figure_id in self.builder.fignumbers.get(key, {}):
self.body.append('<span class="caption-number">') self.body.append('<span class="caption-number">')
prefix = self.builder.config.numfig_format.get(figtype) prefix = self.config.numfig_format.get(figtype)
if prefix is None: if prefix is None:
msg = __('numfig_format is not defined for %s') % figtype msg = __('numfig_format is not defined for %s') % figtype
logger.warning(msg) logger.warning(msg)
@@ -382,14 +382,10 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
linenos = node.get('linenos', False) linenos = node.get('linenos', False)
highlight_args = node.get('highlight_args', {}) highlight_args = node.get('highlight_args', {})
highlight_args['force'] = node.get('force', False) highlight_args['force'] = node.get('force', False)
if lang is self.builder.config.highlight_language: opts = self.config.highlight_options.get(lang, {})
# only pass highlighter options for original language
opts = self.builder.config.highlight_options
else:
opts = {}
if linenos and self.builder.config.html_codeblock_linenos_style: if linenos and self.config.html_codeblock_linenos_style:
linenos = self.builder.config.html_codeblock_linenos_style linenos = self.config.html_codeblock_linenos_style
highlighted = self.highlighter.highlight_block( highlighted = self.highlighter.highlight_block(
node.rawsource, lang, opts=opts, linenos=linenos, node.rawsource, lang, opts=opts, linenos=linenos,

View File

@@ -521,7 +521,7 @@ class LaTeXTranslator(SphinxTranslator):
ret = [] ret = []
# latex_domain_indices can be False/True or a list of index names # latex_domain_indices can be False/True or a list of index names
indices_config = self.builder.config.latex_domain_indices indices_config = self.config.latex_domain_indices
if indices_config: if indices_config:
for domain in self.builder.env.domains.values(): for domain in self.builder.env.domains.values():
for indexcls in domain.indices: for indexcls in domain.indices:
@@ -541,7 +541,7 @@ class LaTeXTranslator(SphinxTranslator):
def render(self, template_name: str, variables: Dict) -> str: def render(self, template_name: str, variables: Dict) -> str:
renderer = LaTeXRenderer(latex_engine=self.config.latex_engine) renderer = LaTeXRenderer(latex_engine=self.config.latex_engine)
for template_dir in self.builder.config.templates_path: for template_dir in self.config.templates_path:
template = path.join(self.builder.confdir, template_dir, template = path.join(self.builder.confdir, template_dir,
template_name) template_name)
if path.exists(template): if path.exists(template):
@@ -961,7 +961,7 @@ class LaTeXTranslator(SphinxTranslator):
cell = self.table.cell() cell = self.table.cell()
context = '' context = ''
if cell.width > 1: if cell.width > 1:
if self.builder.config.latex_use_latex_multicolumn: if self.config.latex_use_latex_multicolumn:
if self.table.col == 0: if self.table.col == 0:
self.body.append('\\multicolumn{%d}{|l|}{%%\n' % cell.width) self.body.append('\\multicolumn{%d}{|l|}{%%\n' % cell.width)
else: else:
@@ -1541,7 +1541,7 @@ class LaTeXTranslator(SphinxTranslator):
id = self.curfilestack[-1] + ':' + uri[1:] id = self.curfilestack[-1] + ':' + uri[1:]
self.body.append(self.hyperlink(id)) self.body.append(self.hyperlink(id))
self.body.append(r'\emph{') self.body.append(r'\emph{')
if self.builder.config.latex_show_pagerefs and not \ if self.config.latex_show_pagerefs and not \
self.in_production_list: self.in_production_list:
self.context.append('}}} (%s)' % self.hyperpageref(id)) self.context.append('}}} (%s)' % self.hyperpageref(id))
else: else:
@@ -1565,8 +1565,7 @@ class LaTeXTranslator(SphinxTranslator):
self.body.append(r'\sphinxtermref{') self.body.append(r'\sphinxtermref{')
else: else:
self.body.append(r'\sphinxcrossref{') self.body.append(r'\sphinxcrossref{')
if self.builder.config.latex_show_pagerefs and not \ if self.config.latex_show_pagerefs and not self.in_production_list:
self.in_production_list:
self.context.append('}}} (%s)' % self.hyperpageref(id)) self.context.append('}}} (%s)' % self.hyperpageref(id))
else: else:
self.context.append('}}}') self.context.append('}}}')
@@ -1750,11 +1749,7 @@ class LaTeXTranslator(SphinxTranslator):
linenos = node.get('linenos', False) linenos = node.get('linenos', False)
highlight_args = node.get('highlight_args', {}) highlight_args = node.get('highlight_args', {})
highlight_args['force'] = node.get('force', False) highlight_args['force'] = node.get('force', False)
if lang is self.builder.config.highlight_language: opts = self.config.highlight_options.get(lang, {})
# only pass highlighter options for original language
opts = self.builder.config.highlight_options
else:
opts = {}
hlcode = self.highlighter.highlight_block( hlcode = self.highlighter.highlight_block(
node.rawsource, lang, opts=opts, linenos=linenos, node.rawsource, lang, opts=opts, linenos=linenos,
@@ -2016,12 +2011,12 @@ class LaTeXTranslator(SphinxTranslator):
else: else:
from sphinx.util.math import wrap_displaymath from sphinx.util.math import wrap_displaymath
self.body.append(wrap_displaymath(node.astext(), label, self.body.append(wrap_displaymath(node.astext(), label,
self.builder.config.math_number_all)) self.config.math_number_all))
raise nodes.SkipNode raise nodes.SkipNode
def visit_math_reference(self, node: Element) -> None: def visit_math_reference(self, node: Element) -> None:
label = "equation:%s:%s" % (node['docname'], node['target']) label = "equation:%s:%s" % (node['docname'], node['target'])
eqref_format = self.builder.config.math_eqref_format eqref_format = self.config.math_eqref_format
if eqref_format: if eqref_format:
try: try:
ref = r'\ref{%s}' % label ref = r'\ref{%s}' % label

View File

@@ -287,8 +287,7 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator):
if uri.startswith('mailto:') or uri.startswith('http:') or \ if uri.startswith('mailto:') or uri.startswith('http:') or \
uri.startswith('https:') or uri.startswith('ftp:'): uri.startswith('https:') or uri.startswith('ftp:'):
# if configured, put the URL after the link # if configured, put the URL after the link
if self.builder.config.man_show_urls and \ if self.config.man_show_urls and node.astext() != uri:
node.astext() != uri:
if uri.startswith('mailto:'): if uri.startswith('mailto:'):
uri = uri[7:] uri = uri[7:]
self.body.extend([ self.body.extend([

View File

@@ -232,12 +232,12 @@ class TexinfoTranslator(SphinxTranslator):
'author': self.settings.author, 'author': self.settings.author,
# if empty, use basename of input file # if empty, use basename of input file
'filename': self.settings.texinfo_filename, 'filename': self.settings.texinfo_filename,
'release': self.escape(self.builder.config.release), 'release': self.escape(self.config.release),
'project': self.escape(self.builder.config.project), 'project': self.escape(self.config.project),
'copyright': self.escape(self.builder.config.copyright), 'copyright': self.escape(self.config.copyright),
'date': self.escape(self.builder.config.today or 'date': self.escape(self.config.today or
format_date(self.builder.config.today_fmt or _('%b %d, %Y'), format_date(self.config.today_fmt or _('%b %d, %Y'),
language=self.builder.config.language)) language=self.config.language))
}) })
# title # title
title = self.settings.title # type: str title = self.settings.title # type: str
@@ -433,7 +433,7 @@ class TexinfoTranslator(SphinxTranslator):
self.add_menu_entries(entries) self.add_menu_entries(entries)
if (node_name != 'Top' or if (node_name != 'Top' or
not self.node_menus[entries[0]] or not self.node_menus[entries[0]] or
self.builder.config.texinfo_no_detailmenu): self.config.texinfo_no_detailmenu):
self.body.append('\n@end menu\n') self.body.append('\n@end menu\n')
return return
@@ -483,7 +483,7 @@ class TexinfoTranslator(SphinxTranslator):
ret.append('@end menu\n') ret.append('@end menu\n')
return ''.join(ret) return ''.join(ret)
indices_config = self.builder.config.texinfo_domain_indices indices_config = self.config.texinfo_domain_indices
if indices_config: if indices_config:
for domain in self.builder.env.domains.values(): for domain in self.builder.env.domains.values():
for indexcls in domain.indices: for indexcls in domain.indices:
@@ -738,7 +738,7 @@ class TexinfoTranslator(SphinxTranslator):
else: else:
uri = self.escape_arg(uri) uri = self.escape_arg(uri)
name = self.escape_arg(name) name = self.escape_arg(name)
show_urls = self.builder.config.texinfo_show_urls show_urls = self.config.texinfo_show_urls
if self.in_footnote: if self.in_footnote:
show_urls = 'inline' show_urls = 'inline'
if not name or uri == name: if not name or uri == name:
@@ -1394,9 +1394,8 @@ class TexinfoTranslator(SphinxTranslator):
# use the full name of the objtype for the category # use the full name of the objtype for the category
try: try:
domain = self.builder.env.get_domain(node.parent['domain']) domain = self.builder.env.get_domain(node.parent['domain'])
primary = self.builder.config.primary_domain
name = domain.get_type_name(domain.object_types[objtype], name = domain.get_type_name(domain.object_types[objtype],
primary == domain.name) self.config.primary_domain == domain.name)
except (KeyError, ExtensionError): except (KeyError, ExtensionError):
name = objtype name = objtype
# by convention, the deffn category should be capitalized like a title # by convention, the deffn category should be capitalized like a title

View File

@@ -0,0 +1,4 @@
exclude_patterns = ['_build']
extensions = [
'sphinx.ext.intersphinx',
]

View File

@@ -0,0 +1,62 @@
.. c:member:: void __member = _member
- :any:`_member`
- :c:member:`_member`
- :c:var:`_member`
- :c:data:`_member`
.. c:member:: void __var = _var
- :any:`_var`
- :c:member:`_var`
- :c:var:`_var`
- :c:data:`_var`
.. c:member:: void __function = _function
- :any:`_function`
- :c:func:`_function`
- :c:type:`_function`
.. c:member:: void __macro = _macro
- :any:`_macro`
- :c:macro:`_macro`
.. c:type:: _struct __struct
struct _struct __structTagged
- :any:`_struct`
- :c:struct:`_struct`
- :c:type:`_struct`
.. c:type:: _union __union
union _union __unionTagged
- :any:`_union`
- :c:union:`_union`
- :c:type:`_union`
.. c:type:: _enum __enum
enum _enum __enumTagged
- :any:`_enum`
- :c:enum:`_enum`
- :c:type:`_enum`
.. c:member:: void __enumerator = _enumerator
- :any:`_enumerator`
- :c:enumerator:`_enumerator`
.. c:type:: _type __type
- :any:`_type`
- :c:type:`_type`
.. c:member:: void __functionParam = _functionParam.param
- :any:`_functionParam.param`
- :c:member:`_functionParam.param`
- :c:var:`_functionParam.param`
- :c:data:`_functionParam.param`

View File

@@ -0,0 +1,4 @@
exclude_patterns = ['_build']
extensions = [
'sphinx.ext.intersphinx',
]

View File

@@ -0,0 +1,112 @@
.. cpp:type:: _class __class
- :any:`_class`
- :cpp:any:`_class`
- :cpp:class:`_class`
- :cpp:struct:`_class`
- :cpp:type:`_class`
.. cpp:type:: _struct __struct
- :any:`_struct`
- :cpp:any:`_struct`
- :cpp:class:`_struct`
- :cpp:struct:`_struct`
- :cpp:type:`_struct`
.. cpp:type:: _union __union
- :any:`_union`
- :cpp:any:`_union`
- :cpp:union:`_union`
- :cpp:type:`_union`
.. cpp:member:: void __function = _function
- :any:`_function`
- :cpp:any:`_function`
- :cpp:func:`_function`
- :cpp:type:`_function`
.. cpp:member:: void __member = _member
- :any:`_member`
- :cpp:any:`_member`
- :cpp:member:`_member`
- :cpp:var:`_member`
.. cpp:member:: void __var = _var
- :any:`_var`
- :cpp:any:`_var`
- :cpp:member:`_var`
- :cpp:var:`_var`
.. cpp:type:: _type __type
- :any:`_type`
- :cpp:any:`_type`
- :cpp:type:`_type`
.. cpp:function:: template<_concept T> void __concept()
- :any:`_concept`
- :cpp:any:`_concept`
- :cpp:concept:`_concept`
.. cpp:type:: _enum __enum
- :any:`_enum`
- :cpp:any:`_enum`
- :cpp:enum:`_enum`
- :cpp:type:`_enum`
.. cpp:type:: _enumStruct __enumStruct
- :any:`_enumStruct`
- :cpp:any:`_enumStruct`
- :cpp:enum:`_enumStruct`
- :cpp:type:`_enumStruct`
.. cpp:type:: _enumClass __enumClass
- :any:`_enumClass`
- :cpp:any:`_enumClass`
- :cpp:enum:`_enumClass`
- :cpp:type:`_enumClass`
.. cpp:member:: void __enumerator = _enumerator
- :any:`_enumerator`
- :cpp:any:`_enumerator`
- :cpp:enumerator:`_enumerator`
.. cpp:member:: void __scopedEnumerator = _enumStruct::_scopedEnumerator
- :any:`_enumStruct::_scopedEnumerator`
- :cpp:any:`_enumStruct::_scopedEnumerator`
- :cpp:enumerator:`_enumStruct::_scopedEnumerator`
.. cpp:member:: void __enumerator2 = _enum::_enumerator
- :any:`_enum::_enumerator`
- :cpp:any:`_enum::_enumerator`
- :cpp:enumerator:`_enum::_enumerator`
.. cpp:member:: void __functionParam = _functionParam::param
- :any:`_functionParam::param`
- :cpp:any:`_functionParam::param`
- :cpp:member:`_functionParam::param`
- :cpp:var:`_functionParam::param`
.. cpp:type:: _templateParam::TParam __templateParam
- :any:`_templateParam::TParam`
- :cpp:any:`_templateParam::TParam`
- :cpp:type:`_templateParam::TParam`
- :cpp:member:`_templateParam::TParam`
- :cpp:var:`_templateParam::TParam`
- :cpp:class:`_templateParam::TParam`
- :cpp:struct:`_templateParam::TParam`
- :cpp:union:`_templateParam::TParam`

View File

@@ -123,37 +123,37 @@
:class:`TParamType` :class:`TParamType`
:struct:`TParamType` :struct:`TParamType`
:union:`TParamType` :union:`TParamType`
:func:`TParamType` function
:member:`TParamType` :member:`TParamType`
:var:`TParamType` :var:`TParamType`
:type:`TParamType` :type:`TParamType`
:concept:`TParamType` concept
:enum:`TParamType` enum
:enumerator:`TParamType` enumerator
:cpp:any:`TParamVar` :cpp:any:`TParamVar`
:class:`TParamVar` :class:`TParamVar`
:struct:`TParamVar` :struct:`TParamVar`
:union:`TParamVar` :union:`TParamVar`
:func:`TParamVar` function
:member:`TParamVar` :member:`TParamVar`
:var:`TParamVar` :var:`TParamVar`
:type:`TParamVar` :type:`TParamVar`
:concept:`TParamVar` concept
:enum:`TParamVar` enum
:enumerator:`TParamVar` enumerator
:cpp:any:`TParamTemplate` :cpp:any:`TParamTemplate`
:class:`TParamTemplate` :class:`TParamTemplate`
:struct:`TParamTemplate` :struct:`TParamTemplate`
:union:`TParamTemplate` :union:`TParamTemplate`
:func:`TParamTemplate` function
:member:`TParamTemplate` :member:`TParamTemplate`
:var:`TParamTemplate` :var:`TParamTemplate`
:type:`TParamTemplate` :type:`TParamTemplate`
:concept:`TParamTemplate` concept
:enum:`TParamTemplate` enum
:enumerator:`TParamTemplate` enumerator
.. function:: void FunctionParams(int FunctionParam) .. function:: void FunctionParams(int FunctionParam)

View File

@@ -114,35 +114,35 @@
class class
struct struct
union union
func :func:`TParamType`
member member
var var
type type
concept :concept:`TParamType`
enum :enum:`TParamType`
enumerator :enumerator:`TParamType`
class class
struct struct
union union
func :func:`TParamVar`
member member
var var
type type
concept :concept:`TParamVar`
enum :enum:`TParamVar`
enumerator :enumerator:`TParamVar`
class class
struct struct
union union
func :func:`TParamTemplate`
member member
var var
type type
concept :concept:`TParamTemplate`
enum :enum:`TParamTemplate`
enumerator :enumerator:`TParamTemplate`
.. function:: void FunctionParams(int FunctionParam) .. function:: void FunctionParams(int FunctionParam)

View File

@@ -0,0 +1,10 @@
abbrev
======
.. currentmodule:: module_a.submodule
* normal: :py:meth:`module_a.submodule.ModTopLevel.mod_child_1`
* relative: :py:meth:`.ModTopLevel.mod_child_1`
* short name: :py:meth:`~module_a.submodule.ModTopLevel.mod_child_1`
* relative + short name: :py:meth:`~.ModTopLevel.mod_child_1`
* short name + relative: :py:meth:`~.ModTopLevel.mod_child_1`

View File

@@ -0,0 +1,4 @@
highlight_options = {
'default': {'default_option': True},
'python': {'python_option': True}
}

View File

@@ -0,0 +1,14 @@
test-highlight_options
======================
.. code-block::
blah blah blah
.. code-block:: python
blah blah blah
.. code-block:: java
blah blah blah

View File

@@ -12,6 +12,7 @@ import os
import re import re
from distutils.version import LooseVersion from distutils.version import LooseVersion
from itertools import chain, cycle from itertools import chain, cycle
from unittest.mock import ANY, call, patch
import pygments import pygments
import pytest import pytest
@@ -1607,3 +1608,36 @@ def test_html_codeblock_linenos_style_inline(app):
assert '<span class="linenos">1</span>' in content assert '<span class="linenos">1</span>' in content
else: else:
assert '<span class="lineno">1 </span>' in content assert '<span class="lineno">1 </span>' in content
@pytest.mark.sphinx('html', testroot='highlight_options')
def test_highlight_options(app):
subject = app.builder.highlighter
with patch.object(subject, 'highlight_block', wraps=subject.highlight_block) as highlight:
app.build()
call_args = highlight.call_args_list
assert len(call_args) == 3
assert call_args[0] == call(ANY, 'default', force=False, linenos=False,
location=ANY, opts={'default_option': True})
assert call_args[1] == call(ANY, 'python', force=False, linenos=False,
location=ANY, opts={'python_option': True})
assert call_args[2] == call(ANY, 'java', force=False, linenos=False,
location=ANY, opts={})
@pytest.mark.sphinx('html', testroot='highlight_options',
confoverrides={'highlight_options': {'default_option': True}})
def test_highlight_options_old(app):
subject = app.builder.highlighter
with patch.object(subject, 'highlight_block', wraps=subject.highlight_block) as highlight:
app.build()
call_args = highlight.call_args_list
assert len(call_args) == 3
assert call_args[0] == call(ANY, 'default', force=False, linenos=False,
location=ANY, opts={'default_option': True})
assert call_args[1] == call(ANY, 'python', force=False, linenos=False,
location=ANY, opts={})
assert call_args[2] == call(ANY, 'java', force=False, linenos=False,
location=ANY, opts={})

View File

@@ -250,6 +250,14 @@ def test_LiteralIncludeReader_dedent(literal_inc_path):
" pass\n" " pass\n"
"\n") "\n")
# dedent: None
options = {'lines': '9-11', 'dedent': None}
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
content, lines = reader.read()
assert content == ("def baz():\n"
" pass\n"
"\n")
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows") @pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
def test_LiteralIncludeReader_tabwidth(testroot): def test_LiteralIncludeReader_tabwidth(testroot):

View File

@@ -7,6 +7,8 @@
:copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import zlib
from xml.etree import ElementTree from xml.etree import ElementTree
import pytest import pytest
@@ -14,6 +16,7 @@ import pytest
from sphinx import addnodes from sphinx import addnodes
from sphinx.addnodes import desc from sphinx.addnodes import desc
from sphinx.domains.c import DefinitionError, DefinitionParser, Symbol, _id_prefix, _max_id from sphinx.domains.c import DefinitionError, DefinitionParser, Symbol, _id_prefix, _max_id
from sphinx.ext.intersphinx import load_mappings, normalize_intersphinx_mapping
from sphinx.testing import restructuredtext from sphinx.testing import restructuredtext
from sphinx.testing.util import assert_node from sphinx.testing.util import assert_node
@@ -642,3 +645,52 @@ def test_noindexentry(app):
assert_node(doctree, (addnodes.index, desc, addnodes.index, desc)) assert_node(doctree, (addnodes.index, desc, addnodes.index, desc))
assert_node(doctree[0], addnodes.index, entries=[('single', 'f (C function)', 'c.f', '', None)]) assert_node(doctree[0], addnodes.index, entries=[('single', 'f (C function)', 'c.f', '', None)])
assert_node(doctree[2], addnodes.index, entries=[]) assert_node(doctree[2], addnodes.index, entries=[])
@pytest.mark.sphinx(testroot='domain-c-intersphinx', confoverrides={'nitpicky': True})
def test_intersphinx(tempdir, app, status, warning):
origSource = """\
.. c:member:: int _member
.. c:var:: int _var
.. c:function:: void _function()
.. c:macro:: _macro
.. c:struct:: _struct
.. c:union:: _union
.. c:enum:: _enum
.. c:enumerator:: _enumerator
.. c:type:: _type
.. c:function:: void _functionParam(int param)
""" # noqa
inv_file = tempdir / 'inventory'
inv_file.write_bytes(b'''\
# Sphinx inventory version 2
# Project: C Intersphinx Test
# Version:
# The remainder of this file is compressed using zlib.
''' + zlib.compress(b'''\
_enum c:enum 1 index.html#c.$ -
_enum._enumerator c:enumerator 1 index.html#c.$ -
_enumerator c:enumerator 1 index.html#c._enum.$ -
_function c:function 1 index.html#c.$ -
_functionParam c:function 1 index.html#c.$ -
_functionParam.param c:functionParam 1 index.html#c._functionParam -
_macro c:macro 1 index.html#c.$ -
_member c:member 1 index.html#c.$ -
_struct c:struct 1 index.html#c.$ -
_type c:type 1 index.html#c.$ -
_union c:union 1 index.html#c.$ -
_var c:member 1 index.html#c.$ -
''')) # noqa
app.config.intersphinx_mapping = {
'https://localhost/intersphinx/c/': inv_file,
}
app.config.intersphinx_cache_limit = 0
# load the inventory and check if it's done correctly
normalize_intersphinx_mapping(app, app.config)
load_mappings(app)
app.builder.build_all()
ws = filter_warnings(warning, "index")
assert len(ws) == 0

View File

@@ -9,6 +9,7 @@
""" """
import re import re
import zlib
import pytest import pytest
@@ -17,6 +18,7 @@ from sphinx import addnodes
from sphinx.addnodes import desc from sphinx.addnodes import desc
from sphinx.domains.cpp import (DefinitionError, DefinitionParser, NoOldIdError, Symbol, from sphinx.domains.cpp import (DefinitionError, DefinitionParser, NoOldIdError, Symbol,
_id_prefix, _max_id) _id_prefix, _max_id)
from sphinx.ext.intersphinx import load_mappings, normalize_intersphinx_mapping
from sphinx.testing import restructuredtext from sphinx.testing import restructuredtext
from sphinx.testing.util import assert_node from sphinx.testing.util import assert_node
@@ -1048,8 +1050,8 @@ def test_build_domain_cpp_misuse_of_roles(app, status, warning):
('concept', ['concept']), ('concept', ['concept']),
('enum', ['type', 'enum']), ('enum', ['type', 'enum']),
('enumerator', ['enumerator']), ('enumerator', ['enumerator']),
('tParam', ['class', 'struct', 'union', 'func', 'member', 'var', 'type', 'concept', 'enum', 'enumerator', 'functionParam']),
('functionParam', ['member', 'var']), ('functionParam', ['member', 'var']),
('templateParam', ['class', 'struct', 'union', 'member', 'var', 'type']),
] ]
warn = [] warn = []
for targetType, roles in ok: for targetType, roles in ok:
@@ -1057,6 +1059,9 @@ def test_build_domain_cpp_misuse_of_roles(app, status, warning):
for r in allRoles: for r in allRoles:
if r not in roles: if r not in roles:
warn.append("WARNING: cpp:{} targets a {} (".format(r, txtTargetType)) warn.append("WARNING: cpp:{} targets a {} (".format(r, txtTargetType))
if targetType == 'templateParam':
warn.append("WARNING: cpp:{} targets a {} (".format(r, txtTargetType))
warn.append("WARNING: cpp:{} targets a {} (".format(r, txtTargetType))
warn = list(sorted(warn)) warn = list(sorted(warn))
for w in ws: for w in ws:
assert "targets a" in w assert "targets a" in w
@@ -1245,3 +1250,66 @@ def test_mix_decl_duplicate(app, warning):
assert "index.rst:3: WARNING: Duplicate C++ declaration, also defined at index:1." in ws[2] assert "index.rst:3: WARNING: Duplicate C++ declaration, also defined at index:1." in ws[2]
assert "Declaration is '.. cpp:struct:: A'." in ws[3] assert "Declaration is '.. cpp:struct:: A'." in ws[3]
assert ws[4] == "" assert ws[4] == ""
@pytest.mark.sphinx(testroot='domain-cpp-intersphinx', confoverrides={'nitpicky': True})
def test_intersphinx(tempdir, app, status, warning):
origSource = """\
.. cpp:class:: _class
.. cpp:struct:: _struct
.. cpp:union:: _union
.. cpp:function:: void _function()
.. cpp:member:: int _member
.. cpp:var:: int _var
.. cpp:type:: _type
.. cpp:concept:: template<typename T> _concept
.. cpp:enum:: _enum
.. cpp:enumerator:: _enumerator
.. cpp:enum-struct:: _enumStruct
.. cpp:enumerator:: _scopedEnumerator
.. cpp:enum-class:: _enumClass
.. cpp:function:: void _functionParam(int param)
.. cpp:function:: template<typename TParam> void _templateParam()
""" # noqa
inv_file = tempdir / 'inventory'
inv_file.write_bytes(b'''\
# Sphinx inventory version 2
# Project: C Intersphinx Test
# Version:
# The remainder of this file is compressed using zlib.
''' + zlib.compress(b'''\
_class cpp:class 1 index.html#_CPPv46$ -
_concept cpp:concept 1 index.html#_CPPv4I0E8$ -
_concept::T cpp:templateParam 1 index.html#_CPPv4I0E8_concept -
_enum cpp:enum 1 index.html#_CPPv45$ -
_enum::_enumerator cpp:enumerator 1 index.html#_CPPv4N5_enum11_enumeratorE -
_enumClass cpp:enum 1 index.html#_CPPv410$ -
_enumStruct cpp:enum 1 index.html#_CPPv411$ -
_enumStruct::_scopedEnumerator cpp:enumerator 1 index.html#_CPPv4N11_enumStruct17_scopedEnumeratorE -
_enumerator cpp:enumerator 1 index.html#_CPPv4N5_enum11_enumeratorE -
_function cpp:function 1 index.html#_CPPv49_functionv -
_functionParam cpp:function 1 index.html#_CPPv414_functionParami -
_functionParam::param cpp:functionParam 1 index.html#_CPPv414_functionParami -
_member cpp:member 1 index.html#_CPPv47$ -
_struct cpp:class 1 index.html#_CPPv47$ -
_templateParam cpp:function 1 index.html#_CPPv4I0E14_templateParamvv -
_templateParam::TParam cpp:templateParam 1 index.html#_CPPv4I0E14_templateParamvv -
_type cpp:type 1 index.html#_CPPv45$ -
_union cpp:union 1 index.html#_CPPv46$ -
_var cpp:member 1 index.html#_CPPv44$ -
''')) # noqa
app.config.intersphinx_mapping = {
'https://localhost/intersphinx/cpp/': inv_file,
}
app.config.intersphinx_cache_limit = 0
# load the inventory and check if it's done correctly
normalize_intersphinx_mapping(app, app.config)
load_mappings(app)
app.builder.build_all()
ws = filter_warnings(warning, "index")
assert len(ws) == 0

View File

@@ -8,6 +8,7 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import re
import sys import sys
from unittest.mock import Mock from unittest.mock import Mock
@@ -132,6 +133,29 @@ def test_domain_py_xrefs(app, status, warning):
assert len(refnodes) == 2 assert len(refnodes) == 2
@pytest.mark.sphinx('html', testroot='domain-py')
def test_domain_py_xrefs_abbreviations(app, status, warning):
app.builder.build_all()
content = (app.outdir / 'abbr.html').read_text()
assert re.search(r'normal: <a .* href="module.html#module_a.submodule.ModTopLevel.'
r'mod_child_1" .*><.*>module_a.submodule.ModTopLevel.mod_child_1\(\)'
r'<.*></a>',
content)
assert re.search(r'relative: <a .* href="module.html#module_a.submodule.ModTopLevel.'
r'mod_child_1" .*><.*>ModTopLevel.mod_child_1\(\)<.*></a>',
content)
assert re.search(r'short name: <a .* href="module.html#module_a.submodule.ModTopLevel.'
r'mod_child_1" .*><.*>mod_child_1\(\)<.*></a>',
content)
assert re.search(r'relative \+ short name: <a .* href="module.html#module_a.submodule.'
r'ModTopLevel.mod_child_1" .*><.*>mod_child_1\(\)<.*></a>',
content)
assert re.search(r'short name \+ relative: <a .* href="module.html#module_a.submodule.'
r'ModTopLevel.mod_child_1" .*><.*>mod_child_1\(\)<.*></a>',
content)
@pytest.mark.sphinx('dummy', testroot='domain-py') @pytest.mark.sphinx('dummy', testroot='domain-py')
def test_domain_py_objects(app, status, warning): def test_domain_py_objects(app, status, warning):
app.builder.build_all() app.builder.build_all()

View File

@@ -409,3 +409,13 @@ def test_disabled_docref(app):
assert_node(doctree, ([nodes.paragraph, ([pending_xref, nodes.inline, "index"], assert_node(doctree, ([nodes.paragraph, ([pending_xref, nodes.inline, "index"],
"\n", "\n",
[nodes.inline, "index"])],)) [nodes.inline, "index"])],))
def test_labeled_rubric(app):
text = (".. _label:\n"
".. rubric:: blah *blah* blah\n")
restructuredtext.parse(app, text)
domain = app.env.get_domain("std")
assert 'label' in domain.labels
assert domain.labels['label'] == ('index', 'label', 'blah blah blah')

View File

@@ -87,6 +87,12 @@ def test_format_date():
assert i18n.format_date(format, date=datet) == 'Feb 7, 2016, 5:11:17 AM' assert i18n.format_date(format, date=datet) == 'Feb 7, 2016, 5:11:17 AM'
assert i18n.format_date(format, date=date) == 'Feb 7, 2016' assert i18n.format_date(format, date=date) == 'Feb 7, 2016'
# timezone
format = '%Z'
assert i18n.format_date(format, date=datet) == 'UTC'
format = '%z'
assert i18n.format_date(format, date=datet) == '+0000'
@pytest.mark.xfail(os.name != 'posix', reason="Path separators don't match on windows") @pytest.mark.xfail(os.name != 'posix', reason="Path separators don't match on windows")
def test_get_filename_for_language(app): def test_get_filename_for_language(app):