diff options
author | Takeshi KOMIYA <i.tkomiya@gmail.com> | 2021-01-16 21:51:46 +0900 |
---|---|---|
committer | Takeshi KOMIYA <i.tkomiya@gmail.com> | 2021-01-16 21:51:46 +0900 |
commit | 30f8640bab786b28e2fbc3a12a4cf212e6f953d1 (patch) | |
tree | f5cf23900a7bc509fe970262195995ddc526fda1 | |
parent | 5460ea103bd91ce910e50e11e05c1e5340c2a9e0 (diff) | |
parent | 7c340e1c1c43f173f11efc14feb29cd08cedb995 (diff) | |
download | sphinx-git-30f8640bab786b28e2fbc3a12a4cf212e6f953d1.tar.gz |
Merge branch '3.x'
36 files changed, 596 insertions, 119 deletions
diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 000000000..680a0e3b5 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,8 @@ +version: 2 +python: + version: 3 + install: + - method: pip + path: . + extra_requirements: + - docs @@ -70,6 +70,7 @@ Deprecated ---------- * ``sphinx.ext.autodoc.AttributeDocumenter.isinstanceattribute()`` +* ``sphinx.ext.autodoc.directive.DocumenterBridge.reporter`` * ``sphinx.ext.autodoc.importer.get_module_members()`` Features added @@ -87,7 +88,11 @@ Features added * #8649: imgconverter: Skip availability check if builder supports the image type * #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` +* #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 ---------- @@ -97,13 +102,21 @@ Bugs fixed * #8592: autodoc: ``:meta public:`` does not effect to variables * #8594: autodoc: empty __all__ attribute is ignored * #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 :recursive: option * #8618: html: kbd role produces incorrect HTML when compound-key separators (-, + or ^) are used as keystrokes * #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 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 -------- diff --git a/doc/extdev/deprecated.rst b/doc/extdev/deprecated.rst index 47b0f68e7..117cabad8 100644 --- a/doc/extdev/deprecated.rst +++ b/doc/extdev/deprecated.rst @@ -107,6 +107,11 @@ The following is a list of deprecated interfaces. - 5.0 - ``sphinx.ext.autodoc.DataDocumenter`` + * - ``sphinx.ext.autodoc.directive.DocumenterBridge.reporter`` + - 3.5 + - 5.0 + - ``sphinx.util.logging`` + * - ``sphinx.ext.autodoc.importer._getannotations()`` - 3.4 - 4.0 diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst index 7a92cc12a..ba5c15bb3 100644 --- a/doc/usage/configuration.rst +++ b/doc/usage/configuration.rst @@ -581,12 +581,27 @@ General configuration .. confval:: highlight_options - A dictionary of options that modify how the lexer specified by - :confval:`highlight_language` generates highlighted source code. These are - lexer-specific; for the options understood by each, see the - `Pygments documentation <https://pygments.org/docs/lexers>`_. + A dictionary that maps language names to options for the lexer modules of + Pygments. These are lexer-specific; for the options understood by each, + see the `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 + .. versionchanged:: 3.5 + + Allow to configure highlight options for multiple languages .. confval:: pygments_style @@ -944,8 +959,11 @@ that use Sphinx's HTMLWriter class. .. confval:: html_baseurl - The URL which points to the root of the HTML documentation. It is used to - indicate the location of document like ``canonical_url``. + The base URL which points to the root of the HTML documentation. It is used + 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 diff --git a/doc/usage/extensions/autodoc.rst b/doc/usage/extensions/autodoc.rst index 2bd220c6d..ed85c941e 100644 --- a/doc/usage/extensions/autodoc.rst +++ b/doc/usage/extensions/autodoc.rst @@ -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`. For example: - .. code-block:: rst + .. code-block:: python def my_function(my_arg, my_other_arg): """blah blah blah @@ -172,7 +172,7 @@ inserting them into the page source under a suitable :rst:dir:`py:module`, an underscore. For example: - .. code-block:: rst + .. code-block:: python def _my_function(my_arg, my_other_arg): """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`. Example: - .. code-block:: rst + .. code-block:: python var1 = None #: :meta hide-value: diff --git a/doc/usage/restructuredtext/directives.rst b/doc/usage/restructuredtext/directives.rst index 92bf78489..a5c1427cf 100644 --- a/doc/usage/restructuredtext/directives.rst +++ b/doc/usage/restructuredtext/directives.rst @@ -572,9 +572,11 @@ __ http://pygments.org/docs/lexers .. versionadded:: 1.3 .. 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 :dedent: 4 @@ -582,6 +584,8 @@ __ http://pygments.org/docs/lexers some ruby code .. versionadded:: 1.3 + .. versionchanged:: 3.5 + Support automatic dedent. .. rst:directive:option:: force :type: no value @@ -742,6 +746,9 @@ __ http://pygments.org/docs/lexers .. versionchanged:: 2.1 Added the ``force`` option. + .. versionchanged:: 3.5 + Support automatic dedent. + .. _glossary-directive: Glossary diff --git a/sphinx/config.py b/sphinx/config.py index 6a8224272..735a3e0b3 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -362,6 +362,18 @@ def convert_source_suffix(app: "Sphinx", config: Config) -> None: "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: """Initialize :confval:`numfig_format`.""" 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]: 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', correct_copyright_year, priority=800) app.connect('config-inited', check_confval_types, priority=800) diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py index d4f73f84f..f5cd92b82 100644 --- a/sphinx/directives/code.py +++ b/sphinx/directives/code.py @@ -7,6 +7,7 @@ """ import sys +import textwrap from difflib import unified_diff from typing import TYPE_CHECKING, Any, Dict, List, Tuple @@ -17,6 +18,7 @@ from docutils.statemachine import StringList from sphinx import addnodes from sphinx.config import Config +from sphinx.directives import optional_int from sphinx.locale import __ from sphinx.util import logging, parselinenos 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]: if not dedent: - return lines + return textwrap.dedent(''.join(lines)).splitlines(True) if any(s[:dedent].strip() for s in lines): logger.warning(__('non-whitespace stripped by dedent'), location=location) @@ -104,7 +106,7 @@ class CodeBlock(SphinxDirective): option_spec = { 'force': directives.flag, 'linenos': directives.flag, - 'dedent': int, + 'dedent': optional_int, 'lineno-start': int, 'emphasize-lines': directives.unchanged_required, 'caption': directives.unchanged_required, @@ -378,7 +380,7 @@ class LiteralInclude(SphinxDirective): optional_arguments = 0 final_argument_whitespace = True option_spec = { - 'dedent': int, + 'dedent': optional_int, 'linenos': directives.flag, 'lineno-start': int, 'lineno-match': directives.flag, diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index fb4da502d..064318e08 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -3657,15 +3657,18 @@ class CDomain(Domain): name = 'c' label = 'C' object_types = { - 'function': ObjType(_('function'), 'func'), - 'member': ObjType(_('member'), 'member'), - 'macro': ObjType(_('macro'), 'macro'), - 'type': ObjType(_('type'), 'type'), - 'var': ObjType(_('variable'), 'data'), - 'enum': ObjType(_('enum'), 'enum'), - 'enumerator': ObjType(_('enumerator'), 'enumerator'), - 'struct': ObjType(_('struct'), 'struct'), - 'union': ObjType(_('union'), 'union'), + # 'identifier' is the one used for xrefs generated in signatures, not in roles + 'member': ObjType(_('member'), 'var', 'member', 'data', 'identifier'), + 'var': ObjType(_('variable'), 'var', 'member', 'data', 'identifier'), + 'function': ObjType(_('function'), 'func', 'identifier', 'type'), + 'macro': ObjType(_('macro'), 'macro', 'identifier'), + 'struct': ObjType(_('struct'), 'struct', 'identifier', 'type'), + 'union': ObjType(_('union'), 'union', 'identifier', 'type'), + 'enum': ObjType(_('enum'), 'enum', 'identifier', 'type'), + 'enumerator': ObjType(_('enumerator'), 'enumerator', 'identifier'), + 'type': ObjType(_('type'), 'identifier', 'type'), + # generated object types + 'functionParam': ObjType(_('function parameter'), 'identifier', 'var', 'member', 'data'), # noqa } directives = { diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index f6e746809..4d6e189a3 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -7251,14 +7251,18 @@ class CPPDomain(Domain): name = 'cpp' label = 'C++' object_types = { - 'class': ObjType(_('class'), 'class', 'type', 'identifier'), - 'union': ObjType(_('union'), 'union', 'type', 'identifier'), - 'function': ObjType(_('function'), 'function', 'func', 'type', 'identifier'), - 'member': ObjType(_('member'), 'member', 'var'), - 'type': ObjType(_('type'), 'type', 'identifier'), - 'concept': ObjType(_('concept'), 'concept', 'identifier'), - 'enum': ObjType(_('enum'), 'enum', 'type', 'identifier'), - 'enumerator': ObjType(_('enumerator'), 'enumerator') + 'class': ObjType(_('class'), 'class', 'struct', 'identifier', 'type'), + 'union': ObjType(_('union'), 'union', 'identifier', 'type'), + 'function': ObjType(_('function'), 'func', 'identifier', 'type'), + 'member': ObjType(_('member'), 'member', 'var', 'identifier'), + 'type': ObjType(_('type'), 'identifier', 'type'), + 'concept': ObjType(_('concept'), 'concept', 'identifier'), + 'enum': ObjType(_('enum'), 'enum', 'identifier', 'type'), + '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 = { @@ -7435,30 +7439,19 @@ class CPPDomain(Domain): if typ.startswith('cpp:'): typ = typ[4:] - origTyp = typ - if typ == 'func': - typ = 'function' - if typ == 'struct': - typ = 'class' declTyp = s.declaration.objectType 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 objtypes = self.objtypes_for_role(typ) if 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 if not checkType(): logger.warning("cpp:%s targets a %s (%s).", - origTyp, s.declaration.objectType, + typ, s.declaration.objectType, s.get_full_nested_name(), location=node) @@ -7488,10 +7481,10 @@ class CPPDomain(Domain): if env.config.add_function_parentheses and typ == 'any': addParen += 1 # 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()')): addParen += 1 - if ((typ == 'any' or typ == 'function') and + if ((typ == 'any' or typ == 'func') and title.endswith('operator') and displayName.endswith('operator()')): addParen += 1 @@ -7500,7 +7493,7 @@ class CPPDomain(Domain): if env.config.add_function_parentheses: if typ == 'any' and displayName.endswith('()'): addParen += 1 - elif typ == 'function': + elif typ == 'func': if title.endswith('()') and not displayName.endswith('()'): title = title[:-2] else: diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py index 18e62c3cb..864c338f4 100644 --- a/sphinx/domains/std.py +++ b/sphinx/domains/std.py @@ -730,9 +730,11 @@ class StandardDomain(Domain): name, env.doc2path(self.labels[name][0]), location=node) self.anonlabels[name] = docname, labelid - if node.tagname in ('section', 'rubric'): + if node.tagname == 'section': title = cast(nodes.title, node[0]) sectname = clean_astext(title) + elif node.tagname == 'rubric': + sectname = clean_astext(node) elif self.is_enumerable_node(node): sectname = self.get_numfig_title(node) if not sectname: diff --git a/sphinx/environment/adapters/toctree.py b/sphinx/environment/adapters/toctree.py index 5b91293a4..2e33cf702 100644 --- a/sphinx/environment/adapters/toctree.py +++ b/sphinx/environment/adapters/toctree.py @@ -319,8 +319,10 @@ class TocTree: toctrees = [] # type: List[Element] if 'includehidden' not in kwargs: kwargs['includehidden'] = True - if 'maxdepth' not in kwargs: + if 'maxdepth' not in kwargs or not kwargs['maxdepth']: kwargs['maxdepth'] = 0 + else: + kwargs['maxdepth'] = int(kwargs['maxdepth']) kwargs['collapse'] = collapse for toctreenode in doctree.traverse(addnodes.toctree): toctree = self.resolve(docname, builder, toctreenode, prune=True, **kwargs) diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py index c134258d0..72407de35 100644 --- a/sphinx/ext/autodoc/directive.py +++ b/sphinx/ext/autodoc/directive.py @@ -6,6 +6,7 @@ :license: BSD, see LICENSE for details. """ +import warnings from typing import Any, Callable, Dict, List, Set, Type from docutils import nodes @@ -15,6 +16,7 @@ from docutils.statemachine import StringList from docutils.utils import Reporter, assemble_option_dict from sphinx.config import Config +from sphinx.deprecation import RemovedInSphinx50Warning from sphinx.environment import BuildEnvironment from sphinx.ext.autodoc import Documenter, Options from sphinx.util import logging @@ -48,7 +50,7 @@ class DocumenterBridge: def __init__(self, env: BuildEnvironment, reporter: Reporter, options: Options, lineno: int, state: Any) -> None: self.env = env - self.reporter = reporter + self._reporter = reporter self.genopt = options self.lineno = lineno self.filename_set = set() # type: Set[str] @@ -58,6 +60,12 @@ class DocumenterBridge: def warn(self, msg: str) -> None: 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 ) -> Options: diff --git a/sphinx/pycode/ast.py b/sphinx/pycode/ast.py index db9bfefb3..b9fbfc83d 100644 --- a/sphinx/pycode/ast.py +++ b/sphinx/pycode/ast.py @@ -52,6 +52,10 @@ def parse(code: str, mode: str = 'exec') -> "ast.AST": try: # type_comments parameter is available on py38+ 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: # fallback to ast module. # typed_ast is used to parse type_comments if installed. diff --git a/sphinx/util/i18n.py b/sphinx/util/i18n.py index 16c7521d5..3a5aca58e 100644 --- a/sphinx/util/i18n.py +++ b/sphinx/util/i18n.py @@ -161,7 +161,9 @@ date_format_mappings = { '%X': 'medium', # Locale’s appropriate time representation. '%y': 'YY', # Year without century as a zero-padded 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). '%%': '%', } diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index 63c15dd96..ca05b7e6d 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -305,7 +305,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): if figure_id in self.builder.fignumbers.get(key, {}): 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: msg = __('numfig_format is not defined for %s') % figtype logger.warning(msg) @@ -429,14 +429,10 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): linenos = node.get('linenos', False) highlight_args = node.get('highlight_args', {}) highlight_args['force'] = node.get('force', False) - if lang is self.builder.config.highlight_language: - # only pass highlighter options for original language - opts = self.builder.config.highlight_options - else: - opts = {} + opts = self.config.highlight_options.get(lang, {}) - if linenos and self.builder.config.html_codeblock_linenos_style: - linenos = self.builder.config.html_codeblock_linenos_style + if linenos and self.config.html_codeblock_linenos_style: + linenos = self.config.html_codeblock_linenos_style highlighted = self.highlighter.highlight_block( node.rawsource, lang, opts=opts, linenos=linenos, diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index 5e0be1a41..a08f62f73 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -278,7 +278,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): if figure_id in self.builder.fignumbers.get(key, {}): 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: msg = __('numfig_format is not defined for %s') % figtype logger.warning(msg) @@ -382,14 +382,10 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): linenos = node.get('linenos', False) highlight_args = node.get('highlight_args', {}) highlight_args['force'] = node.get('force', False) - if lang is self.builder.config.highlight_language: - # only pass highlighter options for original language - opts = self.builder.config.highlight_options - else: - opts = {} + opts = self.config.highlight_options.get(lang, {}) - if linenos and self.builder.config.html_codeblock_linenos_style: - linenos = self.builder.config.html_codeblock_linenos_style + if linenos and self.config.html_codeblock_linenos_style: + linenos = self.config.html_codeblock_linenos_style highlighted = self.highlighter.highlight_block( node.rawsource, lang, opts=opts, linenos=linenos, diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 52432c120..56cc4bd7c 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -521,7 +521,7 @@ class LaTeXTranslator(SphinxTranslator): ret = [] # 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: for domain in self.builder.env.domains.values(): for indexcls in domain.indices: @@ -541,7 +541,7 @@ class LaTeXTranslator(SphinxTranslator): def render(self, template_name: str, variables: Dict) -> str: 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_name) if path.exists(template): @@ -961,7 +961,7 @@ class LaTeXTranslator(SphinxTranslator): cell = self.table.cell() context = '' if cell.width > 1: - if self.builder.config.latex_use_latex_multicolumn: + if self.config.latex_use_latex_multicolumn: if self.table.col == 0: self.body.append('\\multicolumn{%d}{|l|}{%%\n' % cell.width) else: @@ -1541,7 +1541,7 @@ class LaTeXTranslator(SphinxTranslator): id = self.curfilestack[-1] + ':' + uri[1:] self.body.append(self.hyperlink(id)) 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.context.append('}}} (%s)' % self.hyperpageref(id)) else: @@ -1565,8 +1565,7 @@ class LaTeXTranslator(SphinxTranslator): self.body.append(r'\sphinxtermref{') else: self.body.append(r'\sphinxcrossref{') - if self.builder.config.latex_show_pagerefs and not \ - self.in_production_list: + if self.config.latex_show_pagerefs and not self.in_production_list: self.context.append('}}} (%s)' % self.hyperpageref(id)) else: self.context.append('}}}') @@ -1750,11 +1749,7 @@ class LaTeXTranslator(SphinxTranslator): linenos = node.get('linenos', False) highlight_args = node.get('highlight_args', {}) highlight_args['force'] = node.get('force', False) - if lang is self.builder.config.highlight_language: - # only pass highlighter options for original language - opts = self.builder.config.highlight_options - else: - opts = {} + opts = self.config.highlight_options.get(lang, {}) hlcode = self.highlighter.highlight_block( node.rawsource, lang, opts=opts, linenos=linenos, @@ -2016,12 +2011,12 @@ class LaTeXTranslator(SphinxTranslator): else: from sphinx.util.math import wrap_displaymath self.body.append(wrap_displaymath(node.astext(), label, - self.builder.config.math_number_all)) + self.config.math_number_all)) raise nodes.SkipNode def visit_math_reference(self, node: Element) -> None: 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: try: ref = r'\ref{%s}' % label diff --git a/sphinx/writers/manpage.py b/sphinx/writers/manpage.py index b5034c8e7..917a71b3b 100644 --- a/sphinx/writers/manpage.py +++ b/sphinx/writers/manpage.py @@ -287,8 +287,7 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator): if uri.startswith('mailto:') or uri.startswith('http:') or \ uri.startswith('https:') or uri.startswith('ftp:'): # if configured, put the URL after the link - if self.builder.config.man_show_urls and \ - node.astext() != uri: + if self.config.man_show_urls and node.astext() != uri: if uri.startswith('mailto:'): uri = uri[7:] self.body.extend([ diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py index a4fd941a3..a70ffd5e4 100644 --- a/sphinx/writers/texinfo.py +++ b/sphinx/writers/texinfo.py @@ -232,12 +232,12 @@ class TexinfoTranslator(SphinxTranslator): 'author': self.settings.author, # if empty, use basename of input file 'filename': self.settings.texinfo_filename, - 'release': self.escape(self.builder.config.release), - 'project': self.escape(self.builder.config.project), - 'copyright': self.escape(self.builder.config.copyright), - 'date': self.escape(self.builder.config.today or - format_date(self.builder.config.today_fmt or _('%b %d, %Y'), - language=self.builder.config.language)) + 'release': self.escape(self.config.release), + 'project': self.escape(self.config.project), + 'copyright': self.escape(self.config.copyright), + 'date': self.escape(self.config.today or + format_date(self.config.today_fmt or _('%b %d, %Y'), + language=self.config.language)) }) # title title = self.settings.title # type: str @@ -433,7 +433,7 @@ class TexinfoTranslator(SphinxTranslator): self.add_menu_entries(entries) if (node_name != 'Top' 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') return @@ -483,7 +483,7 @@ class TexinfoTranslator(SphinxTranslator): ret.append('@end menu\n') return ''.join(ret) - indices_config = self.builder.config.texinfo_domain_indices + indices_config = self.config.texinfo_domain_indices if indices_config: for domain in self.builder.env.domains.values(): for indexcls in domain.indices: @@ -738,7 +738,7 @@ class TexinfoTranslator(SphinxTranslator): else: uri = self.escape_arg(uri) name = self.escape_arg(name) - show_urls = self.builder.config.texinfo_show_urls + show_urls = self.config.texinfo_show_urls if self.in_footnote: show_urls = 'inline' if not name or uri == name: @@ -1394,9 +1394,8 @@ class TexinfoTranslator(SphinxTranslator): # use the full name of the objtype for the category try: domain = self.builder.env.get_domain(node.parent['domain']) - primary = self.builder.config.primary_domain name = domain.get_type_name(domain.object_types[objtype], - primary == domain.name) + self.config.primary_domain == domain.name) except (KeyError, ExtensionError): name = objtype # by convention, the deffn category should be capitalized like a title diff --git a/tests/roots/test-domain-c-intersphinx/conf.py b/tests/roots/test-domain-c-intersphinx/conf.py new file mode 100644 index 000000000..c176af775 --- /dev/null +++ b/tests/roots/test-domain-c-intersphinx/conf.py @@ -0,0 +1,4 @@ +exclude_patterns = ['_build'] +extensions = [ + 'sphinx.ext.intersphinx', +] diff --git a/tests/roots/test-domain-c-intersphinx/index.rst b/tests/roots/test-domain-c-intersphinx/index.rst new file mode 100644 index 000000000..5d6d3e098 --- /dev/null +++ b/tests/roots/test-domain-c-intersphinx/index.rst @@ -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` diff --git a/tests/roots/test-domain-cpp-intersphinx/conf.py b/tests/roots/test-domain-cpp-intersphinx/conf.py new file mode 100644 index 000000000..c176af775 --- /dev/null +++ b/tests/roots/test-domain-cpp-intersphinx/conf.py @@ -0,0 +1,4 @@ +exclude_patterns = ['_build'] +extensions = [ + 'sphinx.ext.intersphinx', +] diff --git a/tests/roots/test-domain-cpp-intersphinx/index.rst b/tests/roots/test-domain-cpp-intersphinx/index.rst new file mode 100644 index 000000000..9ed9493ed --- /dev/null +++ b/tests/roots/test-domain-cpp-intersphinx/index.rst @@ -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` diff --git a/tests/roots/test-domain-cpp/roles-targets-ok.rst b/tests/roots/test-domain-cpp/roles-targets-ok.rst index e70b9259f..783f7b985 100644 --- a/tests/roots/test-domain-cpp/roles-targets-ok.rst +++ b/tests/roots/test-domain-cpp/roles-targets-ok.rst @@ -123,37 +123,37 @@ :class:`TParamType` :struct:`TParamType` :union:`TParamType` - :func:`TParamType` + function :member:`TParamType` :var:`TParamType` :type:`TParamType` - :concept:`TParamType` - :enum:`TParamType` - :enumerator:`TParamType` + concept + enum + enumerator :cpp:any:`TParamVar` :class:`TParamVar` :struct:`TParamVar` :union:`TParamVar` - :func:`TParamVar` + function :member:`TParamVar` :var:`TParamVar` :type:`TParamVar` - :concept:`TParamVar` - :enum:`TParamVar` - :enumerator:`TParamVar` + concept + enum + enumerator :cpp:any:`TParamTemplate` :class:`TParamTemplate` :struct:`TParamTemplate` :union:`TParamTemplate` - :func:`TParamTemplate` + function :member:`TParamTemplate` :var:`TParamTemplate` :type:`TParamTemplate` - :concept:`TParamTemplate` - :enum:`TParamTemplate` - :enumerator:`TParamTemplate` + concept + enum + enumerator .. function:: void FunctionParams(int FunctionParam) diff --git a/tests/roots/test-domain-cpp/roles-targets-warn.rst b/tests/roots/test-domain-cpp/roles-targets-warn.rst index decebe170..57083ff15 100644 --- a/tests/roots/test-domain-cpp/roles-targets-warn.rst +++ b/tests/roots/test-domain-cpp/roles-targets-warn.rst @@ -114,35 +114,35 @@ class struct union - func + :func:`TParamType` member var type - concept - enum - enumerator + :concept:`TParamType` + :enum:`TParamType` + :enumerator:`TParamType` class struct union - func + :func:`TParamVar` member var type - concept - enum - enumerator + :concept:`TParamVar` + :enum:`TParamVar` + :enumerator:`TParamVar` class struct union - func + :func:`TParamTemplate` member var type - concept - enum - enumerator + :concept:`TParamTemplate` + :enum:`TParamTemplate` + :enumerator:`TParamTemplate` .. function:: void FunctionParams(int FunctionParam) diff --git a/tests/roots/test-domain-py/abbr.rst b/tests/roots/test-domain-py/abbr.rst new file mode 100644 index 000000000..67f11578b --- /dev/null +++ b/tests/roots/test-domain-py/abbr.rst @@ -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` diff --git a/tests/roots/test-highlight_options/conf.py b/tests/roots/test-highlight_options/conf.py new file mode 100644 index 000000000..90997d444 --- /dev/null +++ b/tests/roots/test-highlight_options/conf.py @@ -0,0 +1,4 @@ +highlight_options = { + 'default': {'default_option': True}, + 'python': {'python_option': True} +} diff --git a/tests/roots/test-highlight_options/index.rst b/tests/roots/test-highlight_options/index.rst new file mode 100644 index 000000000..389041ace --- /dev/null +++ b/tests/roots/test-highlight_options/index.rst @@ -0,0 +1,14 @@ +test-highlight_options +====================== + +.. code-block:: + + blah blah blah + +.. code-block:: python + + blah blah blah + +.. code-block:: java + + blah blah blah diff --git a/tests/test_build_html.py b/tests/test_build_html.py index eecd25d07..6bfbe422b 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -12,6 +12,7 @@ import os import re from distutils.version import LooseVersion from itertools import chain, cycle +from unittest.mock import ANY, call, patch import pygments import pytest @@ -1607,3 +1608,36 @@ def test_html_codeblock_linenos_style_inline(app): assert '<span class="linenos">1</span>' in content else: 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={}) diff --git a/tests/test_directive_code.py b/tests/test_directive_code.py index 9eecabe10..0ae11baf3 100644 --- a/tests/test_directive_code.py +++ b/tests/test_directive_code.py @@ -250,6 +250,14 @@ def test_LiteralIncludeReader_dedent(literal_inc_path): " pass\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") def test_LiteralIncludeReader_tabwidth(testroot): diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py index 10d618712..0f17fd041 100644 --- a/tests/test_domain_c.py +++ b/tests/test_domain_c.py @@ -7,6 +7,8 @@ :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ + +import zlib from xml.etree import ElementTree import pytest @@ -14,6 +16,7 @@ import pytest from sphinx import addnodes from sphinx.addnodes import desc 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.util import assert_node @@ -642,3 +645,52 @@ def test_noindexentry(app): 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[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 diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 1c53150e0..da05c1261 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -9,6 +9,7 @@ """ import re +import zlib import pytest @@ -17,6 +18,7 @@ from sphinx import addnodes from sphinx.addnodes import desc from sphinx.domains.cpp import (DefinitionError, DefinitionParser, NoOldIdError, Symbol, _id_prefix, _max_id) +from sphinx.ext.intersphinx import load_mappings, normalize_intersphinx_mapping from sphinx.testing import restructuredtext from sphinx.testing.util import assert_node @@ -1048,8 +1050,8 @@ def test_build_domain_cpp_misuse_of_roles(app, status, warning): ('concept', ['concept']), ('enum', ['type', 'enum']), ('enumerator', ['enumerator']), - ('tParam', ['class', 'struct', 'union', 'func', 'member', 'var', 'type', 'concept', 'enum', 'enumerator', 'functionParam']), ('functionParam', ['member', 'var']), + ('templateParam', ['class', 'struct', 'union', 'member', 'var', 'type']), ] warn = [] for targetType, roles in ok: @@ -1057,6 +1059,9 @@ def test_build_domain_cpp_misuse_of_roles(app, status, warning): for r in allRoles: if r not in roles: 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)) for w in ws: 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 "Declaration is '.. cpp:struct:: A'." in ws[3] 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 diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index 537bae15b..a1e37fa5e 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -8,6 +8,7 @@ :license: BSD, see LICENSE for details. """ +import re import sys from unittest.mock import Mock @@ -132,6 +133,29 @@ def test_domain_py_xrefs(app, status, warning): 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') def test_domain_py_objects(app, status, warning): app.builder.build_all() diff --git a/tests/test_domain_std.py b/tests/test_domain_std.py index f3f45d9c5..d0230ee2e 100644 --- a/tests/test_domain_std.py +++ b/tests/test_domain_std.py @@ -409,3 +409,13 @@ def test_disabled_docref(app): assert_node(doctree, ([nodes.paragraph, ([pending_xref, nodes.inline, "index"], "\n", [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') diff --git a/tests/test_util_i18n.py b/tests/test_util_i18n.py index f15f3cb95..180350e86 100644 --- a/tests/test_util_i18n.py +++ b/tests/test_util_i18n.py @@ -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=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") def test_get_filename_for_language(app): |