diff --git a/CHANGES b/CHANGES
index 24ed7956e..e6c73dc87 100644
--- a/CHANGES
+++ b/CHANGES
@@ -34,25 +34,28 @@ Incompatible changes
* #1857: latex: :confval:`latex_show_pagerefs` does not add pagerefs for
citations
* #4648: latex: Now "rubric" elements are rendered as unnumbered section title
+* #4983: html: The anchor for productionlist tokens has been changed
+* Modifying a template variable ``script_files`` in templates is allowed now.
+ Please use ``app.add_js_file()`` instead.
+* #5072: Save environment object also with only new documents
+* #5035: qthelp builder allows dashes in :confval:`qthelp_namespace`
Deprecated
----------
* :confval:`source_parsers` is deprecated
-* ``Application.import_object()`` is deprecated
* Drop function based directive support. For now, Sphinx only supports class
based directives.
-* ``Sphinx.add_source_parser()`` has changed; the *suffix* argument has
- been deprecated
* ``sphinx.util.docutils.directive_helper()`` is deprecated
* ``sphinx.cmdline`` is deprecated
-* All ``env.update()``, ``env._read_serial()`` and ``env._read_parallel()`` are
- deprecated
* ``sphinx.locale.l_()`` is deprecated
* #2157: helper function ``warn()`` for HTML themes is deprecated
-* ``env._nitpick_ignore`` is deprecated
* ``app.override_domain()`` is deprecated
* ``app.add_stylesheet()`` is deprecated
+* ``app.add_javascript()`` is deprecated
+* ``app.import_object()`` is deprecated
+* ``app.add_source_parser()`` has changed; the *suffix* argument has been
+ deprecated
* ``sphinx.versioning.prepare()`` is deprecated
* ``Config.__init__()`` has changed; the *dirname*, *filename* and *tags*
argument has been deprecated
@@ -60,19 +63,30 @@ Deprecated
* ``Config.check_unicode()`` is deprecated
* ``sphinx.application.CONFIG_FILENAME`` is deprecated
* ``highlightlang`` directive is deprecated
-* ``env.read_doc()`` is deprecated
-* ``sphinx.writers.latex.Table.caption_footnotetexts`` is deprecated
-* ``sphinx.writers.latex.Table.header_footnotetexts`` is deprecated
-* ``sphinx.writers.latex.LaTeXWriter.footnotestack`` is deprecated
-* ``sphinx.writers.latex.LaTeXWriter.restrict_footnote()`` is deprecated
-* ``sphinx.writers.latex.LaTeXWriter.unrestrict_footnote()`` is deprecated
-* ``LaTeXWriter.bibitems`` is deprecated
* ``BuildEnvironment.load()`` is deprecated
* ``BuildEnvironment.loads()`` is deprecated
* ``BuildEnvironment.frompickle()`` is deprecated
-* ``BuildEnvironment.dump()`` is deprecated
-* ``BuildEnvironment.dumps()`` is deprecated
-* ``BuildEnvironment.topickle()`` is deprecated
+* ``env.read_doc()`` is deprecated
+* ``env.update()`` is deprecated
+* ``env._read_serial()`` is deprecated
+* ``env._read_parallel()`` is deprecated
+* ``env.write_doctree()`` is deprecated
+* ``env._nitpick_ignore`` is deprecated
+* ``env.dump()`` is deprecated
+* ``env.dumps()`` is deprecated
+* ``env.topickle()`` is deprecated
+* ``sphinx.writers.latex.Table.caption_footnotetexts`` is deprecated
+* ``sphinx.writers.latex.Table.header_footnotetexts`` is deprecated
+* ``sphinx.writers.latex.LaTeXTranslator.footnotestack`` is deprecated
+* ``sphinx.writers.latex.LaTeXTranslator.in_container_literal_block`` is deprecated
+* ``sphinx.writers.latex.LaTeXTranslator.next_section_ids`` is deprecated
+* ``sphinx.writers.latex.LaTeXTranslator.next_hyperlink_ids`` is deprecated
+* ``sphinx.writers.latex.LaTeXTranslator.restrict_footnote()`` is deprecated
+* ``sphinx.writers.latex.LaTeXTranslator.unrestrict_footnote()`` is deprecated
+* ``sphinx.writers.latex.LaTeXTranslator.push_hyperlink_ids()`` is deprecated
+* ``sphinx.writers.latex.LaTeXTranslator.pop_hyperlink_ids()`` is deprecated
+* ``sphinx.writers.latex.LaTeXTranslator.bibitems`` is deprecated
+* ``sphinx.writers.latex.ExtBabel.get_shorthandoff()`` is deprecated
* ``sphinx.ext.mathbase.math`` node is deprecated
* ``sphinx.ext.mathbase.displaymath`` node is deprecated
* ``sphinx.ext.mathbase.eqref`` node is deprecated
@@ -103,6 +117,7 @@ Features added
fontsize in code-blocks (refs: #4793)
* Add :confval:`html_css_files` and :confval:`epub_css_files` for adding CSS
files from configuration
+* Add :confval:`html_js_files` for adding JS files from configuration
* #4834: Ensure set object descriptions are reproducible.
* #4828: Allow to override :confval:`numfig_format` partially. Full definition
is not needed.
@@ -116,6 +131,17 @@ Features added
* #4785: napoleon: Add strings to translation file for localisation
* #4927: Display a warning when invalid values are passed to linenothreshold
option of highlight directive
+* C++, add a ``cpp:texpr`` role as a sibling to ``cpp:expr``.
+* C++, add support for unions.
+* C++, add support for anonymous entities using names staring with ``@``.
+ Fixes #3593 and #2683.
+* #3606: MathJax should be loaded with async attribute
+* html: Output ``canonical_url`` metadata if :confval:`html_baseurl` set (refs:
+ #4193)
+* #5029: autosummary: expose ``inherited_members`` to template
+* #3784: mathjax: Add :confval:`mathjax_options` to give options to script tag
+ for mathjax
+* #4362: latex: Don't overwrite .tex file if document not changed
* #1431: latex: Add alphanumeric enumerated list support
Bugs fixed
@@ -125,6 +151,7 @@ Bugs fixed
* #4850: latex: footnote inside footnote was not rendered
* #4945: i18n: fix lang_COUNTRY not fallback correctly for IndexBuilder. Thanks
to Shengjing Zhu.
+* #4983: productionlist directive generates invalid IDs for the tokens
Testing
--------
@@ -134,7 +161,12 @@ Features removed
* ``sphinx.ext.pngmath`` extension
-Release 1.7.5 (in development)
+Documentation
+-------------
+
+* #5083: Fix wrong make.bat option for internationalization.
+
+Release 1.7.6 (in development)
==============================
Dependencies
@@ -152,6 +184,33 @@ Features added
Bugs fixed
----------
+* #5037: LaTeX ``\sphinxupquote{}`` breaks in Russian
+* sphinx.testing uses deprecated pytest API; ``Node.get_marker(name)``
+* #5016: crashed when recommonmark.AutoStrictify is enabled
+* #5022: latex: crashed with docutils package provided by Debian/Ubuntu
+* #5009: latex: a label for table is vanished if table does not have a caption
+* #5048: crashed with numbered toctree
+* #2410: C, render empty argument lists for macros.
+* C++, fix lookup of full template specializations with no template arguments.
+* #4667: C++, fix assertion on missing references in global scope when using
+ intersphinx. Thanks to Alan M. Carroll.
+* #5019: autodoc: crashed by Form Feed Character
+* #5032: autodoc: loses the first staticmethod parameter for old styled classes
+* #5036: quickstart: Typing Ctrl-U clears the whole of line
+* #5066: html: "relations" sidebar is not shown by default
+* #5091: latex: curly braces in index entries are not handled correctly
+* #5070: epub: Wrong internal href fragment links
+* #5104: apidoc: Interface of ``sphinx.apidoc:main()`` has changed
+
+Testing
+--------
+
+Release 1.7.5 (released May 29, 2018)
+=====================================
+
+Bugs fixed
+----------
+
* #4924: html search: Upper characters problem in any other languages
* #4932: apidoc: some subpackage is ignored if sibling subpackage contains a
module starting with underscore
@@ -162,6 +221,7 @@ Bugs fixed
* #4825: C++, properly parse expr roles and give better error messages when
(escaped) line breaks are present.
* C++, properly use ``desc_addname`` nodes for prefixes of names.
+* C++, parse pack expansions in function calls.
* #4915, #4916: links on search page are broken when using dirhtml builder
* #4969: autodoc: constructor method should not have return annotation
* latex: deeply nested enumerated list which is beginning with non-1 causes
@@ -174,9 +234,12 @@ Bugs fixed
mocked module
* #4973: latex: glossary directive adds whitespace to each item
* #4980: latex: Explicit labels on code blocks are duplicated
-
-Testing
---------
+* #4919: node.asdom() crashes if toctree has :numbered: option
+* #4914: autodoc: Parsing error when using dataclasses without default values
+* #4931: autodoc: crashed when handler for autodoc-skip-member raises an error
+* #4931: autodoc: crashed when subclass of mocked class are processed by
+ napoleon module
+* #5007: sphinx-build crashes when error log contains a "%" character
Release 1.7.4 (released Apr 25, 2018)
=====================================
diff --git a/EXAMPLES b/EXAMPLES
index 4310eff75..94aade9bc 100644
--- a/EXAMPLES
+++ b/EXAMPLES
@@ -195,6 +195,7 @@ Documentation using sphinx_rtd_theme
* Julia: https://julia.readthedocs.io/
* Jupyter Notebook: https://jupyter-notebook.readthedocs.io/
* Lasagne: https://lasagne.readthedocs.io/
+* latexindent.pl: https://latexindentpl.readthedocs.io/
* Linguistica: https://linguistica-uchicago.github.io/lxa5/
* Linux kernel: https://www.kernel.org/doc/html/latest/index.html
* MathJax: https://docs.mathjax.org/
@@ -218,6 +219,7 @@ Documentation using sphinx_rtd_theme
* peewee: http://docs.peewee-orm.com/
* Phinx: http://docs.phinx.org/
* phpMyAdmin: https://docs.phpmyadmin.net/
+* PROS: https://pros.cs.purdue.edu/v5/ (customized)
* Pweave: http://mpastell.com/pweave/
* PyPy: http://doc.pypy.org/
* python-sqlparse: https://sqlparse.readthedocs.io/
diff --git a/doc/Makefile b/doc/Makefile
index c54236be0..4d2067071 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -1,9 +1,10 @@
# Makefile for Sphinx documentation
#
+PYTHON ?= python
# You can set these variables from the command line.
SPHINXOPTS =
-SPHINXBUILD = python ../sphinx/cmd/build.py
+SPHINXBUILD = $(PYTHON) ../sphinx/cmd/build.py
SPHINXPROJ = sphinx
SOURCEDIR = .
BUILDDIR = _build
diff --git a/doc/_templates/index.html b/doc/_templates/index.html
index 032636d94..be174317d 100644
--- a/doc/_templates/index.html
+++ b/doc/_templates/index.html
@@ -73,11 +73,8 @@
{%trans%}
- You can also download PDF/EPUB versions of the Sphinx documentation:
- a PDF version generated from
- the LaTeX Sphinx producer, and
- an EPUB version .
- {%endtrans%}
+ You can also download PDF/EPUB versions of the Sphinx documentation
+ from pop up menu on lower right corner.{%endtrans%}
{%trans%}Examples{%endtrans%}
diff --git a/doc/_templates/indexsidebar.html b/doc/_templates/indexsidebar.html
index e1747c762..94174ce90 100644
--- a/doc/_templates/indexsidebar.html
+++ b/doc/_templates/indexsidebar.html
@@ -3,19 +3,9 @@
{%trans%}project{%endtrans%}
Download
-{% if version.endswith('+') %}
-{%trans%}This documentation is for version {{ version }} , which is
- not released yet.{%endtrans%}
-{%trans%}You can use it from the
- Git repo or look for
- released versions in the Python
- Package Index .{%endtrans%}
-{% else %}
-{%trans%}Current version: {{ version }} {%endtrans%}
-{%trans%}Get Sphinx from the Python Package
-Index , or install it with:{%endtrans%}
+{%trans%}Current version: {%endtrans%}
+{%trans%}Install Sphinx with:{%endtrans%}
pip install -U Sphinx
-{% endif %}
{%trans%}Questions? Suggestions?{%endtrans%}
diff --git a/doc/_themes/sphinx13/static/sphinx13.css b/doc/_themes/sphinx13/static/sphinx13.css
index eff18df3c..c2afc2a7c 100644
--- a/doc/_themes/sphinx13/static/sphinx13.css
+++ b/doc/_themes/sphinx13/static/sphinx13.css
@@ -140,6 +140,10 @@ div.sphinxsidebar .logo img {
vertical-align: middle;
}
+div.sphinxsidebar .download a img {
+ vertical-align: middle;
+}
+
div.subscribeformwrapper {
display: block;
overflow: auto;
diff --git a/doc/conf.py b/doc/conf.py
index 18d82b223..732c4d4a0 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -103,7 +103,7 @@ texinfo_documents = [
# We're not using intersphinx right now, but if we did, this would be part of
# the mapping:
-intersphinx_mapping = {'python': ('https://docs.python.org/2/', None)}
+intersphinx_mapping = {'python': ('https://docs.python.org/3/', None)}
# Sphinx document translation with sphinx gettext feature uses these settings:
locale_dirs = ['locale/']
diff --git a/doc/ext/autodoc.rst b/doc/ext/autodoc.rst
index 0bddba5c5..9064fe1b3 100644
--- a/doc/ext/autodoc.rst
+++ b/doc/ext/autodoc.rst
@@ -41,7 +41,7 @@ you can also enable the :mod:`napoleon ` extension.
docstrings to correct reStructuredText before :mod:`autodoc` processes them.
.. _Google:
- https://google.github.io/styleguide/pyguide.html#Comments
+ https://github.com/google/styleguide/blob/gh-pages/pyguide.md#38-comments-and-docstrings
.. _NumPy:
https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt
@@ -374,7 +374,7 @@ There are also new config values that you can set:
This value contains a list of modules to be mocked up. This is useful when
some external dependencies are not met at build time and break the building
process. You may only specify the root package of the dependencies
- themselves and ommit the sub-modules:
+ themselves and omit the sub-modules:
.. code-block:: python
diff --git a/doc/ext/autosummary.rst b/doc/ext/autosummary.rst
index c35ba50a5..46d8e4b56 100644
--- a/doc/ext/autosummary.rst
+++ b/doc/ext/autosummary.rst
@@ -205,6 +205,11 @@ The following variables available in the templates:
List containing names of all members of the module or class. Only available
for modules and classes.
+.. data:: inherited_members
+
+ List containing names of all inherited members of class. Only available for
+ classes.
+
.. data:: functions
List containing names of "public" functions in the module. Here, "public"
diff --git a/doc/ext/math.rst b/doc/ext/math.rst
index 3e4f94c6c..c299b7bee 100644
--- a/doc/ext/math.rst
+++ b/doc/ext/math.rst
@@ -246,7 +246,7 @@ Sphinx.
page`__ for details. If you want MathJax to be available offline, you have
to download it and set this value to a different path.
- __ https://cdjns.com
+ __ https://cdnjs.com
__ https://docs.mathjax.org/en/latest/start.html
@@ -260,6 +260,16 @@ Sphinx.
You can also give a full ``https://`` URL different from the CDN URL.
+.. confval:: mathjax_options
+
+ The options to script tag for mathjax. For example, you can set integrity
+ option with following setting::
+
+ mathjax_options = {
+ 'integrity': 'sha384-......',
+ }
+
+ The default is empty (``{}``).
:mod:`sphinx.ext.jsmath` -- Render math via JavaScript
------------------------------------------------------
diff --git a/doc/extdev/appapi.rst b/doc/extdev/appapi.rst
index 584cf375b..9828f035f 100644
--- a/doc/extdev/appapi.rst
+++ b/doc/extdev/appapi.rst
@@ -71,9 +71,9 @@ package.
.. automethod:: Sphinx.add_post_transform(transform)
-.. automethod:: Sphinx.add_javascript(filename)
+.. automethod:: Sphinx.add_js_file(filename, **kwargs)
-.. automethod:: Sphinx.add_stylesheet(filename, alternate=None, title=None)
+.. automethod:: Sphinx.add_css_file(filename, **kwargs)
.. automethod:: Sphinx.add_latex_package(packagename, options=None)
@@ -144,19 +144,19 @@ Sphinx runtime information
The application object also provides runtime information as attributes.
-.. attribute:: srcdir
+.. attribute:: Sphinx.srcdir
Source directory.
-.. attribute:: confdir
+.. attribute:: Sphinx.confdir
Directory containing ``conf.py``.
-.. attribute:: doctreedir
+.. attribute:: Sphinx.doctreedir
Directory for storing pickled doctrees.
-.. attribute:: outdir
+.. attribute:: Sphinx.outdir
Directory for storing built document.
@@ -269,7 +269,7 @@ handlers to the events. Example:
Here is the place to replace custom nodes that don't have visitor methods in
the writers, so that they don't cause errors when the writers encounter them.
-.. event:: env-merge-info (env, docnames, other)
+.. event:: env-merge-info (app, env, docnames, other)
This event is only emitted when parallel reading of documents is enabled. It
is emitted once for every subprocess that has read some documents.
@@ -303,7 +303,7 @@ handlers to the events. Example:
.. versionchanged:: 1.3
The handlers' return value is now used.
-.. event:: env-check-consistency (env)
+.. event:: env-check-consistency (app, env)
Emitted when Consistency checks phase. You can check consistency of
metadata for whole of documents.
diff --git a/doc/extdev/index.rst b/doc/extdev/index.rst
index d10ada464..7aa6a7948 100644
--- a/doc/extdev/index.rst
+++ b/doc/extdev/index.rst
@@ -101,7 +101,7 @@ Deprecated APIs
On developing Sphinx, we are always careful to the compatibility of our APIs.
But, sometimes, the change of interface are needed for some reasons. In such
-cases, we've marked thme as deprecated. And they are kept during the two
+cases, we've marked them as deprecated. And they are kept during the two
major versions (for more details, please see :ref:`deprecation-policy`).
The following is a list of deprecated interface.
@@ -126,6 +126,11 @@ The following is a list of deprecated interface.
- 4.0
- :meth:`~sphinx.application.Sphinx.add_css_file()`
+ * - :meth:`~sphinx.application.Sphinx.add_javascript()`
+ - 1.8
+ - 4.0
+ - :meth:`~sphinx.application.Sphinx.add_js_file()`
+
* - ``sphinx.ext.mathbase.MathDomain``
- 1.8
- 3.0
@@ -166,22 +171,52 @@ The following is a list of deprecated interface.
- 3.0
- N/A
- * - ``sphinx.writers.latex.LaTeXWriter.footnotestack``
+ * - ``sphinx.writers.latex.LaTeXTranslator.footnotestack``
- 1.8
- 3.0
- N/A
- * - ``sphinx.writers.latex.LaTeXWriter.restrict_footnote()``
+ * - ``sphinx.writers.latex.LaTeXTranslator.in_container_literal_block``
- 1.8
- 3.0
- N/A
- * - ``sphinx.writers.latex.LaTeXWriter.unrestrict_footnote()``
+ * - ``sphinx.writers.latex.LaTeXTranslator.next_section_ids``
- 1.8
- 3.0
- N/A
- * - ``sphinx.writers.latex.LaTeXWriter.bibitems``
+ * - ``sphinx.writers.latex.LaTeXTranslator.next_hyperlink_ids``
+ - 1.8
+ - 3.0
+ - N/A
+
+ * - ``sphinx.writers.latex.LaTeXTranslator.restrict_footnote()``
+ - 1.8
+ - 3.0
+ - N/A
+
+ * - ``sphinx.writers.latex.LaTeXTranslator.unrestrict_footnote()``
+ - 1.8
+ - 3.0
+ - N/A
+
+ * - ``sphinx.writers.latex.LaTeXTranslator.push_hyperlink_ids()``
+ - 1.8
+ - 3.0
+ - N/A
+
+ * - ``sphinx.writers.latex.LaTeXTranslator.pop_hyperlink_ids()``
+ - 1.8
+ - 3.0
+ - N/A
+
+ * - ``sphinx.writers.latex.LaTeXTranslator.bibitems``
+ - 1.8
+ - 3.0
+ - N/A
+
+ * - ``sphinx.writers.latex.ExtBabel.get_shorthandoff()``
- 1.8
- 3.0
- N/A
@@ -217,11 +252,23 @@ The following is a list of deprecated interface.
- 3.0
- ``sphinx.versioning.UIDTransform``
- * - ``sphinx.application.Sphinx.override_domain()``
+ * - ``Sphinx.override_domain()``
- 1.8
- 3.0
- :meth:`~sphinx.application.Sphinx.add_domain()`
+ * - ``Sphinx.import_object()``
+ - 1.8
+ - 3.0
+ - ``sphinx.util.import_object()``
+
+ * - ``suffix`` argument of
+ :meth:`~sphinx.application.Sphinx.add_source_parser()`
+ - 1.8
+ - 3.0
+ - :meth:`~sphinx.application.Sphinx.add_source_suffix()`
+
+
* - ``BuildEnvironment.load()``
- 1.8
- 3.0
@@ -257,37 +304,6 @@ The following is a list of deprecated interface.
- 3.0
- :confval:`nitpick_ignore`
- * - ``warn()`` (template helper function)
- - 1.8
- - 3.0
- - ``warning()``
-
- * - :confval:`source_parsers`
- - 1.8
- - 3.0
- - :meth:`~sphinx.application.Sphinx.add_source_parser()`
-
- * - ``Sphinx.import_object()``
- - 1.8
- - 3.0
- - ``sphinx.util.import_object()``
-
- * - ``suffix`` argument of
- :meth:`~sphinx.application.Sphinx.add_source_parser()`
- - 1.8
- - 3.0
- - :meth:`~sphinx.application.Sphinx.add_source_suffix()`
-
- * - ``sphinx.util.docutils.directive_helper()``
- - 1.8
- - 3.0
- - ``Directive`` class of docutils
-
- * - ``sphinx.cmdline``
- - 1.8
- - 3.0
- - ``sphinx.cmd.build``
-
* - ``BuildEnvironment.update()``
- 1.8
- 3.0
@@ -313,6 +329,26 @@ The following is a list of deprecated interface.
- 3.0
- ``Builder.write_doctree()``
+ * - ``warn()`` (template helper function)
+ - 1.8
+ - 3.0
+ - ``warning()``
+
+ * - :confval:`source_parsers`
+ - 1.8
+ - 3.0
+ - :meth:`~sphinx.application.Sphinx.add_source_parser()`
+
+ * - ``sphinx.util.docutils.directive_helper()``
+ - 1.8
+ - 3.0
+ - ``Directive`` class of docutils
+
+ * - ``sphinx.cmdline``
+ - 1.8
+ - 3.0
+ - ``sphinx.cmd.build``
+
* - ``sphinx.locale.l_()``
- 1.8
- 3.0
diff --git a/doc/intl.rst b/doc/intl.rst
index 43f620bd0..129665dde 100644
--- a/doc/intl.rst
+++ b/doc/intl.rst
@@ -123,14 +123,14 @@ This section describe an easy way to translate with sphinx-intl.
.. code-block:: console
- > set SPHINXOPTS=-D language='de'
+ > set SPHINXOPTS=-D language=de
> .\make.bat html
command line (for PowerShell):
.. code-block:: console
- > Set-Item env:SPHINXOPTS "-D language='de'"
+ > Set-Item env:SPHINXOPTS "-D language=de"
> .\make.bat html
diff --git a/doc/make.bat b/doc/make.bat
index 0a4bd77b9..1e6dc991e 100644
--- a/doc/make.bat
+++ b/doc/make.bat
@@ -3,7 +3,7 @@
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
- set SPHINXBUILD=python ../sphinx-build.py
+ set SPHINXBUILD=python ../sphinx/cmd/build.py
)
set SOURCEDIR=.
set BUILDDIR=_build
diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst
index 10556b063..83700e65e 100644
--- a/doc/usage/configuration.rst
+++ b/doc/usage/configuration.rst
@@ -827,6 +827,11 @@ that use Sphinx's HTMLWriter class.
.. versionadded:: 0.4
+.. 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``.
+
.. confval:: html_context
A dictionary of values to pass into the template engine's context for all
@@ -874,6 +879,22 @@ that use Sphinx's HTMLWriter class.
.. versionadded:: 1.8
+.. confval:: html_js_files
+
+ A list of JavaScript *filename*. The entry must be a *filename* string or a
+ tuple containing the *filename* string and the *attributes* dictionary. The
+ *filename* must be relative to the :confval:`html_static_path`, or a full
+ URI with scheme like ``http://example.org/script.js``. The *attributes* is
+ used for attributes of ``
+
+ app.add_js_file('example.js', async="async")
+ # =>
.. versionadded:: 0.5
+
+ .. versionchanged:: 1.8
+ Renamed from ``app.add_javascript()``.
+ And it allows keyword arguments as attributes of script tag.
"""
- logger.debug('[app] adding javascript: %r', filename)
- from sphinx.builders.html import StandaloneHTMLBuilder
- if '://' in filename:
- StandaloneHTMLBuilder.script_files.append(filename)
- else:
- StandaloneHTMLBuilder.script_files.append(
- posixpath.join('_static', filename))
+ self.registry.add_js_file(filename, **kwargs)
def add_css_file(self, filename, **kwargs):
# type: (unicode, **unicode) -> None
diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py
index 7cfacbd3c..e446ca94a 100644
--- a/sphinx/builders/__init__.py
+++ b/sphinx/builders/__init__.py
@@ -372,14 +372,14 @@ class Builder(object):
else:
logger.info(__('none found'))
- if updated_docnames:
- # save the environment
- from sphinx.application import ENV_PICKLE_FILENAME
- logger.info(bold(__('pickling environment... ')), nonl=True)
- with open(path.join(self.doctreedir, ENV_PICKLE_FILENAME), 'wb') as f:
- pickle.dump(self.env, f, pickle.HIGHEST_PROTOCOL)
- logger.info(__('done'))
+ # save the environment
+ from sphinx.application import ENV_PICKLE_FILENAME
+ logger.info(bold(__('pickling environment... ')), nonl=True)
+ with open(path.join(self.doctreedir, ENV_PICKLE_FILENAME), 'wb') as f:
+ pickle.dump(self.env, f, pickle.HIGHEST_PROTOCOL)
+ logger.info(__('done'))
+ if updated_docnames:
# global actions
self.app.phase = BuildPhase.CONSISTENCY_CHECK
logger.info(bold(__('checking consistency... ')), nonl=True)
diff --git a/sphinx/builders/_epub_base.py b/sphinx/builders/_epub_base.py
index fe285403b..2a975fa94 100644
--- a/sphinx/builders/_epub_base.py
+++ b/sphinx/builders/_epub_base.py
@@ -272,6 +272,16 @@ class EpubBuilder(StandaloneHTMLBuilder):
node['refuri'] = self.fix_fragment(m.group(1), m.group(2))
if 'refid' in node:
node['refid'] = self.fix_fragment('', node['refid'])
+ for node in tree.traverse(nodes.target):
+ for i, node_id in enumerate(node['ids']):
+ if ':' in node_id:
+ node['ids'][i] = self.fix_fragment('', node_id)
+
+ next_node = node.next_node(siblings=True)
+ if next_node and isinstance(next_node, nodes.Element):
+ for i, node_id in enumerate(next_node['ids']):
+ if ':' in node_id:
+ next_node['ids'][i] = self.fix_fragment('', node_id)
for node in tree.traverse(addnodes.desc_signature):
ids = node.attributes['ids']
newids = []
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index 90558c0e4..62e0a28d7 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -13,6 +13,7 @@ import codecs
import posixpath
import re
import sys
+import types
import warnings
from hashlib import md5
from os import path
@@ -92,7 +93,7 @@ def get_stable_hash(obj):
class CSSContainer(list):
- """The container of stylesheets.
+ """The container for stylesheets.
To support the extensions which access the container directly, this wraps
the entry with Stylesheet class.
@@ -139,7 +140,7 @@ class CSSContainer(list):
class Stylesheet(text_type):
- """The metadata of stylesheet.
+ """A metadata of stylesheet.
To keep compatibility with old themes, an instance of stylesheet behaves as
its filename (str).
@@ -162,6 +163,59 @@ class Stylesheet(text_type):
return self
+class JSContainer(list):
+ """The container for JavaScript scripts."""
+ def insert(self, index, obj):
+ # type: (int, unicode) -> None
+ warnings.warn('builder.script_files is deprecated. '
+ 'Please use app.add_js_file() instead.',
+ RemovedInSphinx30Warning)
+ super(JSContainer, self).insert(index, obj)
+
+ def extend(self, other): # type: ignore
+ # type: (List[unicode]) -> None
+ warnings.warn('builder.script_files is deprecated. '
+ 'Please use app.add_js_file() instead.',
+ RemovedInSphinx30Warning)
+ for item in other:
+ self.append(item)
+
+ def __iadd__(self, other): # type: ignore
+ # type: (List[unicode]) -> JSContainer
+ warnings.warn('builder.script_files is deprecated. '
+ 'Please use app.add_js_file() instead.',
+ RemovedInSphinx30Warning)
+ for item in other:
+ self.append(item)
+ return self
+
+ def __add__(self, other):
+ # type: (List[unicode]) -> JSContainer
+ ret = JSContainer(self)
+ ret += other
+ return ret
+
+
+class JavaScript(text_type):
+ """A metadata of javascript file.
+
+ To keep compatibility with old themes, an instance of javascript behaves as
+ its filename (str).
+ """
+
+ attributes = None # type: Dict[unicode, unicode]
+ filename = None # type: unicode
+
+ def __new__(cls, filename, **attributes):
+ # type: (unicode, **unicode) -> None
+ self = text_type.__new__(cls, filename) # type: ignore
+ self.filename = filename
+ self.attributes = attributes
+ self.attributes.setdefault('type', 'text/javascript')
+
+ return self
+
+
class BuildInfo(object):
"""buildinfo file manipulator.
@@ -246,10 +300,6 @@ class StandaloneHTMLBuilder(Builder):
# use html5 translator by default
default_html5_translator = False
- # This is a class attribute because it is mutated by Sphinx.add_javascript.
- script_files = ['_static/jquery.js', '_static/underscore.js',
- '_static/doctools.js'] # type: List[unicode]
-
imgpath = None # type: unicode
domain_indices = [] # type: List[Tuple[unicode, Type[Index], List[Tuple[unicode, List[List[Union[unicode, int]]]]], bool]] # NOQA
@@ -263,6 +313,9 @@ class StandaloneHTMLBuilder(Builder):
# CSS files
self.css_files = CSSContainer() # type: List[Dict[unicode, unicode]]
+ # JS files
+ self.script_files = JSContainer() # type: List[JavaScript]
+
def init(self):
# type: () -> None
self.build_info = self.create_build_info()
@@ -276,6 +329,7 @@ class StandaloneHTMLBuilder(Builder):
self.init_templates()
self.init_highlighter()
self.init_css_files()
+ self.init_js_files()
if self.config.html_file_suffix is not None:
self.out_suffix = self.config.html_file_suffix
@@ -284,9 +338,6 @@ class StandaloneHTMLBuilder(Builder):
else:
self.link_suffix = self.out_suffix
- if self.config.language is not None:
- if self._get_translations_js():
- self.script_files.append('_static/translations.js')
self.use_index = self.get_builder_config('use_index', 'html')
if self.config.html_experimental_html5_writer and not html5_ready:
@@ -353,6 +404,28 @@ class StandaloneHTMLBuilder(Builder):
self.css_files.append(Stylesheet(filename, **kwargs)) # type: ignore
+ def init_js_files(self):
+ # type: () -> None
+ self.add_js_file('jquery.js')
+ self.add_js_file('underscore.js')
+ self.add_js_file('doctools.js')
+
+ for filename, attrs in self.app.registry.js_files:
+ self.add_js_file(filename, **attrs)
+
+ for filename, attrs in self.get_builder_config('js_files', 'html'):
+ self.add_js_file(filename, **attrs)
+
+ if self.config.language and self._get_translations_js():
+ self.add_js_file('translations.js')
+
+ def add_js_file(self, filename, **kwargs):
+ # type: (unicode, **unicode) -> None
+ if '://' not in filename:
+ filename = posixpath.join('_static', filename)
+
+ self.script_files.append(JavaScript(filename, **kwargs)) # type: ignore
+
@property
def default_translator_class(self):
# type: () -> nodes.NodeVisitor
@@ -941,19 +1014,27 @@ class StandaloneHTMLBuilder(Builder):
def has_wildcard(pattern):
# type: (unicode) -> bool
return any(char in pattern for char in '*?[')
- sidebars = self.theme.get_config('theme', 'sidebars', None)
+ sidebars = None
matched = None
customsidebar = None
# default sidebars settings for selected theme
- theme_default_sidebars = self.theme.get_config('theme', 'sidebars', None)
- if theme_default_sidebars:
- sidebars = [name.strip() for name in theme_default_sidebars.split(',')]
- elif self.theme.name == 'alabaster':
+ if self.theme.name == 'alabaster':
# provide default settings for alabaster (for compatibility)
# Note: this will be removed before Sphinx-2.0
- sidebars = ['about.html', 'navigation.html', 'relation.html',
- 'searchbox.html', 'donate.html']
+ try:
+ # get default sidebars settings from alabaster (if defined)
+ theme_default_sidebars = self.theme.config.get('theme', 'sidebars')
+ if theme_default_sidebars:
+ sidebars = [name.strip() for name in theme_default_sidebars.split(',')]
+ except Exception:
+ # fallback to better default settings
+ sidebars = ['about.html', 'navigation.html', 'relations.html',
+ 'searchbox.html', 'donate.html']
+ else:
+ theme_default_sidebars = self.theme.get_config('theme', 'sidebars', None)
+ if theme_default_sidebars:
+ sidebars = [name.strip() for name in theme_default_sidebars.split(',')]
# user sidebar settings
for pattern, patsidebars in iteritems(self.config.html_sidebars):
@@ -1004,6 +1085,12 @@ class StandaloneHTMLBuilder(Builder):
# part, which relative_uri doesn't really like...
default_baseuri = default_baseuri.rsplit('#', 1)[0]
+ if self.config.html_baseurl:
+ ctx['pageurl'] = posixpath.join(self.config.html_baseurl,
+ pagename + self.out_suffix)
+ else:
+ ctx['pageurl'] = None
+
def pathto(otheruri, resource=False, baseuri=default_baseuri):
# type: (unicode, bool, unicode) -> unicode
if resource and '://' in otheruri:
@@ -1357,6 +1444,7 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder):
self.init_templates()
self.init_highlighter()
self.init_css_files()
+ self.init_js_files()
self.use_index = self.get_builder_config('use_index', 'html')
def get_target_uri(self, docname, typ=None):
@@ -1390,6 +1478,11 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder):
# actually rendered
self.app.emit('html-page-context', pagename, templatename, ctx, event_arg)
+ # make context object serializable
+ for key in list(ctx):
+ if isinstance(ctx[key], types.FunctionType):
+ del ctx[key]
+
ensuredir(path.dirname(outfilename))
self.dump_context(ctx, outfilename)
@@ -1479,6 +1572,50 @@ def convert_html_css_files(app, config):
config.html_css_files = html_css_files # type: ignore
+def convert_html_js_files(app, config):
+ # type: (Sphinx, Config) -> None
+ """This converts string styled html_js_files to tuple styled one."""
+ html_js_files = [] # type: List[Tuple[unicode, Dict]]
+ for entry in config.html_js_files:
+ if isinstance(entry, string_types):
+ html_js_files.append((entry, {}))
+ else:
+ try:
+ filename, attrs = entry
+ html_js_files.append((filename, attrs))
+ except Exception:
+ logger.warning(__('invalid js_file: %r, ignored'), entry)
+ continue
+
+ config.html_js_files = html_js_files # type: ignore
+
+
+def setup_js_tag_helper(app, pagename, templatexname, context, doctree):
+ # type: (Sphinx, unicode, unicode, Dict, nodes.Node) -> None
+ """Set up js_tag() template helper.
+
+ .. note:: This set up function is added to keep compatibility with webhelper.
+ """
+ pathto = context.get('pathto')
+
+ def js_tag(js):
+ # type: (JavaScript) -> unicode
+ attrs = []
+ if isinstance(js, JavaScript):
+ for key in sorted(js.attributes):
+ value = js.attributes[key]
+ if value is not None:
+ attrs.append('%s="%s"' % (key, htmlescape(value, True)))
+ attrs.append('src="%s"' % pathto(js.filename, resource=True))
+ else:
+ # str value (old styled)
+ attrs.append('type="text/javascript"')
+ attrs.append('src="%s"' % pathto(js, resource=True))
+ return '' % ' '.join(attrs)
+
+ context['js_tag'] = js_tag
+
+
def setup(app):
# type: (Sphinx) -> Dict[unicode, Any]
# builders
@@ -1500,6 +1637,7 @@ def setup(app):
app.add_config_value('html_logo', None, 'html', string_classes)
app.add_config_value('html_favicon', None, 'html', string_classes)
app.add_config_value('html_css_files', [], 'html')
+ app.add_config_value('html_js_files', [], 'html')
app.add_config_value('html_static_path', [], 'html')
app.add_config_value('html_extra_path', [], 'html')
app.add_config_value('html_last_updated_fmt', None, 'html', string_classes)
@@ -1526,9 +1664,12 @@ def setup(app):
app.add_config_value('html_search_scorer', '', None)
app.add_config_value('html_scaled_image_link', True, 'html')
app.add_config_value('html_experimental_html5_writer', None, 'html')
+ app.add_config_value('html_baseurl', '', 'html')
# event handlers
app.connect('config-inited', convert_html_css_files)
+ app.connect('config-inited', convert_html_js_files)
+ app.connect('html-page-context', setup_js_tag_helper)
return {
'version': 'builtin',
diff --git a/sphinx/builders/latex/__init__.py b/sphinx/builders/latex/__init__.py
index a98b80289..382914731 100644
--- a/sphinx/builders/latex/__init__.py
+++ b/sphinx/builders/latex/__init__.py
@@ -13,14 +13,14 @@ import os
from os import path
from docutils.frontend import OptionParser
-from docutils.io import FileOutput
from six import text_type
from sphinx import package_dir, addnodes, highlighting
from sphinx.builders import Builder
from sphinx.builders.latex.transforms import (
BibliographyTransform, CitationReferenceTransform, MathReferenceTransform,
- FootnoteDocnameUpdater, LaTeXFootnoteTransform, ShowUrlsTransform
+ FootnoteDocnameUpdater, LaTeXFootnoteTransform, LiteralBlockTransform,
+ ShowUrlsTransform, DocumentTargetTransform,
)
from sphinx.config import string_classes, ENUM
from sphinx.environment import NoUri
@@ -30,7 +30,7 @@ from sphinx.locale import _, __
from sphinx.transforms import SphinxTransformer
from sphinx.util import texescape, logging, status_iterator
from sphinx.util.console import bold, darkgreen # type: ignore
-from sphinx.util.docutils import new_document
+from sphinx.util.docutils import SphinxFileOutput, new_document
from sphinx.util.fileutil import copy_asset_file
from sphinx.util.nodes import inline_all_toctrees
from sphinx.util.osutil import SEP, make_filename
@@ -133,9 +133,8 @@ class LaTeXBuilder(Builder):
toctree_only = False
if len(entry) > 5:
toctree_only = entry[5]
- destination = FileOutput(
- destination_path=path.join(self.outdir, targetname),
- encoding='utf-8')
+ destination = SphinxFileOutput(destination_path=path.join(self.outdir, targetname),
+ encoding='utf-8', overwrite_if_changed=True)
logger.info(__("processing %s..."), targetname, nonl=1)
toctrees = self.env.get_doctree(docname).traverse(addnodes.toctree)
if toctrees:
@@ -223,7 +222,9 @@ class LaTeXBuilder(Builder):
transformer.set_environment(self.env)
transformer.add_transforms([BibliographyTransform,
ShowUrlsTransform,
- LaTeXFootnoteTransform])
+ LaTeXFootnoteTransform,
+ LiteralBlockTransform,
+ DocumentTargetTransform])
transformer.apply_transforms()
def finish(self):
diff --git a/sphinx/builders/latex/nodes.py b/sphinx/builders/latex/nodes.py
index c79adbae4..32ac7c6a0 100644
--- a/sphinx/builders/latex/nodes.py
+++ b/sphinx/builders/latex/nodes.py
@@ -12,6 +12,11 @@
from docutils import nodes
+class captioned_literal_block(nodes.container):
+ """A node for a container of literal_block having a caption."""
+ pass
+
+
class footnotemark(nodes.Inline, nodes.Referential, nodes.TextElement):
"""A node represents ``\footnotemark``."""
pass
diff --git a/sphinx/builders/latex/transforms.py b/sphinx/builders/latex/transforms.py
index 635f2489c..e3cfb2121 100644
--- a/sphinx/builders/latex/transforms.py
+++ b/sphinx/builders/latex/transforms.py
@@ -13,7 +13,7 @@ from docutils import nodes
from sphinx import addnodes
from sphinx.builders.latex.nodes import (
- footnotemark, footnotetext, math_reference, thebibliography
+ captioned_literal_block, footnotemark, footnotetext, math_reference, thebibliography
)
from sphinx.transforms import SphinxTransform
@@ -566,3 +566,33 @@ class MathReferenceTransform(SphinxTransform):
if docname:
refnode = math_reference('', docname=docname, target=node['reftarget'])
node.replace_self(refnode)
+
+
+class LiteralBlockTransform(SphinxTransform):
+ """Replace container nodes for literal_block by captioned_literal_block."""
+ default_priority = 400
+
+ def apply(self):
+ # type: () -> None
+ if self.app.builder.name != 'latex':
+ return
+
+ for node in self.document.traverse(nodes.container):
+ if node.get('literal_block') is True:
+ newnode = captioned_literal_block('', *node.children, **node.attributes)
+ node.replace_self(newnode)
+
+
+class DocumentTargetTransform(SphinxTransform):
+ """Add :doc label to the first section of each document."""
+ default_priority = 400
+
+ def apply(self):
+ # type: () -> None
+ if self.app.builder.name != 'latex':
+ return
+
+ for node in self.document.traverse(addnodes.start_of_file):
+ section = node.next_node(nodes.section)
+ if section:
+ section['ids'].append(':doc') # special label for :doc:
diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py
index 9afe47637..a40cb2709 100644
--- a/sphinx/builders/qthelp.py
+++ b/sphinx/builders/qthelp.py
@@ -140,7 +140,7 @@ class QtHelpBuilder(StandaloneHTMLBuilder):
else:
nspace = 'org.sphinx.%s.%s' % (outname, self.config.version)
- nspace = re.sub('[^a-zA-Z0-9.]', '', nspace)
+ nspace = re.sub('[^a-zA-Z0-9.\-]', '', nspace)
nspace = re.sub(r'\.+', '.', nspace).strip('.')
nspace = nspace.lower()
diff --git a/sphinx/cmd/quickstart.py b/sphinx/cmd/quickstart.py
index d6c7fa22a..14217d1a7 100644
--- a/sphinx/cmd/quickstart.py
+++ b/sphinx/cmd/quickstart.py
@@ -41,7 +41,7 @@ from sphinx import __display_version__, package_dir
from sphinx.locale import __
from sphinx.util import texescape
from sphinx.util.console import ( # type: ignore
- purple, bold, red, turquoise, nocolor, color_terminal
+ colorize, bold, red, turquoise, nocolor, color_terminal
)
from sphinx.util.osutil import ensuredir, make_filename
from sphinx.util.template import SphinxRenderer
@@ -85,8 +85,14 @@ PROMPT_PREFIX = '> '
# function to get input from terminal -- overridden by the test suite
def term_input(prompt):
# type: (unicode) -> unicode
- print(prompt, end='')
- return input('')
+ if sys.platform == 'win32':
+ # Important: On windows, readline is not enabled by default. In these
+ # environment, escape sequences have been broken. To avoid the
+ # problem, quickstart uses ``print()`` to show prompt.
+ print(prompt, end='')
+ return input('')
+ else:
+ return input(prompt)
class ValidationError(Exception):
@@ -186,7 +192,7 @@ def do_prompt(text, default=None, validator=nonempty):
prompt = prompt.encode('utf-8')
except UnicodeEncodeError:
prompt = prompt.encode('latin1')
- prompt = purple(prompt)
+ prompt = colorize('purple', prompt, input_mode=True)
x = term_input(prompt).strip()
if default and not x:
x = default
diff --git a/sphinx/config.py b/sphinx/config.py
index 4ad09c08b..c5f44e57c 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -365,7 +365,7 @@ def eval_config_file(filename, tags):
"called sys.exit()")
raise ConfigError(msg)
except Exception:
- msg = __("There is a programable error in your configuration file:\n\n%s")
+ msg = __("There is a programmable error in your configuration file:\n\n%s")
raise ConfigError(msg % traceback.format_exc())
return namespace
diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py
index 20ad5a20c..a98ab5883 100644
--- a/sphinx/directives/code.py
+++ b/sphinx/directives/code.py
@@ -249,7 +249,7 @@ class LiteralIncludeReader(object):
new_lines = self.read_file(self.filename)
old_filename = self.options.get('diff')
old_lines = self.read_file(old_filename)
- diff = unified_diff(old_lines, new_lines, old_filename, self.filename) # type: ignore
+ diff = unified_diff(old_lines, new_lines, old_filename, self.filename)
return list(diff)
def pyobject_filter(self, lines, location=None):
diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py
index 50e9b2e5e..e6b370d62 100644
--- a/sphinx/domains/c.py
+++ b/sphinx/domains/c.py
@@ -147,7 +147,8 @@ class CObject(ObjectDescription):
fullname = name
if not arglist:
- if self.objtype == 'function':
+ if self.objtype == 'function' or \
+ self.objtype == 'macro' and sig.rstrip().endswith('()'):
# for functions, add an empty parameter list
signode += addnodes.desc_parameterlist()
if const:
diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py
index 830f15f29..aebc5d245 100644
--- a/sphinx/domains/cpp.py
+++ b/sphinx/domains/cpp.py
@@ -296,7 +296,15 @@ _hex_literal_re = re.compile(r'0[xX][0-7a-fA-F][0-7a-fA-F]*')
_binary_literal_re = re.compile(r'0[bB][01][01]*')
_integer_suffix_re = re.compile(r'')
_float_literal_re = re.compile(r'[+-]?[0-9]*\.[0-9]+')
-_identifier_re = re.compile(r'(~?\b[a-zA-Z_][a-zA-Z0-9_]*)\b')
+_anon_identifier_re = re.compile(r'(@[a-zA-Z0-9_])[a-zA-Z0-9_]*\b')
+_identifier_re = re.compile(r'''(?x)
+ ( # This 'extends' _anon_identifier_re with the ordinary identifiers,
+ # make sure they are in sync.
+ (~?\b[a-zA-Z_]) # ordinary identifiers
+ | (@[a-zA-Z0-9_]) # our extension for names of anonymous entities
+ )
+ [a-zA-Z0-9_]*\b
+''')
_whitespace_re = re.compile(r'(?u)\s+')
_string_re = re.compile(r"[LuU8]?('([^'\\]*(?:\\.[^'\\]*)*)'"
r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S)
@@ -611,9 +619,17 @@ class ASTBase(UnicodeMixin):
"""Prefix a name node (a node returned by :meth:`get_name`)."""
raise NotImplementedError(repr(self))
+ def _stringify(self, transform):
+ # type: (Callable[[Any], unicode]) -> unicode
+ raise NotImplementedError(repr(self))
+
def __unicode__(self):
# type: () -> unicode
- raise NotImplementedError(repr(self))
+ return self._stringify(lambda ast: text_type(ast))
+
+ def get_display_string(self):
+ # type: () -> unicode
+ return self._stringify(lambda ast: ast.get_display_string())
def __repr__(self):
# type: () -> str
@@ -635,8 +651,7 @@ class ASTCPPAttribute(ASTBase):
# type: (unicode) -> None
self.arg = arg
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
return "[[" + self.arg + "]]"
def describe_signature(self, signode):
@@ -651,12 +666,11 @@ class ASTGnuAttribute(ASTBase):
self.name = name
self.args = args
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
res = [self.name] # type: List[unicode]
if self.args:
res.append('(')
- res.append(text_type(self.args))
+ res.append(transform(self.args))
res.append(')')
return ''.join(res)
@@ -666,15 +680,14 @@ class ASTGnuAttributeList(ASTBase):
# type: (List[Any]) -> None
self.attrs = attrs
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
res = ['__attribute__(('] # type: List[unicode]
first = True
for attr in self.attrs:
if not first:
res.append(', ')
first = False
- res.append(text_type(attr))
+ res.append(transform(attr))
res.append('))')
return ''.join(res)
@@ -691,8 +704,7 @@ class ASTIdAttribute(ASTBase):
# type: (unicode) -> None
self.id = id
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
return self.id
def describe_signature(self, signode):
@@ -708,8 +720,7 @@ class ASTParenAttribute(ASTBase):
self.id = id
self.arg = arg
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
return self.id + '(' + self.arg + ')'
def describe_signature(self, signode):
@@ -723,7 +734,7 @@ class ASTParenAttribute(ASTBase):
################################################################################
class ASTPointerLiteral(ASTBase):
- def __unicode__(self):
+ def _stringify(self, transform):
return u'nullptr'
def get_id(self, version):
@@ -737,7 +748,7 @@ class ASTBooleanLiteral(ASTBase):
def __init__(self, value):
self.value = value
- def __unicode__(self):
+ def _stringify(self, transform):
if self.value:
return u'true'
else:
@@ -758,7 +769,7 @@ class ASTNumberLiteral(ASTBase):
# type: (unicode) -> None
self.data = data
- def __unicode__(self):
+ def _stringify(self, transform):
return self.data
def get_id(self, version):
@@ -774,7 +785,7 @@ class ASTStringLiteral(ASTBase):
# type: (unicode) -> None
self.data = data
- def __unicode__(self):
+ def _stringify(self, transform):
return self.data
def get_id(self, version):
@@ -787,7 +798,7 @@ class ASTStringLiteral(ASTBase):
class ASTThisLiteral(ASTBase):
- def __unicode__(self):
+ def _stringify(self, transform):
return "this"
def get_id(self, version):
@@ -801,8 +812,8 @@ class ASTParenExpr(ASTBase):
def __init__(self, expr):
self.expr = expr
- def __unicode__(self):
- return '(' + text_type(self.expr) + ')'
+ def _stringify(self, transform):
+ return '(' + transform(self.expr) + ')'
def get_id(self, version):
return self.expr.get_id(version)
@@ -820,19 +831,19 @@ class ASTFoldExpr(ASTBase):
self.op = op
self.rightExpr = rightExpr
- def __unicode__(self):
+ def _stringify(self, transform):
res = [u'(']
if self.leftExpr:
- res.append(text_type(self.leftExpr))
+ res.append(transform(self.leftExpr))
res.append(u' ')
- res.append(text_type(self.op))
+ res.append(transform(self.op))
res.append(u' ')
res.append(u'...')
if self.rightExpr:
res.append(u' ')
- res.append(text_type(self.op))
+ res.append(transform(self.op))
res.append(u' ')
- res.append(text_type(self.rightExpr))
+ res.append(transform(self.rightExpr))
res.append(u')')
return u''.join(res)
@@ -866,14 +877,14 @@ class ASTBinOpExpr(ASTBase):
self.exprs = exprs
self.ops = ops
- def __unicode__(self):
+ def _stringify(self, transform):
res = []
- res.append(text_type(self.exprs[0]))
+ res.append(transform(self.exprs[0]))
for i in range(1, len(self.exprs)):
res.append(' ')
res.append(self.ops[i - 1])
res.append(' ')
- res.append(text_type(self.exprs[i]))
+ res.append(transform(self.exprs[i]))
return u''.join(res)
def get_id(self, version):
@@ -901,14 +912,14 @@ class ASTAssignmentExpr(ASTBase):
self.exprs = exprs
self.ops = ops
- def __unicode__(self):
+ def _stringify(self, transform):
res = []
- res.append(text_type(self.exprs[0]))
+ res.append(transform(self.exprs[0]))
for i in range(1, len(self.exprs)):
res.append(' ')
res.append(self.ops[i - 1])
res.append(' ')
- res.append(text_type(self.exprs[i]))
+ res.append(transform(self.exprs[i]))
return u''.join(res)
def get_id(self, version):
@@ -933,11 +944,11 @@ class ASTCastExpr(ASTBase):
self.typ = typ
self.expr = expr
- def __unicode__(self):
+ def _stringify(self, transform):
res = [u'(']
- res.append(text_type(self.typ))
+ res.append(transform(self.typ))
res.append(u')')
- res.append(text_type(self.expr))
+ res.append(transform(self.expr))
return u''.join(res)
def get_id(self, version):
@@ -955,8 +966,8 @@ class ASTUnaryOpExpr(ASTBase):
self.op = op
self.expr = expr
- def __unicode__(self):
- return text_type(self.op) + text_type(self.expr)
+ def _stringify(self, transform):
+ return transform(self.op) + transform(self.expr)
def get_id(self, version):
return _id_operator_unary_v2[self.op] + self.expr.get_id(version)
@@ -970,8 +981,8 @@ class ASTSizeofParamPack(ASTBase):
def __init__(self, identifier):
self.identifier = identifier
- def __unicode__(self):
- return "sizeof...(" + text_type(self.identifier) + ")"
+ def _stringify(self, transform):
+ return "sizeof...(" + transform(self.identifier) + ")"
def get_id(self, version):
return 'sZ' + self.identifier.get_id(version)
@@ -987,8 +998,8 @@ class ASTSizeofType(ASTBase):
def __init__(self, typ):
self.typ = typ
- def __unicode__(self):
- return "sizeof(" + text_type(self.typ) + ")"
+ def _stringify(self, transform):
+ return "sizeof(" + transform(self.typ) + ")"
def get_id(self, version):
return 'st' + self.typ.get_id(version)
@@ -1003,8 +1014,8 @@ class ASTSizeofExpr(ASTBase):
def __init__(self, expr):
self.expr = expr
- def __unicode__(self):
- return "sizeof " + text_type(self.expr)
+ def _stringify(self, transform):
+ return "sizeof " + transform(self.expr)
def get_id(self, version):
return 'sz' + self.expr.get_id(version)
@@ -1018,8 +1029,8 @@ class ASTAlignofExpr(ASTBase):
def __init__(self, typ):
self.typ = typ
- def __unicode__(self):
- return "alignof(" + text_type(self.typ) + ")"
+ def _stringify(self, transform):
+ return "alignof(" + transform(self.typ) + ")"
def get_id(self, version):
return 'at' + self.typ.get_id(version)
@@ -1034,8 +1045,8 @@ class ASTNoexceptExpr(ASTBase):
def __init__(self, expr):
self.expr = expr
- def __unicode__(self):
- return "noexcept(" + text_type(self.expr) + ")"
+ def _stringify(self, transform):
+ return "noexcept(" + transform(self.expr) + ")"
def get_id(self, version):
return 'nx' + self.expr.get_id(version)
@@ -1053,12 +1064,12 @@ class ASTExplicitCast(ASTBase):
self.typ = typ
self.expr = expr
- def __unicode__(self):
+ def _stringify(self, transform):
res = [self.cast]
res.append('<')
- res.append(text_type(self.typ))
+ res.append(transform(self.typ))
res.append('>(')
- res.append(text_type(self.expr))
+ res.append(transform(self.expr))
res.append(')')
return u''.join(res)
@@ -1082,8 +1093,8 @@ class ASTTypeId(ASTBase):
self.typeOrExpr = typeOrExpr
self.isType = isType
- def __unicode__(self):
- return 'typeid(' + text_type(self.typeOrExpr) + ')'
+ def _stringify(self, transform):
+ return 'typeid(' + transform(self.typeOrExpr) + ')'
def get_id(self, version):
prefix = 'ti' if self.isType else 'te'
@@ -1100,14 +1111,14 @@ class ASTPostfixCallExpr(ASTBase):
def __init__(self, exprs):
self.exprs = exprs
- def __unicode__(self):
+ def _stringify(self, transform):
res = [u'(']
first = True
for e in self.exprs:
if not first:
res.append(u', ')
first = False
- res.append(text_type(e))
+ res.append(transform(e))
res.append(u')')
return u''.join(res)
@@ -1133,8 +1144,8 @@ class ASTPostfixArray(ASTBase):
def __init__(self, expr):
self.expr = expr
- def __unicode__(self):
- return u'[' + text_type(self.expr) + ']'
+ def _stringify(self, transform):
+ return u'[' + transform(self.expr) + ']'
def get_id(self, idPrefix, version):
return 'ix' + idPrefix + self.expr.get_id(version)
@@ -1146,7 +1157,7 @@ class ASTPostfixArray(ASTBase):
class ASTPostfixInc(ASTBase):
- def __unicode__(self):
+ def _stringify(self, transform):
return u'++'
def get_id(self, idPrefix, version):
@@ -1157,7 +1168,7 @@ class ASTPostfixInc(ASTBase):
class ASTPostfixDec(ASTBase):
- def __unicode__(self):
+ def _stringify(self, transform):
return u'--'
def get_id(self, idPrefix, version):
@@ -1171,8 +1182,8 @@ class ASTPostfixMember(ASTBase):
def __init__(self, name):
self.name = name
- def __unicode__(self):
- return u'.' + text_type(self.name)
+ def _stringify(self, transform):
+ return u'.' + transform(self.name)
def get_id(self, idPrefix, version):
return 'dt' + idPrefix + self.name.get_id(version)
@@ -1186,8 +1197,8 @@ class ASTPostfixMemberOfPointer(ASTBase):
def __init__(self, name):
self.name = name
- def __unicode__(self):
- return u'->' + text_type(self.name)
+ def _stringify(self, transform):
+ return u'->' + transform(self.name)
def get_id(self, idPrefix, version):
return 'pt' + idPrefix + self.name.get_id(version)
@@ -1203,10 +1214,10 @@ class ASTPostfixExpr(ASTBase):
self.prefix = prefix
self.postFixes = postFixes
- def __unicode__(self):
- res = [text_type(self.prefix)]
+ def _stringify(self, transform):
+ res = [transform(self.prefix)]
for p in self.postFixes:
- res.append(text_type(p))
+ res.append(transform(p))
return u''.join(res)
def get_id(self, version):
@@ -1221,11 +1232,27 @@ class ASTPostfixExpr(ASTBase):
p.describe_signature(signode, mode, env, symbol)
+class ASTPackExpansionExpr(ASTBase):
+ def __init__(self, expr):
+ self.expr = expr
+
+ def _stringify(self, transform):
+ return transform(self.expr) + '...'
+
+ def get_id(self, version):
+ id = self.expr.get_id(version)
+ return 'sp' + id
+
+ def describe_signature(self, signode, mode, env, symbol):
+ self.expr.describe_signature(signode, mode, env, symbol)
+ signode += nodes.Text('...')
+
+
class ASTFallbackExpr(ASTBase):
def __init__(self, expr):
self.expr = expr
- def __unicode__(self):
+ def _stringify(self, transform):
return self.expr
def get_id(self, version):
@@ -1243,10 +1270,16 @@ class ASTIdentifier(ASTBase):
def __init__(self, identifier):
# type: (unicode) -> None
assert identifier is not None
+ assert len(identifier) != 0
self.identifier = identifier
+ def is_anon(self):
+ return self.identifier[0] == '@'
+
def get_id(self, version):
# type: (int) -> unicode
+ if self.is_anon() and version < 3:
+ raise NoOldIdError()
if version == 1:
if self.identifier == 'size_t':
return 's'
@@ -1258,12 +1291,21 @@ class ASTIdentifier(ASTBase):
# a destructor, just use an arbitrary version of dtors
return 'D0'
else:
- return text_type(len(self.identifier)) + self.identifier
+ if self.is_anon():
+ return u'Ut%d_%s' % (len(self.identifier) - 1, self.identifier[1:])
+ else:
+ return text_type(len(self.identifier)) + self.identifier
+
+ # and this is where we finally make a difference between __unicode__ and the display string
def __unicode__(self):
# type: () -> unicode
return self.identifier
+ def get_display_string(self):
+ # type: () -> unicode
+ return u"[anonymous]" if self.is_anon() else self.identifier
+
def describe_signature(self, signode, mode, env, prefix, templateArgs, symbol):
# type: (Any, unicode, BuildEnvironment, unicode, unicode, Symbol) -> None
_verify_description_mode(mode)
@@ -1275,12 +1317,21 @@ class ASTIdentifier(ASTBase):
classname=None)
key = symbol.get_lookup_key()
pnode['cpp:parent_key'] = key
- pnode += nodes.Text(self.identifier)
+ if self.is_anon():
+ pnode += nodes.strong(text="[anonymous]")
+ else:
+ pnode += nodes.Text(self.identifier)
signode += pnode
elif mode == 'lastIsName':
- signode += addnodes.desc_name(self.identifier, self.identifier)
+ if self.is_anon():
+ signode += nodes.strong(text="[anonymous]")
+ else:
+ signode += addnodes.desc_name(self.identifier, self.identifier)
elif mode == 'noneIsName':
- signode += nodes.Text(self.identifier)
+ if self.is_anon():
+ signode += nodes.strong(text="[anonymous]")
+ else:
+ signode += nodes.Text(self.identifier)
else:
raise Exception('Unknown description mode: %s' % mode)
@@ -1311,8 +1362,7 @@ class ASTTemplateKeyParamPackIdDefault(ASTBase):
res.append('0') # we need to put something
return ''.join(res)
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
res = [self.key] # type: List[unicode]
if self.parameterPack:
if self.identifier:
@@ -1321,10 +1371,10 @@ class ASTTemplateKeyParamPackIdDefault(ASTBase):
if self.identifier:
if not self.parameterPack:
res.append(' ')
- res.append(text_type(self.identifier))
+ res.append(transform(self.identifier))
if self.default:
res.append(' = ')
- res.append(text_type(self.default))
+ res.append(transform(self.default))
return ''.join(res)
def describe_signature(self, signode, mode, env, symbol):
@@ -1373,9 +1423,8 @@ class ASTTemplateParamType(ASTBase):
else:
return self.data.get_id(version)
- def __unicode__(self):
- # type: () -> unicode
- return text_type(self.data)
+ def _stringify(self, transform):
+ return transform(self.data)
def describe_signature(self, signode, mode, env, symbol):
# type: (addnodes.desc_signature, unicode, BuildEnvironment, Symbol) -> None
@@ -1404,12 +1453,11 @@ class ASTTemplateParamConstrainedTypeWithInit(ASTBase):
else:
return self.type.get_id(version)
- def __unicode__(self):
- # type: () -> unicode
- res = text_type(self.type)
+ def _stringify(self, transform):
+ res = transform(self.type)
if self.init:
res += " = "
- res += text_type(self.init)
+ res += transform(self.init)
return res
def describe_signature(self, signode, mode, env, symbol):
@@ -1448,9 +1496,8 @@ class ASTTemplateParamTemplateType(ASTBase):
else:
return self.nestedParams.get_id(version) + self.data.get_id(version)
- def __unicode__(self):
- # type: () -> unicode
- return text_type(self.nestedParams) + text_type(self.data)
+ def _stringify(self, transform):
+ return transform(self.nestedParams) + transform(self.data)
def describe_signature(self, signode, mode, env, symbol):
# type: (addnodes.desc_signature, unicode, BuildEnvironment, Symbol) -> None
@@ -1492,9 +1539,8 @@ class ASTTemplateParamNonType(ASTBase):
else:
return '_' + self.param.get_id(version)
- def __unicode__(self):
- # type: () -> unicode
- return text_type(self.param)
+ def _stringify(self, transform):
+ return transform(self.param)
def describe_signature(self, signode, mode, env, symbol):
# type: (addnodes.desc_signature, unicode, BuildEnvironment, Symbol) -> None
@@ -1518,11 +1564,10 @@ class ASTTemplateParams(ASTBase):
res.append("E")
return ''.join(res)
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
res = []
res.append(u"template<")
- res.append(u", ".join(text_type(a) for a in self.params))
+ res.append(u", ".join(transform(a) for a in self.params))
res.append(u"> ")
return ''.join(res)
@@ -1595,12 +1640,11 @@ class ASTTemplateIntroductionParameter(ASTBase):
else:
return res
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
res = [] # type: List[unicode]
if self.parameterPack:
res.append('...')
- res.append(text_type(self.identifier))
+ res.append(transform(self.identifier))
return ''.join(res)
def describe_signature(self, signode, mode, env, symbol):
@@ -1636,12 +1680,11 @@ class ASTTemplateIntroduction(ASTBase):
res.append("E")
return ''.join(res)
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
res = []
- res.append(text_type(self.concept))
+ res.append(transform(self.concept))
res.append('{')
- res.append(', '.join(text_type(param) for param in self.params))
+ res.append(', '.join(transform(param) for param in self.params))
res.append('} ')
return ''.join(res)
@@ -1677,11 +1720,10 @@ class ASTTemplateDeclarationPrefix(ASTBase):
res.append(t.get_id(version))
return u''.join(res)
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
res = []
for t in self.templates:
- res.append(text_type(t))
+ res.append(transform(t))
return u''.join(res)
def describe_signature(self, signode, mode, env, symbol, lineSpec):
@@ -1696,6 +1738,9 @@ class ASTOperatorBuildIn(ASTBase):
# type: (unicode) -> None
self.op = op
+ def is_anon(self):
+ return False
+
def is_operator(self):
# type: () -> bool
return True
@@ -1711,8 +1756,7 @@ class ASTOperatorBuildIn(ASTBase):
'be mapped to an id.' % self.op)
return ids[self.op]
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
if self.op in ('new', 'new[]', 'delete', 'delete[]'):
return u'operator ' + self.op
else:
@@ -1733,6 +1777,9 @@ class ASTOperatorType(ASTBase):
# type: (Any) -> None
self.type = type
+ def is_anon(self):
+ return False
+
def is_operator(self):
# type: () -> bool
return True
@@ -1744,9 +1791,8 @@ class ASTOperatorType(ASTBase):
else:
return u'cv' + self.type.get_id(version)
- def __unicode__(self):
- # type: () -> unicode
- return u''.join(['operator ', text_type(self.type)])
+ def _stringify(self, transform):
+ return u''.join(['operator ', transform(self.type)])
def get_name_no_template(self):
# type: () -> unicode
@@ -1767,6 +1813,9 @@ class ASTOperatorLiteral(ASTBase):
# type: (Any) -> None
self.identifier = identifier
+ def is_anon(self):
+ return False
+
def is_operator(self):
# type: () -> bool
return True
@@ -1778,9 +1827,8 @@ class ASTOperatorLiteral(ASTBase):
else:
return u'li' + self.identifier.get_id(version)
- def __unicode__(self):
- # type: () -> unicode
- return u'operator""' + text_type(self.identifier)
+ def _stringify(self, transform):
+ return u'operator""' + transform(self.identifier)
def describe_signature(self, signode, mode, env, prefix, templateArgs, symbol):
# type: (addnodes.desc_signature, unicode, Any, unicode, unicode, Symbol) -> None
@@ -1797,9 +1845,8 @@ class ASTTemplateArgConstant(ASTBase):
# type: (Any) -> None
self.value = value
- def __unicode__(self):
- # type: () -> unicode
- return text_type(self.value)
+ def _stringify(self, transform):
+ return transform(self.value)
def get_id(self, version):
# type: (int) -> unicode
@@ -1837,9 +1884,8 @@ class ASTTemplateArgs(ASTBase):
res.append('E')
return u''.join(res)
- def __unicode__(self):
- # type: () -> unicode
- res = ', '.join(text_type(a) for a in self.args)
+ def _stringify(self, transform):
+ res = ', '.join(transform(a) for a in self.args)
return '<' + res + '>'
def describe_signature(self, signode, mode, env, symbol):
@@ -1872,11 +1918,10 @@ class ASTNestedNameElement(ASTBase):
res += self.templateArgs.get_id(version)
return res
- def __unicode__(self):
- # type: () -> unicode
- res = text_type(self.identOrOp)
+ def _stringify(self, transform):
+ res = transform(self.identOrOp)
if self.templateArgs:
- res += text_type(self.templateArgs)
+ res += transform(self.templateArgs)
return res
def describe_signature(self, signode, mode, env, prefix, symbol):
@@ -1929,8 +1974,7 @@ class ASTNestedName(ASTBase):
res.append('E')
return u''.join(res)
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
res = [] # type: List[unicode]
if self.rooted:
res.append('')
@@ -1938,9 +1982,9 @@ class ASTNestedName(ASTBase):
n = self.names[i]
t = self.templates[i]
if t:
- res.append("template " + text_type(n))
+ res.append("template " + transform(n))
else:
- res.append(text_type(n))
+ res.append(transform(n))
return '::'.join(res)
def describe_signature(self, signode, mode, env, symbol):
@@ -2007,8 +2051,7 @@ class ASTTrailingTypeSpecFundamental(ASTBase):
# type: (unicode) -> None
self.name = name
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
return self.name
def get_id(self, version):
@@ -2049,13 +2092,12 @@ class ASTTrailingTypeSpecName(ASTBase):
# type: (int) -> unicode
return self.nestedName.get_id(version)
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
res = [] # type: List[unicode]
if self.prefix:
res.append(self.prefix)
res.append(' ')
- res.append(text_type(self.nestedName))
+ res.append(transform(self.nestedName))
return u''.join(res)
def describe_signature(self, signode, mode, env, symbol):
@@ -2067,7 +2109,7 @@ class ASTTrailingTypeSpecName(ASTBase):
class ASTTrailingTypeSpecDecltypeAuto(ASTBase):
- def __unicode__(self):
+ def _stringify(self, transform):
return u'decltype(auto)'
def get_id(self, version):
@@ -2084,8 +2126,8 @@ class ASTTrailingTypeSpecDecltype(ASTBase):
def __init__(self, expr):
self.expr = expr
- def __unicode__(self):
- return u'decltype(' + text_type(self.expr) + ')'
+ def _stringify(self, transform):
+ return u'decltype(' + transform(self.expr) + ')'
def get_id(self, version):
if version == 1:
@@ -2116,12 +2158,11 @@ class ASTFunctionParameter(ASTBase):
else:
return self.arg.get_id(version)
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
if self.ellipsis:
return '...'
else:
- return text_type(self.arg)
+ return transform(self.arg)
def describe_signature(self, signode, mode, env, symbol):
# type: (addnodes.desc_signature, unicode, BuildEnvironment, Symbol) -> None
@@ -2178,8 +2219,7 @@ class ASTParametersQualifiers(ASTBase):
else:
return u''.join(a.get_id(version) for a in self.args)
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
res = [] # type: List[unicode]
res.append('(')
first = True
@@ -2274,10 +2314,9 @@ class ASTDeclSpecsSimple(ASTBase):
self.friend or other.friend,
self.attrs + other.attrs)
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
res = [] # type: List[unicode]
- res.extend(text_type(attr) for attr in self.attrs)
+ res.extend(transform(attr) for attr in self.attrs)
if self.storage:
res.append(self.storage)
if self.threadLocal:
@@ -2361,10 +2400,9 @@ class ASTDeclSpecs(ASTBase):
res.append(self.trailingTypeSpec.get_id(version))
return u''.join(res)
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
res = [] # type: List[unicode]
- l = text_type(self.leftSpecs)
+ l = transform(self.leftSpecs)
if len(l) > 0:
if len(res) > 0:
res.append(" ")
@@ -2372,7 +2410,7 @@ class ASTDeclSpecs(ASTBase):
if self.trailingTypeSpec:
if len(res) > 0:
res.append(" ")
- res.append(text_type(self.trailingTypeSpec))
+ res.append(transform(self.trailingTypeSpec))
r = text_type(self.rightSpecs)
if len(r) > 0:
if len(res) > 0:
@@ -2411,10 +2449,9 @@ class ASTArray(ASTBase):
def __init__(self, size):
self.size = size
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
if self.size:
- return u''.join(['[', text_type(self.size), ']'])
+ return u'[' + transform(self.size) + ']'
else:
return u'[]'
@@ -2463,8 +2500,7 @@ class ASTDeclaratorPtr(ASTBase):
# TODO: if has paramPack, then False ?
return True
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
res = ['*'] # type: List[unicode]
if self.volatile:
res.append('volatile')
@@ -2475,7 +2511,7 @@ class ASTDeclaratorPtr(ASTBase):
if self.const or self.volatile:
if self.next.require_space_after_declSpecs:
res.append(' ')
- res.append(text_type(self.next))
+ res.append(transform(self.next))
return u''.join(res)
def get_modifiers_id(self, version):
@@ -2559,9 +2595,8 @@ class ASTDeclaratorRef(ASTBase):
# type: () -> bool
return self.next.require_space_after_declSpecs()
- def __unicode__(self):
- # type: () -> unicode
- return '&' + text_type(self.next)
+ def _stringify(self, transform):
+ return '&' + transform(self.next)
def get_modifiers_id(self, version):
# type: (int) -> unicode
@@ -2615,9 +2650,8 @@ class ASTDeclaratorParamPack(ASTBase):
# type: () -> bool
return False
- def __unicode__(self):
- # type: () -> unicode
- res = text_type(self.next)
+ def _stringify(self, transform):
+ res = transform(self.next)
if self.next.name:
res = ' ' + res
return '...' + res
@@ -2680,10 +2714,9 @@ class ASTDeclaratorMemPtr(ASTBase):
# type: () -> bool
return True
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
res = []
- res.append(text_type(self.className))
+ res.append(transform(self.className))
res.append('::*')
if self.volatile:
res.append(' volatile')
@@ -2691,7 +2724,7 @@ class ASTDeclaratorMemPtr(ASTBase):
res.append(' const')
if self.next.require_space_after_declSpecs():
res.append(' ')
- res.append(text_type(self.next))
+ res.append(transform(self.next))
return ''.join(res)
def get_modifiers_id(self, version):
@@ -2777,12 +2810,11 @@ class ASTDeclaratorParen(ASTBase):
# type: () -> bool
return True
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
res = ['('] # type: List[unicode]
- res.append(text_type(self.inner))
+ res.append(transform(self.inner))
res.append(')')
- res.append(text_type(self.next))
+ res.append(transform(self.next))
return ''.join(res)
def get_modifiers_id(self, version):
@@ -2885,15 +2917,14 @@ class ASTDeclaratorNameParamQual(ASTBase):
# type: () -> bool
return self.paramQual is not None
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
res = []
if self.declId:
- res.append(text_type(self.declId))
+ res.append(transform(self.declId))
for op in self.arrayOps:
- res.append(text_type(op))
+ res.append(transform(op))
if self.paramQual:
- res.append(text_type(self.paramQual))
+ res.append(transform(self.paramQual))
return u''.join(res)
def describe_signature(self, signode, mode, env, symbol):
@@ -2911,9 +2942,8 @@ class ASTInitializer(ASTBase):
def __init__(self, value):
self.value = value
- def __unicode__(self):
- # type: () -> unicode
- return u''.join([' = ', text_type(self.value)])
+ def _stringify(self, transform):
+ return u' = ' + transform(self.value)
def describe_signature(self, signode, mode, env, symbol):
# type: (addnodes.desc_signature, unicode, BuildEnvironment, Symbol) -> None
@@ -2986,14 +3016,13 @@ class ASTType(ASTBase):
res.append(typeId)
return u''.join(res)
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
res = []
- declSpecs = text_type(self.declSpecs)
+ declSpecs = transform(self.declSpecs)
res.append(declSpecs)
if self.decl.require_space_after_declSpecs() and len(declSpecs) > 0:
res.append(u' ')
- res.append(text_type(self.decl))
+ res.append(transform(self.decl))
return u''.join(res)
def get_type_declaration_prefix(self):
@@ -3010,7 +3039,7 @@ class ASTType(ASTBase):
if (self.decl.require_space_after_declSpecs() and
len(text_type(self.declSpecs)) > 0):
signode += nodes.Text(' ')
- # for paramters that don't really declare new names we get 'markType',
+ # for parameters that don't really declare new names we get 'markType',
# this should not be propagated, but be 'noneIsName'.
if mode == 'markType':
mode = 'noneIsName'
@@ -3037,12 +3066,11 @@ class ASTTypeWithInit(ASTBase):
+ self.type.get_id(version)
return symbol.get_full_nested_name().get_id(version)
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
res = []
- res.append(text_type(self.type))
+ res.append(transform(self.type))
if self.init:
- res.append(text_type(self.init))
+ res.append(transform(self.init))
return u''.join(res)
def describe_signature(self, signode, mode, env, symbol):
@@ -3065,13 +3093,12 @@ class ASTTypeUsing(ASTBase):
raise NoOldIdError()
return symbol.get_full_nested_name().get_id(version)
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
res = []
- res.append(text_type(self.name))
+ res.append(transform(self.name))
if self.type:
res.append(' = ')
- res.append(text_type(self.type))
+ res.append(transform(self.type))
return u''.join(res)
def get_type_declaration_prefix(self):
@@ -3104,11 +3131,10 @@ class ASTConcept(ASTBase):
raise NoOldIdError()
return symbol.get_full_nested_name().get_id(version)
- def __unicode__(self):
- # type: () -> unicode
- res = text_type(self.nestedName)
+ def _stringify(self, transform):
+ res = transform(self.nestedName)
if self.initializer:
- res += text_type(self.initializer)
+ res += transform(self.initializer)
return res
def describe_signature(self, signode, mode, env, symbol):
@@ -3126,15 +3152,14 @@ class ASTBaseClass(ASTBase):
self.virtual = virtual
self.pack = pack
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
res = [] # type: List[unicode]
if self.visibility != 'private':
res.append(self.visibility)
res.append(' ')
if self.virtual:
res.append('virtual ')
- res.append(text_type(self.name))
+ res.append(transform(self.name))
if self.pack:
res.append('...')
return u''.join(res)
@@ -3165,10 +3190,9 @@ class ASTClass(ASTBase):
# type: (int, unicode, Symbol) -> unicode
return symbol.get_full_nested_name().get_id(version)
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
res = []
- res.append(text_type(self.name))
+ res.append(transform(self.name))
if self.final:
res.append(' final')
if len(self.bases) > 0:
@@ -3178,7 +3202,7 @@ class ASTClass(ASTBase):
if not first:
res.append(', ')
first = False
- res.append(text_type(b))
+ res.append(transform(b))
return u''.join(res)
def describe_signature(self, signode, mode, env, symbol):
@@ -3196,6 +3220,26 @@ class ASTClass(ASTBase):
signode.pop()
+class ASTUnion(ASTBase):
+ def __init__(self, name):
+ # type: (Any) -> None
+ self.name = name
+
+ def get_id(self, version, objectType, symbol):
+ # type: (int, unicode, Symbol) -> unicode
+ if version == 1:
+ raise NoOldIdError()
+ return symbol.get_full_nested_name().get_id(version)
+
+ def _stringify(self, transform):
+ return transform(self.name)
+
+ def describe_signature(self, signode, mode, env, symbol):
+ # type: (addnodes.desc_signature, unicode, BuildEnvironment, Symbol) -> None
+ _verify_description_mode(mode)
+ self.name.describe_signature(signode, mode, env, symbol=symbol)
+
+
class ASTEnum(ASTBase):
def __init__(self, name, scoped, underlyingType):
# type: (Any, unicode, Any) -> None
@@ -3209,16 +3253,15 @@ class ASTEnum(ASTBase):
raise NoOldIdError()
return symbol.get_full_nested_name().get_id(version)
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
res = [] # type: List[unicode]
if self.scoped:
res.append(self.scoped)
res.append(' ')
- res.append(text_type(self.name))
+ res.append(transform(self.name))
if self.underlyingType:
res.append(' : ')
- res.append(text_type(self.underlyingType))
+ res.append(transform(self.underlyingType))
return u''.join(res)
def describe_signature(self, signode, mode, env, symbol):
@@ -3244,12 +3287,11 @@ class ASTEnumerator(ASTBase):
raise NoOldIdError()
return symbol.get_full_nested_name().get_id(version)
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
res = []
- res.append(text_type(self.name))
+ res.append(transform(self.name))
if self.init:
- res.append(text_type(self.init))
+ res.append(transform(self.init))
return u''.join(res)
def describe_signature(self, signode, mode, env, symbol):
@@ -3318,15 +3360,14 @@ class ASTDeclaration(ASTBase):
# type: () -> unicode
return self.get_id(_max_id, True)
- def __unicode__(self):
- # type: () -> unicode
+ def _stringify(self, transform):
res = [] # type: List[unicode]
if self.visibility and self.visibility != "public":
res.append(self.visibility)
res.append(u' ')
if self.templatePrefix:
- res.append(text_type(self.templatePrefix))
- res.append(text_type(self.declaration))
+ res.append(transform(self.templatePrefix))
+ res.append(transform(self.declaration))
return u''.join(res)
def describe_signature(self, signode, mode, env, options):
@@ -3361,6 +3402,8 @@ class ASTDeclaration(ASTBase):
pass
elif self.objectType == 'class':
mainDeclNode += addnodes.desc_annotation('class ', 'class ')
+ elif self.objectType == 'union':
+ mainDeclNode += addnodes.desc_annotation('union ', 'union ')
elif self.objectType == 'enum':
prefix = 'enum '
if self.scoped: # type: ignore
@@ -3395,6 +3438,12 @@ class Symbol(object):
if self.declaration:
assert self.docname
+ def __setattr__(self, key, value):
+ if key == "children":
+ assert False
+ else:
+ return object.__setattr__(self, key, value)
+
def __init__(self, parent, identOrOp,
templateParams, templateArgs, declaration, docname):
# type: (Any, Any, Any, Any, Any, unicode) -> None
@@ -3407,13 +3456,15 @@ class Symbol(object):
self.isRedeclaration = False
self._assert_invariants()
- self.children = [] # type: List[Any]
+ self._children = [] # type: List[Any]
+ self._anonChildren = [] # type: List[Any]
+ # note: _children includes _anonChildren
if self.parent:
- self.parent.children.append(self)
+ self.parent._children.append(self)
if self.declaration:
self.declaration.symbol = self
# add symbols for the template params
- # (do it after self.children has been initialised
+ # (do it after self._children has been initialised
if self.templateParams:
for p in self.templateParams.params:
if not p.get_identifier():
@@ -3458,21 +3509,31 @@ class Symbol(object):
def clear_doc(self, docname):
# type: (unicode) -> None
newChildren = []
- for sChild in self.children:
+ for sChild in self._children:
sChild.clear_doc(docname)
if sChild.declaration and sChild.docname == docname:
sChild.declaration = None
sChild.docname = None
newChildren.append(sChild)
- self.children = newChildren
+ self._children = newChildren
def get_all_symbols(self):
# type: () -> Iterator[Any]
yield self
- for sChild in self.children:
+ for sChild in self._children:
for s in sChild.get_all_symbols():
yield s
+ @property
+ def children_recurse_anon(self):
+ for c in self._children:
+ yield c
+ if not c.identOrOp.is_anon():
+ continue
+ # TODO: change to 'yield from' when Python 2 support is dropped
+ for nested in c.children_recurse_anon:
+ yield nested
+
def get_lookup_key(self):
# type: () -> List[Tuple[ASTNestedNameElement, Any]]
symbols = []
@@ -3497,14 +3558,17 @@ class Symbol(object):
return ASTNestedName(names, templates, rooted=False)
def _find_named_symbol(self, identOrOp, templateParams, templateArgs,
- templateShorthand, matchSelf):
- # type: (Any, Any, Any, Any, bool) -> Symbol
+ templateShorthand, matchSelf, recurseInAnon):
+ # type: (Any, Any, Any, Any, bool, bool) -> Symbol
def isSpecialization():
# the names of the template parameters must be given exactly as args
# and params that are packs must in the args be the name expanded
if len(templateParams.params) != len(templateArgs.args):
return True
+ # having no template params and no arguments is also a specialization
+ if len(templateParams.params) == 0:
+ return True
for i in range(len(templateParams.params)):
param = templateParams.params[i]
arg = templateArgs.args[i]
@@ -3548,7 +3612,8 @@ class Symbol(object):
return True
if matchSelf and matches(self):
return self
- for s in self.children:
+ children = self.children_recurse_anon if recurseInAnon else self._children
+ for s in children:
if matches(s):
return s
return None
@@ -3580,7 +3645,8 @@ class Symbol(object):
templateParams,
templateArgs,
templateShorthand=False,
- matchSelf=False)
+ matchSelf=False,
+ recurseInAnon=True)
if symbol is None:
symbol = Symbol(parent=parentSymbol, identOrOp=identOrOp,
templateParams=templateParams,
@@ -3603,7 +3669,8 @@ class Symbol(object):
templateParams,
templateArgs,
templateShorthand=False,
- matchSelf=False)
+ matchSelf=False,
+ recurseInAnon=True)
if symbol:
if not declaration:
# good, just a scope creation
@@ -3648,15 +3715,16 @@ class Symbol(object):
def merge_with(self, other, docnames, env):
# type: (Any, List[unicode], BuildEnvironment) -> None
assert other is not None
- for otherChild in other.children:
- ourChild = self._find_named_symbol(otherChild.identOrOp,
- otherChild.templateParams,
- otherChild.templateArgs,
+ for otherChild in other._children:
+ ourChild = self._find_named_symbol(identOrOp=otherChild.identOrOp,
+ templateParams=otherChild.templateParams,
+ templateArgs=otherChild.templateArgs,
templateShorthand=False,
- matchSelf=False)
+ matchSelf=False,
+ recurseInAnon=False)
if ourChild is None:
# TODO: hmm, should we prune by docnames?
- self.children.append(otherChild)
+ self._children.append(otherChild)
otherChild.parent = self
otherChild._assert_invariants()
continue
@@ -3696,11 +3764,12 @@ class Symbol(object):
templateDecls = []
return self._add_symbols(nestedName, templateDecls, declaration, docname)
- def find_identifier(self, identOrOp, matchSelf):
- # type: (Any, bool) -> Symbol
+ def find_identifier(self, identOrOp, matchSelf, recurseInAnon):
+ # type: (Any, bool, bool) -> Symbol
if matchSelf and self.identOrOp == identOrOp:
return self
- for s in self.children:
+ children = self.children_recurse_anon if recurseInAnon else self._children
+ for s in children:
if s.identOrOp == identOrOp:
return s
return None
@@ -3714,13 +3783,15 @@ class Symbol(object):
s = s._find_named_symbol(identOrOp,
templateParams, templateArgs,
templateShorthand=False,
- matchSelf=False)
+ matchSelf=False,
+ recurseInAnon=False)
if not s:
return None
return s
- def find_name(self, nestedName, templateDecls, typ, templateShorthand, matchSelf):
- # type: (Any, Any, Any, Any, bool) -> Symbol
+ def find_name(self, nestedName, templateDecls, typ, templateShorthand,
+ matchSelf, recurseInAnon):
+ # type: (Any, Any, Any, Any, bool, bool) -> Symbol
# templateShorthand: missing template parameter lists for templates is ok
# TODO: unify this with the _add_symbols
@@ -3737,7 +3808,8 @@ class Symbol(object):
if not firstName.is_operator():
while parentSymbol.parent:
if parentSymbol.find_identifier(firstName.identOrOp,
- matchSelf=matchSelf):
+ matchSelf=matchSelf,
+ recurseInAnon=recurseInAnon):
# if we are in the scope of a constructor but wants to reference the class
# we need to walk one extra up
if (len(names) == 1 and typ == 'class' and matchSelf and
@@ -3762,14 +3834,16 @@ class Symbol(object):
symbol = parentSymbol._find_named_symbol(identOrOp,
templateParams, templateArgs,
templateShorthand=templateShorthand,
- matchSelf=matchSelf)
+ matchSelf=matchSelf,
+ recurseInAnon=recurseInAnon)
if symbol is not None:
return symbol
# try without template params and args
symbol = parentSymbol._find_named_symbol(identOrOp,
None, None,
templateShorthand=templateShorthand,
- matchSelf=matchSelf)
+ matchSelf=matchSelf,
+ recurseInAnon=recurseInAnon)
return symbol
else:
identOrOp = name.identOrOp
@@ -3782,7 +3856,8 @@ class Symbol(object):
symbol = parentSymbol._find_named_symbol(identOrOp,
templateParams, templateArgs,
templateShorthand=templateShorthand,
- matchSelf=matchSelf)
+ matchSelf=matchSelf,
+ recurseInAnon=recurseInAnon)
if symbol is None:
# TODO: maybe search without template args
return None
@@ -3822,7 +3897,7 @@ class Symbol(object):
def dump(self, indent):
# type: (int) -> unicode
res = [self.to_string(indent)]
- for c in self.children:
+ for c in self._children:
res.append(c.dump(indent + 1))
return ''.join(res)
@@ -4275,6 +4350,9 @@ class DefinitionParser(object):
if self.skip_string('*'):
# don't steal the dot
self.pos -= 2
+ elif self.skip_string('..'):
+ # don't steal the dot
+ self.pos -= 3
else:
name = self._parse_nested_name()
postFixes.append(ASTPostfixMember(name)) # type: ignore
@@ -4301,7 +4379,11 @@ class DefinitionParser(object):
while True:
self.skip_ws()
expr = self._parse_expression(inTemplate=False)
- exprs.append(expr)
+ self.skip_ws()
+ if self.skip_string('...'):
+ exprs.append(ASTPackExpansionExpr(expr))
+ else:
+ exprs.append(expr)
self.skip_ws()
if self.skip_string(')'):
break
@@ -5259,6 +5341,11 @@ class DefinitionParser(object):
break
return ASTClass(name, final, bases)
+ def _parse_union(self):
+ # type: () -> ASTUnion
+ name = self._parse_nested_name()
+ return ASTUnion(name)
+
def _parse_enum(self):
# type: () -> ASTEnum
scoped = None # type: unicode # is set by CPPEnumObject
@@ -5466,7 +5553,7 @@ class DefinitionParser(object):
def parse_declaration(self, objectType):
# type: (unicode) -> ASTDeclaration
if objectType not in ('type', 'concept', 'member',
- 'function', 'class', 'enum', 'enumerator'):
+ 'function', 'class', 'union', 'enum', 'enumerator'):
raise Exception('Internal error, unknown objectType "%s".' % objectType)
visibility = None
templatePrefix = None
@@ -5505,6 +5592,8 @@ class DefinitionParser(object):
declaration = self._parse_type(named=True, outer='function')
elif objectType == 'class':
declaration = self._parse_class()
+ elif objectType == 'union':
+ declaration = self._parse_union()
elif objectType == 'enum':
declaration = self._parse_enum()
elif objectType == 'enumerator':
@@ -5623,7 +5712,7 @@ class CPPObject(ObjectDescription):
return
targetSymbol = parentSymbol.parent
- s = targetSymbol.find_identifier(symbol.identOrOp, matchSelf=False)
+ s = targetSymbol.find_identifier(symbol.identOrOp, matchSelf=False, recurseInAnon=True)
if s is not None:
# something is already declared with that name
return
@@ -5652,7 +5741,7 @@ class CPPObject(ObjectDescription):
self.warn('Index id generation for C++ object "%s" failed, please '
'report as bug (id=%s).' % (text_type(ast), newestId))
- name = text_type(ast.symbol.get_full_nested_name()).lstrip(':')
+ name = ast.symbol.get_full_nested_name().get_display_string().lstrip(':')
# Add index entry, but not if it's a declaration inside a concept
isInConcept = False
s = ast.symbol.parent
@@ -5807,6 +5896,16 @@ class CPPClassObject(CPPObject):
return parser.parse_declaration("class")
+class CPPUnionObject(CPPObject):
+ def get_index_text(self, name):
+ # type: (unicode) -> unicode
+ return _('%s (C++ union)') % name
+
+ def parse_definition(self, parser):
+ # type: (Any) -> Any
+ return parser.parse_declaration("union")
+
+
class CPPEnumObject(CPPObject):
def get_index_text(self, name):
# type: (unicode) -> unicode
@@ -5944,6 +6043,12 @@ class CPPXRefRole(XRefRole):
def process_link(self, env, refnode, has_explicit_title, title, target):
# type: (BuildEnvironment, nodes.Node, bool, unicode, unicode) -> Tuple[unicode, unicode] # NOQA
refnode.attributes.update(env.ref_context)
+
+ if not has_explicit_title:
+ # major hax: replace anon names via simple string manipulation.
+ # Can this actually fail?
+ title = _anon_identifier_re.sub("[anonymous]", str(title))
+
if refnode['reftype'] == 'any':
# Assume the removal part of fix_parens for :any: refs.
# The addition part is done with the reference is resolved.
@@ -5965,6 +6070,16 @@ class CPPXRefRole(XRefRole):
class CPPExprRole(object):
+ def __init__(self, asCode):
+ if asCode:
+ # render the expression as inline code
+ self.class_type = 'cpp-expr'
+ self.node_type = nodes.literal
+ else:
+ # render the expression as inline text
+ self.class_type = 'cpp-texpr'
+ self.node_type = nodes.inline
+
def __call__(self, typ, rawtext, text, lineno, inliner, options={}, content=[]):
class Warner(object):
def warn(self, msg):
@@ -5972,18 +6087,23 @@ class CPPExprRole(object):
text = utils.unescape(text).replace('\n', ' ')
env = inliner.document.settings.env
parser = DefinitionParser(text, Warner(), env.config)
+ # attempt to mimic XRefRole classes, except that...
+ classes = ['xref', 'cpp', self.class_type]
try:
ast = parser.parse_expression()
except DefinitionError as ex:
Warner().warn('Unparseable C++ expression: %r\n%s'
% (text, text_type(ex.description)))
- return [nodes.literal(text)], []
+ # see below
+ return [self.node_type(text, text, classes=classes)], []
parentSymbol = env.temp_data.get('cpp:parent_symbol', None)
if parentSymbol is None:
parentSymbol = env.domaindata['cpp']['root_symbol']
- p = nodes.literal()
- ast.describe_signature(p, 'markType', env, parentSymbol)
- return [p], []
+ # ...most if not all of these classes should really apply to the individual references,
+ # not the container node
+ signode = self.node_type(classes=classes)
+ ast.describe_signature(signode, 'markType', env, parentSymbol)
+ return [signode], []
class CPPDomain(Domain):
@@ -5992,6 +6112,7 @@ class CPPDomain(Domain):
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'),
@@ -6002,6 +6123,7 @@ class CPPDomain(Domain):
directives = {
'class': CPPClassObject,
+ 'union': CPPUnionObject,
'function': CPPFunctionObject,
'member': CPPMemberObject,
'var': CPPMemberObject,
@@ -6018,6 +6140,7 @@ class CPPDomain(Domain):
roles = {
'any': CPPXRefRole(),
'class': CPPXRefRole(),
+ 'union': CPPXRefRole(),
'func': CPPXRefRole(fix_parens=True),
'member': CPPXRefRole(),
'var': CPPXRefRole(),
@@ -6025,7 +6148,8 @@ class CPPDomain(Domain):
'concept': CPPXRefRole(),
'enum': CPPXRefRole(),
'enumerator': CPPXRefRole(),
- 'expr': CPPExprRole()
+ 'expr': CPPExprRole(asCode=True),
+ 'texpr': CPPExprRole(asCode=False)
}
initial_data = {
'root_symbol': Symbol(None, None, None, None, None, None),
@@ -6117,7 +6241,7 @@ class CPPDomain(Domain):
templateDecls = []
s = parentSymbol.find_name(name, templateDecls, typ,
templateShorthand=True,
- matchSelf=True)
+ matchSelf=True, recurseInAnon=True)
if s is None or s.declaration is None:
txtName = text_type(name)
if txtName.startswith('std::') or txtName == 'std':
@@ -6147,31 +6271,37 @@ class CPPDomain(Domain):
declaration = s.declaration
fullNestedName = s.get_full_nested_name()
- name = text_type(fullNestedName).lstrip(':')
+ displayName = fullNestedName.get_display_string().lstrip(':')
docname = s.docname
assert docname
- # If it's operator(), we need to add '()' if explicit function parens
- # are requested. Then the Sphinx machinery will add another pair.
- # Also, if it's an 'any' ref that resolves to a function, we need to add
- # parens as well.
- addParen = 0
- if not node.get('refexplicit', False) and declaration.objectType == 'function':
- # this is just the normal haxing for 'any' roles
- 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
- contnode[-1].astext().endswith('operator()')):
- addParen += 1
- if ((typ == 'any' or typ == 'function') and
- contnode[-1].astext().endswith('operator') and
- name.endswith('operator()')):
- addParen += 1
- if addParen > 0:
+
+ # the non-identifier refs are cross-references, which should be processed:
+ # - fix parenthesis due to operator() and add_function_parentheses
+ if typ != "identifier":
title = contnode.pop(0).astext()
- contnode += nodes.Text(title + '()' * addParen)
+ # If it's operator(), we need to add '()' if explicit function parens
+ # are requested. Then the Sphinx machinery will add another pair.
+ # Also, if it's an 'any' ref that resolves to a function, we need to add
+ # parens as well.
+ addParen = 0
+ if not node.get('refexplicit', False) and declaration.objectType == 'function':
+ # this is just the normal haxing for 'any' roles
+ 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
+ title.endswith('operator()')):
+ addParen += 1
+ if ((typ == 'any' or typ == 'function') and
+ title.endswith('operator') and
+ displayName.endswith('operator()')):
+ addParen += 1
+ if addParen > 0:
+ title += '()' * addParen
+ # and reconstruct the title again
+ contnode += nodes.Text(title)
return make_refnode(builder, fromdocname, docname,
- declaration.get_newest_id(), contnode, name
+ declaration.get_newest_id(), contnode, displayName
), declaration.objectType
def resolve_xref(self, env, fromdocname, builder,
@@ -6200,11 +6330,13 @@ class CPPDomain(Domain):
if symbol.declaration is None:
continue
assert symbol.docname
- name = text_type(symbol.get_full_nested_name()).lstrip(':')
+ fullNestedName = symbol.get_full_nested_name()
+ name = text_type(fullNestedName).lstrip(':')
+ dispname = fullNestedName.get_display_string().lstrip(':')
objectType = symbol.declaration.objectType
docname = symbol.docname
newestId = symbol.declaration.get_newest_id()
- yield (name, name, objectType, docname, newestId, 1)
+ yield (name, dispname, objectType, docname, newestId, 1)
def get_full_qualified_name(self, node):
# type: (nodes.Node) -> unicode
@@ -6212,7 +6344,7 @@ class CPPDomain(Domain):
if target is None:
return None
parentKey = node.get("cpp:parent_key", None)
- if parentKey is None:
+ if parentKey is None or len(parentKey) <= 0:
return None
rootSymbol = self.data['root_symbol']
diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py
index bc6b4de6c..002820ebd 100644
--- a/sphinx/domains/std.py
+++ b/sphinx/domains/std.py
@@ -432,7 +432,7 @@ class ProductionList(SphinxDirective):
subnode = addnodes.production()
subnode['tokenname'] = name.strip()
if subnode['tokenname']:
- idname = 'grammar-token-%s' % subnode['tokenname']
+ idname = nodes.make_id('grammar-token-%s' % subnode['tokenname'])
if idname not in self.state.document.ids:
subnode['ids'].append(idname)
self.state.document.note_implicit_target(subnode, subnode)
diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py
index 69649829f..e9f3338aa 100644
--- a/sphinx/environment/__init__.py
+++ b/sphinx/environment/__init__.py
@@ -313,19 +313,6 @@ class BuildEnvironment(object):
"""Like :meth:`warn`, but with source information taken from *node*."""
self._warnfunc(msg, '%s:%s' % get_source_line(node), **kwargs)
- def need_refresh(self, app):
- # type: (Sphinx) -> Tuple[bool, unicode]
- """Check refresh environment is needed.
-
- If needed, this method returns the reason for refresh.
- """
- if self.version != app.registry.get_envversion(app):
- return True, __('build environment version not current')
- elif self.srcdir != app.srcdir:
- return True, __('source directory has changed')
- else:
- return False, None
-
def clear_doc(self, docname):
# type: (unicode) -> None
"""Remove all traces of a source file in the inventory."""
diff --git a/sphinx/environment/collectors/toctree.py b/sphinx/environment/collectors/toctree.py
index dcbee07f7..fe67bbcba 100644
--- a/sphinx/environment/collectors/toctree.py
+++ b/sphinx/environment/collectors/toctree.py
@@ -169,11 +169,12 @@ class TocTreeCollector(EnvironmentCollector):
elif isinstance(subnode, addnodes.compact_paragraph):
numstack[-1] += 1
if depth > 0:
- number = tuple(numstack)
+ number = list(numstack)
+ secnums[subnode[0]['anchorname']] = tuple(numstack)
else:
number = None
- secnums[subnode[0]['anchorname']] = \
- subnode[0]['secnumber'] = number
+ secnums[subnode[0]['anchorname']] = None
+ subnode[0]['secnumber'] = number
if titlenode:
titlenode['secnumber'] = number
titlenode = None
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index 353188aaf..62143fda4 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -643,11 +643,17 @@ class Documenter(object):
# should be skipped
if self.env.app:
# let extensions preprocess docstrings
- skip_user = self.env.app.emit_firstresult(
- 'autodoc-skip-member', self.objtype, membername, member,
- not keep, self.options)
- if skip_user is not None:
- keep = not skip_user
+ try:
+ skip_user = self.env.app.emit_firstresult(
+ 'autodoc-skip-member', self.objtype, membername, member,
+ not keep, self.options)
+ if skip_user is not None:
+ keep = not skip_user
+ except Exception as exc:
+ logger.warning(__('autodoc: failed to determine %r to be documented.'
+ 'the following exception was raised:\n%s'),
+ member, exc)
+ keep = False
if keep:
ret.append((membername, member, isattr))
diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py
index 789cfc1c1..bf6bfc03d 100644
--- a/sphinx/ext/autodoc/importer.py
+++ b/sphinx/ext/autodoc/importer.py
@@ -23,7 +23,7 @@ from sphinx.util.inspect import isenumclass, safe_getattr
if False:
# For type annotation
- from typing import Any, Callable, Dict, Generator, List, Optional, Tuple # NOQA
+ from typing import Any, Callable, Dict, Generator, Iterator, List, Optional, Tuple # NOQA
logger = logging.getLogger(__name__)
@@ -41,7 +41,7 @@ class _MockObject(object):
def __init__(self, *args, **kwargs):
# type: (Any, Any) -> None
- pass
+ self.__qualname__ = ''
def __len__(self):
# type: () -> int
@@ -52,8 +52,8 @@ class _MockObject(object):
return False
def __iter__(self):
- # type: () -> None
- pass
+ # type: () -> Iterator
+ return iter([])
def __mro_entries__(self, bases):
# type: (Tuple) -> Tuple
diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py
index 4c9175a5d..4156e0b2b 100644
--- a/sphinx/ext/autosummary/generate.py
+++ b/sphinx/ext/autosummary/generate.py
@@ -203,6 +203,8 @@ def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
get_members(obj, 'exception', imported=imported_members)
elif doc.objtype == 'class':
ns['members'] = dir(obj)
+ ns['inherited_members'] = \
+ set(dir(obj)) - set(obj.__dict__.keys())
ns['methods'], ns['all_methods'] = \
get_members(obj, 'method', ['__init__'])
ns['attributes'], ns['all_attributes'] = \
diff --git a/sphinx/ext/coverage.py b/sphinx/ext/coverage.py
index 351108faa..adbf2ddee 100644
--- a/sphinx/ext/coverage.py
+++ b/sphinx/ext/coverage.py
@@ -56,7 +56,7 @@ class CoverageBuilder(Builder):
"""
name = 'coverage'
epilog = __('Testing of coverage in the sources finished, look at the '
- 'results in %(outdir)s/python.txt.')
+ 'results in %(outdir)s' + path.sep + 'python.txt.')
def init(self):
# type: () -> None
diff --git a/sphinx/ext/jsmath.py b/sphinx/ext/jsmath.py
index 7381da42b..809ce0424 100644
--- a/sphinx/ext/jsmath.py
+++ b/sphinx/ext/jsmath.py
@@ -64,7 +64,8 @@ def builder_inited(app):
if not app.config.jsmath_path:
raise ExtensionError('jsmath_path config value must be set for the '
'jsmath extension to work')
- app.add_javascript(app.config.jsmath_path)
+ if app.builder.format == 'html':
+ app.builder.add_js_file(app.config.jsmath_path) # type: ignore
def setup(app):
diff --git a/sphinx/ext/mathjax.py b/sphinx/ext/mathjax.py
index 39fbb8539..0d73f3d3d 100644
--- a/sphinx/ext/mathjax.py
+++ b/sphinx/ext/mathjax.py
@@ -72,7 +72,11 @@ def builder_inited(app):
if not app.config.mathjax_path:
raise ExtensionError('mathjax_path config value must be set for the '
'mathjax extension to work')
- app.add_javascript(app.config.mathjax_path)
+ if app.builder.format == 'html':
+ options = {'async': 'async'}
+ if app.config.mathjax_options:
+ options.update(app.config.mathjax_options)
+ app.builder.add_js_file(app.config.mathjax_path, **options) # type: ignore
def setup(app):
@@ -86,7 +90,8 @@ def setup(app):
# https://docs.mathjax.org/en/latest/start.html#secure-access-to-the-cdn
app.add_config_value('mathjax_path',
'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?'
- 'config=TeX-AMS-MML_HTMLorMML', False)
+ 'config=TeX-AMS-MML_HTMLorMML', 'html')
+ app.add_config_value('mathjax_options', {}, 'html')
app.add_config_value('mathjax_inline', [r'\(', r'\)'], 'html')
app.add_config_value('mathjax_display', [r'\[', r'\]'], 'html')
app.connect('builder-inited', builder_inited)
diff --git a/sphinx/ext/napoleon/iterators.py b/sphinx/ext/napoleon/iterators.py
index b4bba8863..8926c865c 100644
--- a/sphinx/ext/napoleon/iterators.py
+++ b/sphinx/ext/napoleon/iterators.py
@@ -41,7 +41,7 @@ class peek_iter(object):
See Also
--------
`peek_iter` can operate as a drop in replacement for the built-in
- `iter `_ function.
+ `iter `_ function.
Attributes
----------
diff --git a/sphinx/io.py b/sphinx/io.py
index b9fa3f002..c90090e74 100644
--- a/sphinx/io.py
+++ b/sphinx/io.py
@@ -13,6 +13,7 @@ import re
from docutils.core import Publisher
from docutils.io import FileInput, NullOutput
+from docutils.parsers.rst import Parser as RSTParser
from docutils.readers import standalone
from docutils.statemachine import StringList, string2lines
from docutils.writers import UnfilteredWriter
@@ -304,6 +305,13 @@ def read_doc(app, env, filename):
source = input_class(app, env, source=None, source_path=filename,
encoding=env.config.source_encoding)
parser = app.registry.create_source_parser(app, filetype)
+ if parser.__class__.__name__ == 'CommonMarkParser' and parser.settings_spec == ():
+ # a workaround for recommonmark
+ # If recommonmark.AutoStrictify is enabled, the parser invokes reST parser
+ # internally. But recommonmark-0.4.0 does not provide settings_spec for reST
+ # parser. As a workaround, this copies settings_spec for RSTParser to the
+ # CommonMarkParser.
+ parser.settings_spec = RSTParser.settings_spec
pub = Publisher(reader=reader,
parser=parser,
diff --git a/sphinx/parsers.py b/sphinx/parsers.py
index db62e7154..5f15c4103 100644
--- a/sphinx/parsers.py
+++ b/sphinx/parsers.py
@@ -41,6 +41,9 @@ class Parser(docutils.parsers.Parser):
Emit a warning. (Same as :meth:`sphinx.application.Sphinx.warn()`)
self.info()
Emit a informational message. (Same as :meth:`sphinx.application.Sphinx.info()`)
+
+ .. deprecated:: 1.6
+ ``warn()`` and ``info()`` is deprecated. Use :mod:`sphinx.util.logging` instead.
"""
def set_application(self, app):
diff --git a/sphinx/pycode/parser.py b/sphinx/pycode/parser.py
index f943b7985..9d464a253 100644
--- a/sphinx/pycode/parser.py
+++ b/sphinx/pycode/parser.py
@@ -34,6 +34,11 @@ else:
ASSIGN_NODES = (ast.Assign)
+def filter_whitespace(code):
+ # type: (unicode) -> unicode
+ return code.replace('\f', ' ') # replace FF (form feed) with whitespace
+
+
def get_assign_targets(node):
# type: (ast.AST) -> List[ast.expr]
"""Get list of targets from Assign and AnnAssign node."""
@@ -225,12 +230,13 @@ class AfterCommentParser(TokenProcessor):
def parse(self):
# type: () -> None
"""Parse the code and obtain comment after assignment."""
- # skip lvalue (until '=' operator)
- while self.fetch_token() != [OP, '=']:
+ # skip lvalue (or whole of AnnAssign)
+ while not self.fetch_token().match([OP, '='], NEWLINE, COMMENT):
assert self.current
- # skip rvalue
- self.fetch_rvalue()
+ # skip rvalue (if exists)
+ if self.current == [OP, '=']:
+ self.fetch_rvalue()
if self.current == COMMENT:
self.comment = self.current.value
@@ -466,7 +472,7 @@ class Parser(object):
def __init__(self, code, encoding='utf-8'):
# type: (unicode, unicode) -> None
- self.code = code
+ self.code = filter_whitespace(code)
self.encoding = encoding
self.comments = {} # type: Dict[Tuple[unicode, unicode], unicode]
self.deforders = {} # type: Dict[unicode, int]
diff --git a/sphinx/registry.py b/sphinx/registry.py
index e11f0654d..f95a8fe7d 100644
--- a/sphinx/registry.py
+++ b/sphinx/registry.py
@@ -91,6 +91,9 @@ class SphinxComponentRegistry(object):
#: a dict of node class -> tuple of figtype and title_getter function
self.enumerable_nodes = {} # type: Dict[nodes.Node, Tuple[unicode, TitleGetter]]
+ #: js_files; list of JS paths or URLs
+ self.js_files = [] # type: List[Tuple[unicode, Dict[unicode, unicode]]]
+
#: LaTeX packages; list of package names and its options
self.latex_packages = [] # type: List[Tuple[unicode, unicode]]
@@ -418,6 +421,11 @@ class SphinxComponentRegistry(object):
def add_css_files(self, filename, **attributes):
self.css_files.append((filename, attributes))
+ def add_js_file(self, filename, **attributes):
+ # type: (unicode, **unicode) -> None
+ logger.debug('[app] adding js_file: %r, %r', filename, attributes)
+ self.js_files.append((filename, attributes)) # type: ignore
+
def add_latex_package(self, name, options):
# type: (unicode, unicode) -> None
logger.debug('[app] adding latex package: %r', name)
diff --git a/sphinx/templates/latex/longtable.tex_t b/sphinx/templates/latex/longtable.tex_t
index 5a151808c..98981d7a9 100644
--- a/sphinx/templates/latex/longtable.tex_t
+++ b/sphinx/templates/latex/longtable.tex_t
@@ -9,8 +9,12 @@
<%= table.get_colspec() %>
<%- if table.caption -%>
\caption{<%= ''.join(table.caption) %>\strut}<%= labels %>\\*[\sphinxlongtablecapskipadjust]
-<% endif -%>
\hline
+<% elif labels -%>
+\hline\noalign{\phantomsection<%= labels %>}%
+<% else -%>
+\hline
+<% endif -%>
<%= ''.join(table.header) %>
\endfirsthead
diff --git a/sphinx/templates/latex/tabular.tex_t b/sphinx/templates/latex/tabular.tex_t
index a35d2b2cb..cd4031e8c 100644
--- a/sphinx/templates/latex/tabular.tex_t
+++ b/sphinx/templates/latex/tabular.tex_t
@@ -14,6 +14,8 @@
\sphinxcapstartof{table}
\sphinxcaption{<%= ''.join(table.caption) %>}<%= labels %>
\sphinxaftercaption
+<% elif labels -%>
+\phantomsection<%= labels %>\nobreak
<% endif -%>
\begin{tabular}[t]<%= table.get_colspec() -%>
\hline
diff --git a/sphinx/templates/latex/tabulary.tex_t b/sphinx/templates/latex/tabulary.tex_t
index 331ef845d..bf31b7a58 100644
--- a/sphinx/templates/latex/tabulary.tex_t
+++ b/sphinx/templates/latex/tabulary.tex_t
@@ -14,6 +14,8 @@
\sphinxcapstartof{table}
\sphinxcaption{<%= ''.join(table.caption) %>}<%= labels %>
\sphinxaftercaption
+<% elif labels -%>
+\phantomsection<%= labels %>\nobreak
<% endif -%>
\begin{tabulary}{\linewidth}[t]<%= table.get_colspec() -%>
\hline
diff --git a/sphinx/testing/fixtures.py b/sphinx/testing/fixtures.py
index 94ac9a6e7..fcf1028fd 100644
--- a/sphinx/testing/fixtures.py
+++ b/sphinx/testing/fixtures.py
@@ -42,7 +42,10 @@ def app_params(request, test_params, shared_result, sphinx_test_tempdir, rootdir
# ##### process pytest.mark.sphinx
- markers = request.node.get_marker("sphinx")
+ if hasattr(request.node, 'iter_markers'): # pytest-3.6.0 or newer
+ markers = request.node.iter_markers("sphinx")
+ else:
+ markers = request.node.get_marker("sphinx")
pargs = {}
kwargs = {} # type: Dict[str, str]
@@ -89,7 +92,10 @@ def test_params(request):
have same 'shared_result' value.
**NOTE**: You can not specify shared_result and srcdir in same time.
"""
- env = request.node.get_marker('test_params')
+ if hasattr(request.node, 'get_closest_marker'): # pytest-3.6.0 or newer
+ env = request.node.get_closest_marker('test_params')
+ else:
+ env = request.node.get_marker('test_params')
kwargs = env.kwargs if env else {}
result = {
'shared_result': None,
diff --git a/sphinx/themes/basic/layout.html b/sphinx/themes/basic/layout.html
index 4795f7397..cd854a6b5 100644
--- a/sphinx/themes/basic/layout.html
+++ b/sphinx/themes/basic/layout.html
@@ -88,8 +88,8 @@
{%- macro script() %}
- {%- for scriptfile in script_files %}
-
+ {%- for js in script_files %}
+ {{ js_tag(js) }}
{%- endfor %}
{%- endmacro %}
@@ -130,6 +130,9 @@
{%- block scripts %}
{{- script() }}
{%- endblock %}
+ {%- if pageurl %}
+
+ {%- endif %}
{%- if use_opensearch %}
+{%- endblock %}
{% block extrahead %}
+{%- endblock %}
{# put the sidebar before the body #}
{% block sidebar1 %}{{ sidebar() }}{% endblock %}
diff --git a/sphinx/themes/classic/layout.html b/sphinx/themes/classic/layout.html
index 19f3d0279..8042e3f7e 100644
--- a/sphinx/themes/classic/layout.html
+++ b/sphinx/themes/classic/layout.html
@@ -10,5 +10,8 @@
{%- extends "basic/layout.html" %}
{% if theme_collapsiblesidebar|tobool %}
-{% set script_files = script_files + ['_static/sidebar.js'] %}
+{%- block scripts %}
+ {{ super() }}
+
+{%- endblock %}
{% endif %}
diff --git a/sphinx/themes/scrolls/layout.html b/sphinx/themes/scrolls/layout.html
index 7ed27edf7..7bb7b19e3 100644
--- a/sphinx/themes/scrolls/layout.html
+++ b/sphinx/themes/scrolls/layout.html
@@ -9,11 +9,14 @@
:license: BSD, see LICENSE for details.
#}
{%- extends "basic/layout.html" %}
-{% set script_files = script_files + ['_static/theme_extras.js'] %}
{%- block css %}
- {{ super() }}
+ {{ super() }}
{%- endblock %}
+{%- block scripts %}
+ {{ super() }}
+
+{%- endblock %}
{# do not display relbars #}
{% block relbar1 %}{% endblock %}
{% block relbar2 %}{% endblock %}
diff --git a/sphinx/transforms/post_transforms/compat.py b/sphinx/transforms/post_transforms/compat.py
index 94360f038..c67f7149f 100644
--- a/sphinx/transforms/post_transforms/compat.py
+++ b/sphinx/transforms/post_transforms/compat.py
@@ -71,8 +71,12 @@ class MathNodeMigrator(SphinxTransform):
warnings.warn("math node for Sphinx was replaced by docutils'. "
"Please use ``docutils.nodes.math_block`` instead.",
RemovedInSphinx30Warning)
- latex = node['latex']
- node += nodes.Text(latex, latex)
+ if isinstance(node, displaymath):
+ newnode = nodes.math_block('', node['latex'], **node.attributes)
+ node.replace_self(newnode)
+ else:
+ latex = node['latex']
+ node += nodes.Text(latex, latex)
def setup(app):
diff --git a/sphinx/util/console.py b/sphinx/util/console.py
index 7663feb1e..d62169adf 100644
--- a/sphinx/util/console.py
+++ b/sphinx/util/console.py
@@ -87,9 +87,21 @@ def coloron():
codes.update(_orig_codes)
-def colorize(name, text):
- # type: (str, unicode) -> unicode
- return codes.get(name, '') + text + codes.get('reset', '')
+def colorize(name, text, input_mode=False):
+ # type: (str, unicode, bool) -> unicode
+ def escseq(name):
+ # Wrap escape sequence with ``\1`` and ``\2`` to let readline know
+ # it is non-printable characters
+ # ref: https://tiswww.case.edu/php/chet/readline/readline.html
+ #
+ # Note: This hack does not work well in Windows (see #5059)
+ escape = codes.get(name, '')
+ if input_mode and escape and sys.platform != 'win32':
+ return '\1' + escape + '\2'
+ else:
+ return escape
+
+ return escseq(name) + text + escseq('reset')
def strip_colors(s):
diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py
index b6262d793..547d74c17 100644
--- a/sphinx/util/docutils.py
+++ b/sphinx/util/docutils.py
@@ -10,6 +10,7 @@
"""
from __future__ import absolute_import
+import codecs
import os
import re
import types
@@ -21,6 +22,7 @@ from os import path
import docutils
from docutils import nodes
+from docutils.io import FileOutput
from docutils.parsers.rst import Directive, directives, roles, convert_directive_function
from docutils.statemachine import StateMachine
from docutils.utils import Reporter
@@ -300,11 +302,33 @@ def switch_source_input(state, content):
state.memo.reporter.get_source_and_line = get_source_and_line
-class SphinxDirective(Directive):
- """A base class for Directives.
+class SphinxFileOutput(FileOutput):
+ """Better FileOutput class for Sphinx."""
- Compared with ``docutils.parsers.rst.Directive``, this class improves
- accessibility to Sphinx APIs.
+ def __init__(self, **kwargs):
+ # type: (Any) -> None
+ self.overwrite_if_changed = kwargs.pop('overwrite_if_changed', False)
+ FileOutput.__init__(self, **kwargs)
+
+ def write(self, data):
+ # type: (unicode) -> unicode
+ if (self.destination_path and self.autoclose and 'b' not in self.mode and
+ self.overwrite_if_changed and os.path.exists(self.destination_path)):
+ with codecs.open(self.destination_path, encoding=self.encoding) as f:
+ # skip writing: content not changed
+ if f.read() == data:
+ return data
+
+ return FileOutput.write(self, data)
+
+
+class SphinxDirective(Directive):
+ """A base class for Sphinx directives.
+
+ This class provides helper methods for Sphinx directives.
+
+ .. note:: The subclasses of this class might not work with docutils.
+ This class is strongly coupled with Sphinx.
"""
@property
diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py
index 7bda11503..5727d69b1 100644
--- a/sphinx/util/inspect.py
+++ b/sphinx/util/inspect.py
@@ -176,8 +176,8 @@ def isstaticmethod(obj, cls=None, name=None):
elif cls and name:
# trace __mro__ if the method is defined in parent class
#
- # .. note:: This only works with new style classes.
- for basecls in getattr(cls, '__mro__', []):
+ # .. note:: This only works well with new style classes.
+ for basecls in getattr(cls, '__mro__', [cls]):
meth = basecls.__dict__.get(name)
if meth:
if isinstance(meth, staticmethod):
diff --git a/sphinx/util/logging.py b/sphinx/util/logging.py
index 5034c007d..d2f6cafbe 100644
--- a/sphinx/util/logging.py
+++ b/sphinx/util/logging.py
@@ -392,7 +392,7 @@ class WarningIsErrorFilter(logging.Filter):
location = getattr(record, 'location', '')
try:
message = record.msg % record.args
- except TypeError:
+ except (TypeError, ValueError):
message = record.msg # use record.msg itself
if location:
diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py
index d3441565b..448dd2eaa 100644
--- a/sphinx/util/nodes.py
+++ b/sphinx/util/nodes.py
@@ -56,8 +56,7 @@ def repr_domxml(node, length=80):
returns full of DOM XML representation.
:return: DOM XML representation
"""
- # text = node.asdom().toxml() # #4919 crush if node has secnumber with tuple value
- text = text_type(node) # workaround for #4919
+ text = node.asdom().toxml()
if length and len(text) > length:
text = text[:length] + '...'
return text
@@ -82,9 +81,8 @@ def apply_source_workaround(node):
get_full_module_name(node), repr_domxml(node))
node.source, node.line = node.parent.source, node.parent.line
if isinstance(node, nodes.title) and node.source is None:
- # Uncomment these lines after merging into master(1.8)
- # logger.debug('[i18n] PATCH: %r to have source: %s',
- # get_full_module_name(node), repr_domxml(node))
+ logger.debug('[i18n] PATCH: %r to have source: %s',
+ get_full_module_name(node), repr_domxml(node))
node.source, node.line = node.parent.source, node.parent.line
if isinstance(node, nodes.term):
logger.debug('[i18n] PATCH: %r to have rawsource: %s',
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index 51f0607b2..a9ac854a2 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -19,13 +19,12 @@ from collections import defaultdict
from os import path
from docutils import nodes, writers
-from docutils.utils.roman import toRoman
from docutils.writers.latex2e import Babel
from six import itervalues, text_type
from sphinx import addnodes
from sphinx import highlighting
-from sphinx.builders.latex.nodes import footnotetext
+from sphinx.builders.latex.nodes import captioned_literal_block, footnotetext
from sphinx.deprecation import RemovedInSphinx30Warning
from sphinx.errors import SphinxError
from sphinx.locale import admonitionlabels, _, __
@@ -35,6 +34,12 @@ from sphinx.util.nodes import clean_astext
from sphinx.util.template import LaTeXRenderer
from sphinx.util.texescape import tex_escape_map, tex_replace_map
+try:
+ from docutils.utils.roman import toRoman
+except ImportError:
+ # In Debain/Ubuntu, roman package is provided as roman, not as docutils.utils.roman
+ from roman import toRoman
+
if False:
# For type annotation
from typing import Any, Callable, Dict, Iterator, List, Pattern, Tuple, Set, Union # NOQA
@@ -49,6 +54,12 @@ BEGIN_DOC = r'''
%(tableofcontents)s
'''
+SHORTHANDOFF = r'''
+\ifdefined\shorthandoff
+ \ifnum\catcode`\=\string=\active\shorthandoff{=}\fi
+ \ifnum\catcode`\"=\active\shorthandoff{"}\fi
+\fi
+'''
MAX_CITATION_LABEL_LENGTH = 8
LATEXSECTIONNAMES = ["part", "chapter", "section", "subsection",
@@ -58,6 +69,7 @@ HYPERLINK_SUPPORT_NODES = (
nodes.literal_block,
nodes.table,
nodes.section,
+ captioned_literal_block,
)
ENUMERATE_LIST_STYLE = defaultdict(lambda: r'\arabic',
{
@@ -206,16 +218,6 @@ class LaTeXWriter(writers.Writer):
class ExtBabel(Babel):
cyrillic_languages = ('bulgarian', 'kazakh', 'mongolian', 'russian', 'ukrainian')
- shorthands = {
- 'ngerman': '"',
- 'slovene': '"',
- 'portuges': '"',
- 'brazil': '"',
- 'spanish': '"',
- 'dutch': '"',
- 'polish': '"',
- 'italian': '"',
- }
def __init__(self, language_code, use_polyglossia=False):
# type: (unicode, bool) -> None
@@ -226,13 +228,9 @@ class ExtBabel(Babel):
def get_shorthandoff(self):
# type: () -> unicode
- shorthand = self.shorthands.get(self.language)
- if shorthand:
- return r'\ifnum\catcode`\%s=\active\shorthandoff{%s}\fi' % (shorthand, shorthand)
- elif self.language == 'turkish':
- # memo: if ever Sphinx starts supporting 'Latin', do as for Turkish
- return r'\ifnum\catcode`\=\string=\active\shorthandoff{=}\fi'
- return ''
+ warnings.warn('ExtBabel.get_shorthandoff() is deprecated.',
+ RemovedInSphinx30Warning)
+ return SHORTHANDOFF
def uses_cyrillic(self):
# type: () -> bool
@@ -473,7 +471,6 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.in_production_list = 0
self.in_footnote = 0
self.in_caption = 0
- self.in_container_literal_block = 0
self.in_term = 0
self.needs_linetrimming = 0
self.in_minipage = 0
@@ -590,7 +587,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
# this branch is not taken for xelatex/lualatex if default settings
self.elements['multilingual'] = self.elements['babel']
if builder.config.language:
- self.elements['shorthandoff'] = self.babel.get_shorthandoff()
+ self.elements['shorthandoff'] = SHORTHANDOFF
# Times fonts don't work with Cyrillic languages
if self.babel.uses_cyrillic() \
@@ -603,6 +600,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.elements['classoptions'] = ',dvipdfmx'
# disable babel which has not publishing quality in Japanese
self.elements['babel'] = ''
+ self.elements['shorthandoff'] = ''
self.elements['multilingual'] = ''
# disable fncychap in Japanese documents
self.elements['fncychap'] = ''
@@ -699,8 +697,6 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.pending_footnotes = [] # type: List[nodes.footnote_reference]
self.curfilestack = [] # type: List[unicode]
self.handled_abbrs = set() # type: Set[unicode]
- self.next_hyperlink_ids = {} # type: Dict[unicode, Set[unicode]]
- self.next_section_ids = set() # type: Set[unicode]
def pushbody(self, newbody):
# type: (List[unicode]) -> None
@@ -713,15 +709,6 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body = self.bodystack.pop()
return body
- def push_hyperlink_ids(self, figtype, ids):
- # type: (unicode, Set[unicode]) -> None
- hyperlink_ids = self.next_hyperlink_ids.setdefault(figtype, set())
- hyperlink_ids.update(ids)
-
- def pop_hyperlink_ids(self, figtype):
- # type: (unicode) -> Set[unicode]
- return self.next_hyperlink_ids.pop(figtype, set())
-
def check_latex_elements(self):
# type: () -> None
for key in self.builder.config.latex_elements:
@@ -939,8 +926,6 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_start_of_file(self, node):
# type: (nodes.Node) -> None
- # also add a document target
- self.next_section_ids.add(':doc')
self.curfilestack.append(node['docname'])
# use default highlight settings for new file
self.hlsettingstack.append(self.hlsettingstack[0])
@@ -1075,11 +1060,6 @@ class LaTeXTranslator(nodes.NodeVisitor):
# just use "subparagraph", it's not numbered anyway
self.body.append(r'\%s%s{' % (self.sectionnames[-1], short))
self.context.append('}\n' + self.hypertarget_to(node.parent))
-
- if self.next_section_ids:
- for id in self.next_section_ids:
- self.context[-1] += self.hypertarget(id, anchor=False)
- self.next_section_ids.clear()
elif isinstance(parent, nodes.topic):
self.body.append(r'\sphinxstyletopictitle{')
self.context.append('}\n')
@@ -1815,7 +1795,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_caption(self, node):
# type: (nodes.Node) -> None
self.in_caption += 1
- if self.in_container_literal_block:
+ if isinstance(node.parent, captioned_literal_block):
self.body.append('\\sphinxSetupCaptionForVerbatim{')
elif self.in_minipage and isinstance(node.parent, nodes.figure):
self.body.append('\\captionof{figure}{')
@@ -1908,35 +1888,13 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append(self.hypertarget(id, anchor=anchor))
# skip if visitor for next node supports hyperlink
+ domain = self.builder.env.get_domain('std')
next_node = node.next_node(ascend=True)
if isinstance(next_node, HYPERLINK_SUPPORT_NODES):
return
+ elif domain.get_enumerable_node_type(next_node) and domain.get_numfig_title(next_node):
+ return
- # postpone the labels until after the sectioning command
- parindex = node.parent.index(node)
- try:
- try:
- next = node.parent[parindex + 1]
- except IndexError:
- # last node in parent, look at next after parent
- # (for section of equal level) if it exists
- if node.parent.parent is not None:
- next = node.parent.parent[
- node.parent.parent.index(node.parent)]
- else:
- raise
- domain = self.builder.env.get_domain('std')
- figtype = domain.get_enumerable_node_type(next)
- if figtype and domain.get_numfig_title(next):
- ids = set()
- # labels for figures go in the figure body, not before
- if node.get('refid'):
- ids.add(node['refid'])
- ids.update(node['ids'])
- self.push_hyperlink_ids(figtype, ids)
- return
- except IndexError:
- pass
if 'refuri' in node:
return
if node.get('refid'):
@@ -1961,8 +1919,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
# type: (nodes.Node, Pattern) -> None
def escape(value):
value = self.encode(value)
- value = value.replace(r'\{', r'\sphinxleftcurlybrace')
- value = value.replace(r'\}', r'\sphinxrightcurlybrace')
+ value = value.replace(r'\{', r'{\sphinxleftcurlybrace}')
+ value = value.replace(r'\}', r'{\sphinxrightcurlybrace}')
return value
if not node.get('inline', True):
@@ -2246,6 +2204,14 @@ class LaTeXTranslator(nodes.NodeVisitor):
# the \ignorespaces in particular for after table header use
self.body.append('%\n\\end{footnotetext}\\ignorespaces ')
+ def visit_captioned_literal_block(self, node):
+ # type: (nodes.Node) -> None
+ pass
+
+ def depart_captioned_literal_block(self, node):
+ # type: (nodes.Node) -> None
+ pass
+
def visit_literal_block(self, node):
# type: (nodes.Node) -> None
if node.rawsource != node.astext():
@@ -2254,9 +2220,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append('\\begin{sphinxalltt}\n')
else:
labels = self.hypertarget_to(node)
- # LaTeX code will insert \phantomsection prior to \label
+ if isinstance(node.parent, captioned_literal_block):
+ labels += self.hypertarget_to(node.parent)
if labels and not self.in_footnote:
self.body.append('\n\\def\\sphinxLiteralBlockLabel{' + labels + '}')
+
code = node.astext()
lang = self.hlsettingstack[-1][0]
linenos = code.count('\n') >= self.hlsettingstack[-1][1] - 1
@@ -2483,22 +2451,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_container(self, node):
# type: (nodes.Node) -> None
- if node.get('literal_block'):
- self.in_container_literal_block += 1
- ids = '' # type: unicode
- for id in self.pop_hyperlink_ids('code-block'):
- ids += self.hypertarget(id, anchor=False)
- if node['ids']:
- # suppress with anchor=False \phantomsection insertion
- ids += self.hypertarget(node['ids'][0], anchor=False)
- # define label for use in caption.
- if ids:
- self.body.append('\n\\def\\sphinxLiteralBlockLabel{' + ids + '}\n')
+ pass
def depart_container(self, node):
# type: (nodes.Node) -> None
- if node.get('literal_block'):
- self.in_container_literal_block -= 1
+ pass
def visit_decoration(self, node):
# type: (nodes.Node) -> None
@@ -2628,6 +2585,39 @@ class LaTeXTranslator(nodes.NodeVisitor):
RemovedInSphinx30Warning)
return []
+ @property
+ def in_container_literal_block(self):
+ # type: () -> int
+ warnings.warn('LaTeXTranslator.in_container_literal_block is deprecated.',
+ RemovedInSphinx30Warning)
+ return 0
+
+ @property
+ def next_section_ids(self):
+ # type: () -> Set[unicode]
+ warnings.warn('LaTeXTranslator.next_section_ids is deprecated.',
+ RemovedInSphinx30Warning)
+ return set()
+
+ @property
+ def next_hyperlink_ids(self):
+ # type: () -> Dict
+ warnings.warn('LaTeXTranslator.next_hyperlink_ids is deprecated.',
+ RemovedInSphinx30Warning)
+ return {}
+
+ def push_hyperlink_ids(self, figtype, ids):
+ # type: (unicode, Set[unicode]) -> None
+ warnings.warn('LaTeXTranslator.push_hyperlink_ids() is deprecated.',
+ RemovedInSphinx30Warning)
+ pass
+
+ def pop_hyperlink_ids(self, figtype):
+ # type: (unicode) -> Set[unicode]
+ warnings.warn('LaTeXTranslator.pop_hyperlink_ids() is deprecated.',
+ RemovedInSphinx30Warning)
+ return set()
+
# Import old modules here for compatibility
# They should be imported after `LaTeXTranslator` to avoid recursive import.
diff --git a/tests/roots/test-domain-cpp/xref_consistency.rst b/tests/roots/test-domain-cpp/xref_consistency.rst
new file mode 100644
index 000000000..cb33000f7
--- /dev/null
+++ b/tests/roots/test-domain-cpp/xref_consistency.rst
@@ -0,0 +1,12 @@
+xref consistency
+----------------
+
+.. cpp:namespace:: xref_consistency
+
+.. cpp:class:: item
+
+code-role: :code:`item`
+any-role: :any:`item`
+cpp-any-role: :cpp:any:`item`
+cpp-expr-role: :cpp:expr:`item`
+cpp-texpr-role: :cpp:texpr:`item`
diff --git a/tests/roots/test-epub-anchor-id/conf.py b/tests/roots/test-epub-anchor-id/conf.py
new file mode 100644
index 000000000..3b73e0811
--- /dev/null
+++ b/tests/roots/test-epub-anchor-id/conf.py
@@ -0,0 +1,11 @@
+# -*- coding: utf-8 -*-
+
+master_doc = 'index'
+
+latex_documents = [
+ (master_doc, 'test.tex', 'The basic Sphinx documentation for testing', 'Sphinx', 'report')
+]
+
+
+def setup(app):
+ app.add_crossref_type(directivename="setting", rolename="setting")
diff --git a/tests/roots/test-epub-anchor-id/index.rst b/tests/roots/test-epub-anchor-id/index.rst
new file mode 100644
index 000000000..f3064615c
--- /dev/null
+++ b/tests/roots/test-epub-anchor-id/index.rst
@@ -0,0 +1,8 @@
+test-epub-anchor-id
+===================
+
+.. setting:: STATICFILES_FINDERS
+
+blah blah blah
+
+see :setting:`STATICFILES_FINDERS`
diff --git a/tests/roots/test-html_assets/conf.py b/tests/roots/test-html_assets/conf.py
index c61f0b42c..1a82bc9cc 100644
--- a/tests/roots/test-html_assets/conf.py
+++ b/tests/roots/test-html_assets/conf.py
@@ -8,4 +8,6 @@ html_static_path = ['static', 'subdir']
html_extra_path = ['extra', 'subdir']
html_css_files = ['css/style.css',
('https://example.com/custom.css', {'title': 'title', 'media': 'print'})]
+html_js_files = ['js/custom.js',
+ ('https://example.com/script.js', {'async': 'async'})]
exclude_patterns = ['**/_build', '**/.htpasswd']
diff --git a/tests/roots/test-latex-table/expects/longtable_having_widths.tex b/tests/roots/test-latex-table/expects/longtable_having_widths.tex
index d41a87586..505ae4d70 100644
--- a/tests/roots/test-latex-table/expects/longtable_having_widths.tex
+++ b/tests/roots/test-latex-table/expects/longtable_having_widths.tex
@@ -1,7 +1,7 @@
\label{\detokenize{longtable:longtable-having-widths-option}}
\begin{savenotes}\sphinxatlongtablestart\begin{longtable}{|\X{30}{100}|\X{70}{100}|}
-\hline
+\hline\noalign{\phantomsection\label{\detokenize{longtable:namedlongtable}}\label{\detokenize{longtable:mylongtable}}}%
\sphinxstyletheadfamily
header1
&\sphinxstyletheadfamily
@@ -43,3 +43,5 @@ cell3-2
\\
\hline
\end{longtable}\sphinxatlongtableend\end{savenotes}
+
+See {\hyperref[\detokenize{longtable:mylongtable}]{\sphinxcrossref{mylongtable}}}, same as {\hyperref[\detokenize{longtable:namedlongtable}]{\sphinxcrossref{\DUrole{std,std-ref}{this one}}}}.
diff --git a/tests/roots/test-latex-table/expects/table_having_widths.tex b/tests/roots/test-latex-table/expects/table_having_widths.tex
index 914793181..b4fcea04e 100644
--- a/tests/roots/test-latex-table/expects/table_having_widths.tex
+++ b/tests/roots/test-latex-table/expects/table_having_widths.tex
@@ -2,6 +2,7 @@
\begin{savenotes}\sphinxattablestart
\centering
+\phantomsection\label{\detokenize{tabular:namedtabular}}\label{\detokenize{tabular:mytabular}}\nobreak
\begin{tabular}[t]{|\X{30}{100}|\X{70}{100}|}
\hline
\sphinxstyletheadfamily
@@ -28,3 +29,5 @@ cell3-2
\end{tabular}
\par
\sphinxattableend\end{savenotes}
+
+See {\hyperref[\detokenize{tabular:mytabular}]{\sphinxcrossref{\DUrole{std,std-ref}{this}}}}, same as {\hyperref[\detokenize{tabular:namedtabular}]{\sphinxcrossref{namedtabular}}}.
diff --git a/tests/roots/test-latex-table/longtable.rst b/tests/roots/test-latex-table/longtable.rst
index 370226f21..bace9d4d2 100644
--- a/tests/roots/test-latex-table/longtable.rst
+++ b/tests/roots/test-latex-table/longtable.rst
@@ -18,9 +18,12 @@ longtable
longtable having :widths: option
--------------------------------
+.. _mylongtable:
+
.. table::
:class: longtable
:widths: 30,70
+ :name: namedlongtable
======= =======
header1 header2
@@ -30,6 +33,8 @@ longtable having :widths: option
cell3-1 cell3-2
======= =======
+See mylongtable_, same as :ref:`this one `.
+
longtable having :align: option
-------------------------------
diff --git a/tests/roots/test-latex-table/tabular.rst b/tests/roots/test-latex-table/tabular.rst
index b28add3d3..7f0909540 100644
--- a/tests/roots/test-latex-table/tabular.rst
+++ b/tests/roots/test-latex-table/tabular.rst
@@ -1,4 +1,4 @@
-taburar and taburary
+tabular and tabulary
====================
simple table
@@ -15,8 +15,11 @@ cell3-1 cell3-2
table having :widths: option
----------------------------
+.. _mytabular:
+
.. table::
:widths: 30,70
+ :name: namedtabular
======= =======
header1 header2
@@ -26,6 +29,8 @@ table having :widths: option
cell3-1 cell3-2
======= =======
+See :ref:`this `, same as namedtabular_.
+
table having :align: option (tabulary)
--------------------------------------
diff --git a/tests/roots/test-root/conf.py b/tests/roots/test-root/conf.py
index f96ea8821..d5029a776 100644
--- a/tests/roots/test-root/conf.py
+++ b/tests/roots/test-root/conf.py
@@ -105,6 +105,6 @@ def setup(app):
app.add_directive('clsdir', ClassDirective)
app.add_object_type('userdesc', 'userdescrole', '%s (userdesc)',
userdesc_parse, objname='user desc')
- app.add_javascript('file://moo.js')
+ app.add_js_file('file://moo.js')
app.add_source_suffix('.foo', 'foo')
app.add_source_parser(parsermod.Parser)
diff --git a/tests/test_build_epub.py b/tests/test_build_epub.py
index 24450089b..f8c1457fa 100644
--- a/tests/test_build_epub.py
+++ b/tests/test_build_epub.py
@@ -317,6 +317,15 @@ def test_epub_writing_mode(app):
assert 'writing-mode: vertical-rl;' in css
+@pytest.mark.sphinx('epub', testroot='epub-anchor-id')
+def test_epub_anchor_id(app):
+ app.build()
+
+ html = (app.outdir / 'index.xhtml').text()
+ assert 'blah blah blah
' in html
+ assert 'see ' in html
+
+
@pytest.mark.sphinx('epub', testroot='html_assets')
def test_epub_assets(app):
app.builder.build_all()
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index 2b45720d1..68017a80d 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -227,7 +227,7 @@ def test_html_warnings(app, warning):
"[@class='reference internal']/code/span[@class='pre']", 'HOME'),
(".//a[@href='#with']"
"[@class='reference internal']/code/span[@class='pre']", '^with$'),
- (".//a[@href='#grammar-token-try_stmt']"
+ (".//a[@href='#grammar-token-try-stmt']"
"[@class='reference internal']/code/span", '^statement$'),
(".//a[@href='#some-label'][@class='reference internal']/span", '^here$'),
(".//a[@href='#some-label'][@class='reference internal']/span", '^there$'),
@@ -259,7 +259,7 @@ def test_html_warnings(app, warning):
(".//dl/dt[@id='term-boson']", 'boson'),
# a production list
(".//pre/strong", 'try_stmt'),
- (".//pre/a[@href='#grammar-token-try1_stmt']/code/span", 'try1_stmt'),
+ (".//pre/a[@href='#grammar-token-try1-stmt']/code/span", 'try1_stmt'),
# tests for ``only`` directive
(".//p", 'A global substitution.'),
(".//p", 'In HTML.'),
@@ -1123,6 +1123,11 @@ def test_html_assets(app):
assert (' ' in content)
+ # html_js_files
+ assert '' in content
+ assert ('' in content)
+
@pytest.mark.sphinx('html', testroot='basic', confoverrides={'html_copy_source': False})
def test_html_copy_source(app):
@@ -1240,21 +1245,50 @@ def test_html_remote_images(app, status, warning):
@pytest.mark.sphinx('html', testroot='basic')
def test_html_sidebar(app, status, warning):
+ ctx = {}
+
+ # default for alabaster
app.builder.build_all()
result = (app.outdir / 'index.html').text(encoding='utf8')
- assert '' in result
+ assert ('