summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml6
-rw-r--r--CHANGES2
-rw-r--r--doc/extdev/appapi.rst2
-rw-r--r--doc/extdev/index.rst5
-rw-r--r--doc/usage/configuration.rst36
-rw-r--r--doc/usage/extensions/math.rst118
-rw-r--r--doc/usage/restructuredtext/directives.rst58
-rw-r--r--doc/usage/restructuredtext/roles.rst20
-rw-r--r--sphinx/application.py14
-rw-r--r--sphinx/builders/html.py41
-rw-r--r--sphinx/ext/imgmath.py11
-rw-r--r--sphinx/ext/jsmath.py12
-rw-r--r--sphinx/ext/mathbase.py17
-rw-r--r--sphinx/ext/mathjax.py12
-rw-r--r--sphinx/registry.py15
-rw-r--r--sphinx/setup_command.py2
-rw-r--r--sphinx/texinputs/LICRcyr2utf8.xdy (renamed from sphinx/texinputs/cyrLICRutf8.xdy)2
-rw-r--r--sphinx/texinputs/LICRlatin2utf8.xdy236
-rw-r--r--sphinx/texinputs/LatinRules.xdy607
-rw-r--r--sphinx/texinputs/Makefile_t13
-rw-r--r--sphinx/texinputs/sphinx.xdy72
-rw-r--r--sphinx/writers/html.py28
-rw-r--r--sphinx/writers/html5.py28
-rw-r--r--tests/roots/test-ext-viewcode-find/conf.py3
-rw-r--r--tests/roots/test-ext-viewcode-find/not_a_package/submodule.py1
-rw-r--r--tests/roots/test-inheritance/dummy/test.py2
-rw-r--r--tests/roots/test-latex-equations/conf.py1
-rw-r--r--tests/test_application.py1
-rw-r--r--tests/test_autodoc.py731
-rw-r--r--tests/test_build_gettext.py4
-rw-r--r--tests/test_build_html.py60
-rw-r--r--tests/test_config.py12
-rw-r--r--tests/test_domain_cpp.py12
-rw-r--r--tests/test_environment_toctree.py3
-rw-r--r--tests/test_ext_apidoc.py2
-rw-r--r--tests/test_ext_doctest.py1
-rw-r--r--tox.ini4
37 files changed, 1845 insertions, 349 deletions
diff --git a/.travis.yml b/.travis.yml
index 86d7559ca..40c2a639a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -24,8 +24,12 @@ matrix:
env:
- TOXENV=py36
- PYTEST_ADDOPTS="--cov ./ --cov-append --cov-config setup.cfg"
- - python: 'nightly'
+ - python: '3.7'
env: TOXENV=py37
+ dist: xenial
+ sudo: true
+ - python: 'nightly'
+ env: TOXENV=py38
- python: '3.6'
env: TOXENV=docs
- python: '3.6'
diff --git a/CHANGES b/CHANGES
index 1c07f7e18..009b96d6d 100644
--- a/CHANGES
+++ b/CHANGES
@@ -112,6 +112,7 @@ Deprecated
* ``sphinx.ext.mathbase.eqref`` node is deprecated
* ``sphinx.ext.mathbase.is_in_section_title()`` is deprecated
* ``sphinx.ext.mathbase.MathDomain`` is deprecated
+* ``sphinx.ext.mathbase.setup_math()`` is deprecated
* ``sphinx.highlighting.PygmentsBridge.unhighlight()`` is deprecated
* The ``trim_doctest_flags`` argument of ``sphinx.highlighting.PygmentsBridge``
is deprecated
@@ -176,6 +177,7 @@ Features added
* #4976: ``SphinxLoggerAdapter.info()`` now supports ``location`` parameter
* #5122: setuptools: support nitpicky option
* #2820: autoclass directive supports nested class
+* Add ``app.add_html_math_renderer()`` to register a math renderer for HTML
* Apply :confval:`trim_doctest_flags` to all builders (cf. text, manpages)
Bugs fixed
diff --git a/doc/extdev/appapi.rst b/doc/extdev/appapi.rst
index 9828f035f..ee612765c 100644
--- a/doc/extdev/appapi.rst
+++ b/doc/extdev/appapi.rst
@@ -93,6 +93,8 @@ package.
.. automethod:: Sphinx.add_html_theme(name, theme_path)
+.. automethod:: Sphinx.add_html_math_renderer(name, inline_renderers, block_renderers)
+
.. automethod:: Sphinx.add_message_catalog(catalog, locale_dir)
.. automethod:: Sphinx.is_parallel_allowed(typ)
diff --git a/doc/extdev/index.rst b/doc/extdev/index.rst
index a18534714..0002af853 100644
--- a/doc/extdev/index.rst
+++ b/doc/extdev/index.rst
@@ -147,6 +147,11 @@ The following is a list of deprecated interface.
- 3.0
- ``sphinx.domains.math.MathDomain``
+ * - ``sphinx.ext.mathbase.setup_math()``
+ - 1.8
+ - 3.0
+ - :meth:`~sphinx.application.Sphinx.add_html_math_renderer()`
+
* - ``sphinx.ext.mathbase.is_in_section_title()``
- 1.8
- 3.0
diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst
index dd6fcfaef..bc1e3ce13 100644
--- a/doc/usage/configuration.rst
+++ b/doc/usage/configuration.rst
@@ -773,6 +773,35 @@ documentation on :ref:`intl` for details.
Added ``{path}`` and ``{basename}`` tokens.
+.. _math-options:
+
+Options for Math
+----------------
+
+These options influence Math notations.
+
+.. confval:: math_number_all
+
+ Set this option to ``True`` if you want all displayed math to be numbered.
+ The default is ``False``.
+
+.. confval:: math_eqref_format
+
+ A string that are used for format of label of references to equations.
+ As a special character, ``{number}`` will be replaced to equaition number.
+
+ Example: ``'Eq.{number}'`` is rendered as ``Eq.10``
+
+.. confval:: math_numfig
+
+ If ``True``, displayed math equations are numbered across pages when
+ :confval:`numfig` is enabled. The :confval:`numfig_secnum_depth` setting
+ is respected. The :rst:role:`eq`, not :rst:role:`numref`, role
+ must be used to reference equation numbers. Default is ``True``.
+
+ .. versionadded:: 1.7
+
+
.. _html-options:
Options for HTML output
@@ -1290,6 +1319,13 @@ that use Sphinx's HTMLWriter class.
.. versionadded:: 1.3
+.. confval:: html_math_renderer
+
+ The name of math_renderer extension for HTML output. The default is
+ ``'mathjax'``.
+
+ .. versionadded:: 1.8
+
.. confval:: html_experimental_html5_writer
Output is processed with HTML5 writer. This feature needs docutils 0.13 or
diff --git a/doc/usage/extensions/math.rst b/doc/usage/extensions/math.rst
index c299b7bee..35f283846 100644
--- a/doc/usage/extensions/math.rst
+++ b/doc/usage/extensions/math.rst
@@ -2,123 +2,20 @@
.. _math-support:
-Math support in Sphinx
-======================
+Math support for HTML outputs in Sphinx
+=======================================
.. module:: sphinx.ext.mathbase
:synopsis: Common math support for imgmath and mathjax / jsmath.
.. versionadded:: 0.5
+.. versionchanged:: 1.8
-Since mathematical notation isn't natively supported by HTML in any way, Sphinx
-supports math in documentation with several extensions.
-
-The basic math support is contained in :mod:`sphinx.ext.mathbase`. Other math
-support extensions should, if possible, reuse that support too.
-
-.. note::
-
- :mod:`.mathbase` is not meant to be added to the :confval:`extensions` config
- value, instead, use either :mod:`sphinx.ext.imgmath` or
- :mod:`sphinx.ext.mathjax` as described below.
-
-The input language for mathematics is LaTeX markup. This is the de-facto
-standard for plain-text math notation and has the added advantage that no
-further translation is necessary when building LaTeX output.
-
-Keep in mind that when you put math markup in **Python docstrings** read by
-:mod:`autodoc <sphinx.ext.autodoc>`, you either have to double all backslashes,
-or use Python raw strings (``r"raw"``).
-
-:mod:`.mathbase` provides the following config values:
-
-.. confval:: math_number_all
-
- Set this option to ``True`` if you want all displayed math to be numbered.
- The default is ``False``.
-
-.. confval:: math_eqref_format
-
- A string that are used for format of label of references to equations.
- As a special character, ``{number}`` will be replaced to equaition number.
-
- Example: ``'Eq.{number}'`` is rendered as ``Eq.10``
-
-.. confval:: math_numfig
-
- If ``True``, displayed math equations are numbered across pages when
- :confval:`numfig` is enabled. The :confval:`numfig_secnum_depth` setting
- is respected. The :rst:role:`eq`, not :rst:role:`numref`, role
- must be used to reference equation numbers. Default is ``True``.
-
- .. versionadded:: 1.7
-
-:mod:`.mathbase` defines these new markup elements:
-
-.. rst:role:: math
-
- Role for inline math. Use like this::
-
- Since Pythagoras, we know that :math:`a^2 + b^2 = c^2`.
-
-.. rst:directive:: math
-
- Directive for displayed math (math that takes the whole line for itself).
-
- The directive supports multiple equations, which should be separated by a
- blank line::
-
- .. math::
-
- (a + b)^2 = a^2 + 2ab + b^2
-
- (a - b)^2 = a^2 - 2ab + b^2
-
- In addition, each single equation is set within a ``split`` environment,
- which means that you can have multiple aligned lines in an equation,
- aligned at ``&`` and separated by ``\\``::
-
- .. math::
-
- (a + b)^2 &= (a + b)(a + b) \\
- &= a^2 + 2ab + b^2
-
- For more details, look into the documentation of the `AmSMath LaTeX
- package`_.
-
- When the math is only one line of text, it can also be given as a directive
- argument::
-
- .. math:: (a + b)^2 = a^2 + 2ab + b^2
-
- Normally, equations are not numbered. If you want your equation to get a
- number, use the ``label`` option. When given, it selects an internal label
- for the equation, by which it can be cross-referenced, and causes an equation
- number to be issued. See :rst:role:`eq` for an example. The numbering
- style depends on the output format.
-
- There is also an option ``nowrap`` that prevents any wrapping of the given
- math in a math environment. When you give this option, you must make sure
- yourself that the math is properly set up. For example::
-
- .. math::
- :nowrap:
-
- \begin{eqnarray}
- y & = & ax^2 + bx + c \\
- f(x) & = & x^2 + 2xy + y^2
- \end{eqnarray}
-
-.. rst:role:: eq
-
- Role for cross-referencing equations via their label. Example::
-
- .. math:: e^{i\pi} + 1 = 0
- :label: euler
-
- Euler's identity, equation :eq:`euler`, was elected one of the most
- beautiful mathematical formulas.
+ Math support for non-HTML builders is integrated to sphinx-core.
+ So mathbase extension is no longer needed.
+Since mathematical notation isn't natively supported by HTML in any way, Sphinx
+gives a math support to HTML document with several extensions.
:mod:`sphinx.ext.imgmath` -- Render math as images
--------------------------------------------------
@@ -299,4 +196,3 @@ package jsMath_. It provides this config value:
.. _MathJax: https://www.mathjax.org/
.. _jsMath: http://www.math.union.edu/~dpvc/jsmath/
.. _preview-latex package: https://www.gnu.org/software/auctex/preview-latex.html
-.. _AmSMath LaTeX package: https://www.ams.org/publications/authors/tex/amslatex
diff --git a/doc/usage/restructuredtext/directives.rst b/doc/usage/restructuredtext/directives.rst
index f03e12c4d..5678c82f4 100644
--- a/doc/usage/restructuredtext/directives.rst
+++ b/doc/usage/restructuredtext/directives.rst
@@ -1005,9 +1005,63 @@ this reason, the following directive exists:
Math
----
-.. todo:: Move this in here.
+The input language for mathematics is LaTeX markup. This is the de-facto
+standard for plain-text math notation and has the added advantage that no
+further translation is necessary when building LaTeX output.
-See :ref:`math-support`.
+Keep in mind that when you put math markup in **Python docstrings** read by
+:mod:`autodoc <sphinx.ext.autodoc>`, you either have to double all backslashes,
+or use Python raw strings (``r"raw"``).
+
+.. rst:directive:: math
+
+ Directive for displayed math (math that takes the whole line for itself).
+
+ The directive supports multiple equations, which should be separated by a
+ blank line::
+
+ .. math::
+
+ (a + b)^2 = a^2 + 2ab + b^2
+
+ (a - b)^2 = a^2 - 2ab + b^2
+
+ In addition, each single equation is set within a ``split`` environment,
+ which means that you can have multiple aligned lines in an equation,
+ aligned at ``&`` and separated by ``\\``::
+
+ .. math::
+
+ (a + b)^2 &= (a + b)(a + b) \\
+ &= a^2 + 2ab + b^2
+
+ For more details, look into the documentation of the `AmSMath LaTeX
+ package`_.
+
+ When the math is only one line of text, it can also be given as a directive
+ argument::
+
+ .. math:: (a + b)^2 = a^2 + 2ab + b^2
+
+ Normally, equations are not numbered. If you want your equation to get a
+ number, use the ``label`` option. When given, it selects an internal label
+ for the equation, by which it can be cross-referenced, and causes an equation
+ number to be issued. See :rst:role:`eq` for an example. The numbering
+ style depends on the output format.
+
+ There is also an option ``nowrap`` that prevents any wrapping of the given
+ math in a math environment. When you give this option, you must make sure
+ yourself that the math is properly set up. For example::
+
+ .. math::
+ :nowrap:
+
+ \begin{eqnarray}
+ y & = & ax^2 + bx + c \\
+ f(x) & = & x^2 + 2xy + y^2
+ \end{eqnarray}
+
+.. _AmSMath LaTeX package: https://www.ams.org/publications/authors/tex/amslatex
Grammar production displays
diff --git a/doc/usage/restructuredtext/roles.rst b/doc/usage/restructuredtext/roles.rst
index 882647d07..6e819aa68 100644
--- a/doc/usage/restructuredtext/roles.rst
+++ b/doc/usage/restructuredtext/roles.rst
@@ -277,6 +277,26 @@ The following role creates a cross-reference to a term in a
during build.
+Math
+----
+
+.. rst:role:: math
+
+ Role for inline math. Use like this::
+
+ Since Pythagoras, we know that :math:`a^2 + b^2 = c^2`.
+
+.. rst:role:: eq
+
+ Role for cross-referencing equations via their label. Example::
+
+ .. math:: e^{i\pi} + 1 = 0
+ :label: euler
+
+ Euler's identity, equation :eq:`euler`, was elected one of the most
+ beautiful mathematical formulas.
+
+
Other semantic markup
---------------------
diff --git a/sphinx/application.py b/sphinx/application.py
index 0f887b2a5..5da89dd31 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -1221,6 +1221,20 @@ class Sphinx(object):
logger.debug('[app] adding HTML theme: %r, %r', name, theme_path)
self.html_themes[name] = theme_path
+ def add_html_math_renderer(self, name, inline_renderers=None, block_renderers=None):
+ # type: (unicode, Tuple[Callable, Callable], Tuple[Callable, Callable]) -> None
+ """Register a math renderer for HTML.
+
+ The *name* is a name of the math renderer. Both *inline_renderers* and
+ *block_renderes* are used as visitor functions for HTML writer.
+ *inline_renderers* is used for inline math node (``nodes.math`)). The
+ another is used for block math node (``nodes.math_block``). About
+ visitor functions, see :meth:`add_node` for more details.
+
+ .. versionadded:: 1.8
+ """
+ self.registry.add_html_math_renderer(name, inline_renderers, block_renderers)
+
def add_message_catalog(self, catalog, locale_dir):
# type: (unicode, unicode) -> None
"""Register a message catalog.
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index 67107ba0a..f08cb909f 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -36,7 +36,7 @@ from sphinx.deprecation import RemovedInSphinx20Warning, RemovedInSphinx30Warnin
from sphinx.environment.adapters.asset import ImageAdapter
from sphinx.environment.adapters.indexentries import IndexEntries
from sphinx.environment.adapters.toctree import TocTree
-from sphinx.errors import ThemeError
+from sphinx.errors import ConfigError, ThemeError
from sphinx.highlighting import PygmentsBridge
from sphinx.locale import _, __
from sphinx.search import js_index
@@ -437,6 +437,27 @@ class StandaloneHTMLBuilder(Builder):
else:
return HTMLTranslator
+ @property
+ def math_renderer_name(self):
+ # type: () -> unicode
+ name = self.get_builder_config('math_renderer', 'html')
+ if name is not None:
+ # use given name
+ return name
+ else:
+ # not given: choose a math_renderer from registered ones as possible
+ renderers = list(self.app.registry.html_inline_math_renderers)
+ if len(renderers) == 1:
+ # only default math_renderer (mathjax) is registered
+ return renderers[0]
+ elif len(renderers) == 2:
+ # default and another math_renderer are registered; prior the another
+ renderers.remove('mathjax')
+ return renderers[0]
+ else:
+ # many math_renderers are registered. can't choose automatically!
+ return None
+
def get_outdated_docs(self):
# type: () -> Iterator[unicode]
try:
@@ -1624,6 +1645,19 @@ def setup_js_tag_helper(app, pagename, templatexname, context, doctree):
context['js_tag'] = js_tag
+def validate_math_renderer(app):
+ # type: (Sphinx) -> None
+ if app.builder.format != 'html':
+ return
+
+ name = app.builder.math_renderer_name # type: ignore
+ if name is None:
+ raise ConfigError(__('Many math_renderers are registered. '
+ 'But no math_renderer is selected.'))
+ elif name not in app.registry.html_inline_math_renderers:
+ raise ConfigError(__('Unknown math_renderer %r is given.') % name)
+
+
def setup(app):
# type: (Sphinx) -> Dict[unicode, Any]
# builders
@@ -1673,12 +1707,17 @@ def setup(app):
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')
+ app.add_config_value('html_math_renderer', None, 'env')
# event handlers
app.connect('config-inited', convert_html_css_files)
app.connect('config-inited', convert_html_js_files)
+ app.connect('builder-inited', validate_math_renderer)
app.connect('html-page-context', setup_js_tag_helper)
+ # load default math renderer
+ app.setup_extension('sphinx.ext.mathjax')
+
return {
'version': 'builtin',
'parallel_read_safe': True,
diff --git a/sphinx/ext/imgmath.py b/sphinx/ext/imgmath.py
index 0cdc9aba4..eae577f2a 100644
--- a/sphinx/ext/imgmath.py
+++ b/sphinx/ext/imgmath.py
@@ -22,9 +22,9 @@ from docutils import nodes
from six import text_type
import sphinx
-from sphinx.errors import SphinxError, ExtensionError
+from sphinx.errors import SphinxError
from sphinx.ext.mathbase import get_node_equation_number
-from sphinx.ext.mathbase import setup_math as mathbase_setup, wrap_displaymath
+from sphinx.ext.mathbase import wrap_displaymath
from sphinx.locale import _, __
from sphinx.util import logging
from sphinx.util.osutil import ensuredir, ENOENT, cd
@@ -349,10 +349,9 @@ def html_visit_displaymath(self, node):
def setup(app):
# type: (Sphinx) -> Dict[unicode, Any]
- try:
- mathbase_setup(app, (html_visit_math, None), (html_visit_displaymath, None))
- except ExtensionError:
- raise ExtensionError('sphinx.ext.imgmath: other math package is already loaded')
+ app.add_html_math_renderer('imgmath',
+ (html_visit_math, None),
+ (html_visit_displaymath, None))
app.add_config_value('imgmath_image_format', 'png', 'html')
app.add_config_value('imgmath_dvipng', 'dvipng', 'html')
diff --git a/sphinx/ext/jsmath.py b/sphinx/ext/jsmath.py
index 809ce0424..5dbb20fba 100644
--- a/sphinx/ext/jsmath.py
+++ b/sphinx/ext/jsmath.py
@@ -15,7 +15,6 @@ from docutils import nodes
import sphinx
from sphinx.errors import ExtensionError
from sphinx.ext.mathbase import get_node_equation_number
-from sphinx.ext.mathbase import setup_math as mathbase_setup
from sphinx.locale import _
if False:
@@ -61,7 +60,9 @@ def html_visit_displaymath(self, node):
def builder_inited(app):
# type: (Sphinx) -> None
- if not app.config.jsmath_path:
+ if app.builder.format != 'html' or app.builder.math_renderer_name != 'jsmath': # type: ignore # NOQA
+ pass
+ elif not app.config.jsmath_path:
raise ExtensionError('jsmath_path config value must be set for the '
'jsmath extension to work')
if app.builder.format == 'html':
@@ -70,10 +71,9 @@ def builder_inited(app):
def setup(app):
# type: (Sphinx) -> Dict[unicode, Any]
- try:
- mathbase_setup(app, (html_visit_math, None), (html_visit_displaymath, None))
- except ExtensionError:
- raise ExtensionError('sphinx.ext.jsmath: other math package is already loaded')
+ app.add_html_math_renderer('jsmath',
+ (html_visit_math, None),
+ (html_visit_displaymath, None))
app.add_config_value('jsmath_path', '', False)
app.connect('builder-inited', builder_inited)
diff --git a/sphinx/ext/mathbase.py b/sphinx/ext/mathbase.py
index 9ad07a12b..77cf43c8b 100644
--- a/sphinx/ext/mathbase.py
+++ b/sphinx/ext/mathbase.py
@@ -13,7 +13,7 @@ import warnings
from docutils import nodes
-from sphinx.addnodes import math, math_block as displaymath
+from sphinx.addnodes import math, math_block as displaymath # NOQA # to keep compatibility
from sphinx.builders.latex.nodes import math_reference as eqref # NOQA # to keep compatibility
from sphinx.deprecation import RemovedInSphinx30Warning
from sphinx.domains.math import MathDomain # NOQA # to keep compatibility
@@ -44,7 +44,7 @@ def get_node_equation_number(writer, node):
return number
-def wrap_displaymath(math, label, numbering):
+def wrap_displaymath(text, label, numbering):
# type: (unicode, unicode, bool) -> unicode
def is_equation(part):
# type: (unicode) -> unicode
@@ -56,7 +56,7 @@ def wrap_displaymath(math, label, numbering):
labeldef = r'\label{%s}' % label
numbering = True
- parts = list(filter(is_equation, math.split('\n\n')))
+ parts = list(filter(is_equation, text.split('\n\n')))
equations = []
if len(parts) == 0:
return ''
@@ -97,8 +97,9 @@ def is_in_section_title(node):
def setup_math(app, htmlinlinevisitors, htmldisplayvisitors):
- # type: (Sphinx, Tuple[Callable, Any], Tuple[Callable, Any]) -> None
- app.add_node(math, override=True,
- html=htmlinlinevisitors)
- app.add_node(displaymath, override=True,
- html=htmldisplayvisitors)
+ # type: (Sphinx, Tuple[Callable, Callable], Tuple[Callable, Callable]) -> None
+ warnings.warn('setup_math() is deprecated. '
+ 'Please use app.add_html_math_renderer() instead.',
+ RemovedInSphinx30Warning)
+
+ app.add_html_math_renderer('unknown', htmlinlinevisitors, htmldisplayvisitors)
diff --git a/sphinx/ext/mathjax.py b/sphinx/ext/mathjax.py
index 0d73f3d3d..20d5866c5 100644
--- a/sphinx/ext/mathjax.py
+++ b/sphinx/ext/mathjax.py
@@ -16,7 +16,6 @@ from docutils import nodes
import sphinx
from sphinx.errors import ExtensionError
from sphinx.ext.mathbase import get_node_equation_number
-from sphinx.ext.mathbase import setup_math as mathbase_setup
from sphinx.locale import _
if False:
@@ -69,7 +68,9 @@ def html_visit_displaymath(self, node):
def builder_inited(app):
# type: (Sphinx) -> None
- if not app.config.mathjax_path:
+ if app.builder.format != 'html' or app.builder.math_renderer_name != 'mathjax': # type: ignore # NOQA
+ pass
+ elif not app.config.mathjax_path:
raise ExtensionError('mathjax_path config value must be set for the '
'mathjax extension to work')
if app.builder.format == 'html':
@@ -81,10 +82,9 @@ def builder_inited(app):
def setup(app):
# type: (Sphinx) -> Dict[unicode, Any]
- try:
- mathbase_setup(app, (html_visit_math, None), (html_visit_displaymath, None))
- except ExtensionError:
- raise ExtensionError('sphinx.ext.mathjax: other math package is already loaded')
+ app.add_html_math_renderer('mathjax',
+ (html_visit_math, None),
+ (html_visit_displaymath, None))
# more information for mathjax secure url is here:
# https://docs.mathjax.org/en/latest/start.html#secure-access-to-the-cdn
diff --git a/sphinx/registry.py b/sphinx/registry.py
index 2c73dbbd3..416edb2bc 100644
--- a/sphinx/registry.py
+++ b/sphinx/registry.py
@@ -92,6 +92,11 @@ 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]]
+ #: HTML inline and block math renderers
+ #: a dict of name -> tuple of visit function and depart function
+ self.html_inline_math_renderers = {} # type: Dict[unicode, Tuple[Callable, Callable]] # NOQA
+ self.html_block_math_renderers = {} # type: Dict[unicode, Tuple[Callable, Callable]] # NOQA
+
#: js_files; list of JS paths or URLs
self.js_files = [] # type: List[Tuple[unicode, Dict[unicode, unicode]]]
@@ -439,6 +444,16 @@ class SphinxComponentRegistry(object):
raise ExtensionError(__('enumerable_node %r already registered') % node)
self.enumerable_nodes[node] = (figtype, title_getter)
+ def add_html_math_renderer(self, name, inline_renderers, block_renderers):
+ # type: (unicode, Tuple[Callable, Callable], Tuple[Callable, Callable]) -> None
+ logger.debug('[app] adding html_math_renderer: %s, %r, %r',
+ name, inline_renderers, block_renderers)
+ if name in self.html_inline_math_renderers:
+ raise ExtensionError(__('math renderer %s is already registred') % name)
+
+ self.html_inline_math_renderers[name] = inline_renderers
+ self.html_block_math_renderers[name] = block_renderers
+
def load_extension(self, app, extname):
# type: (Sphinx, unicode) -> None
"""Load a Sphinx extension."""
diff --git a/sphinx/setup_command.py b/sphinx/setup_command.py
index ca3256c19..733bc4e34 100644
--- a/sphinx/setup_command.py
+++ b/sphinx/setup_command.py
@@ -21,7 +21,7 @@ from distutils.errors import DistutilsOptionError, DistutilsExecError
from six import StringIO, string_types
from sphinx.application import Sphinx
-from sphinx.cmdline import handle_exception
+from sphinx.cmd.build import handle_exception
from sphinx.util.console import nocolor, color_terminal
from sphinx.util.docutils import docutils_namespace, patch_docutils
from sphinx.util.osutil import abspath
diff --git a/sphinx/texinputs/cyrLICRutf8.xdy b/sphinx/texinputs/LICRcyr2utf8.xdy
index 8aabde9b3..a9ca1c82c 100644
--- a/sphinx/texinputs/cyrLICRutf8.xdy
+++ b/sphinx/texinputs/LICRcyr2utf8.xdy
@@ -1,6 +1,6 @@
;; -*- coding: utf-8; mode: Lisp; -*-
;; style file for xindy
-;; filename: cyrLICRutf8.xdy
+;; filename: LICRcyr2utf8.xdy
;; description: style file for xindy which maps back LaTeX Internal
;; Character Representation of Cyrillic to utf-8
;; usage: for use with pdflatex produced .idx files.
diff --git a/sphinx/texinputs/LICRlatin2utf8.xdy b/sphinx/texinputs/LICRlatin2utf8.xdy
new file mode 100644
index 000000000..60a24b421
--- /dev/null
+++ b/sphinx/texinputs/LICRlatin2utf8.xdy
@@ -0,0 +1,236 @@
+;; style file for xindy
+;; filename: LICRlatin2utf8.xdy
+;; description: style file for xindy which maps back LaTeX Internal
+;; Character Representation of letters (as arising in .idx index
+;; file) to UTF-8 encoding for correct sorting by xindy.
+;; usage: for use with the pdflatex engine,
+;; *not* for use with xelatex or lualatex.
+;;
+;; This is based upon xindy's distributed file tex/inputenc/utf8.xdy.
+;; The modifications include:
+;;
+;; - Updates for compatibility with current LaTeX macro encoding.
+;;
+;; - Systematic usage of the \IeC {...} mark-up, because mark-up in
+;; tex/inputenc/utf8.xdy was using it on seemingly random basis, and
+;; Sphinx coercing of xindy usability for both Latin and Cyrillic scripts
+;; with pdflatex requires its systematic presence here.
+;;
+;; - Support for some extra letters: Ÿ, Ŋ, ŋ, Œ, œ, IJ, ij, ȷ and ẞ.
+;;
+;; Indeed Sphinx needs to support for pdflatex engine all Unicode letters
+;; available in TeX T1 font encoding. The above letters are found in
+;; that encoding but not in the Latin1, 2, 3 charsets which are those
+;; covered by original tex/inputenc/utf8.xdy.
+;;
+;; - There is a problem that ȷ is not supported out-of-the box by LaTeX
+;; with inputenc, one must add explicitely
+;; \DeclareUnicodeCharacter{0237}{\j}
+;; to preamble of LaTeX document. However this character is not supported
+;; by the TeX "times" font used by default by Sphinx for pdflatex engine.
+;;
+;; - ẞ needs \DeclareUnicodeCharacter{1E9E}{\SS} (but ß needs no extra set-up).
+;;
+;; - U+02DB (˛) and U+02D9 (˙) are also not supported by inputenc
+;; out of the box and require
+;; \DeclareUnicodeCharacter{02DB}{\k{}}
+;; \DeclareUnicodeCharacter{02D9}{\.{}}
+;; to be added to preamble.
+;;
+;; - U+0127 ħ and U+0126 Ħ are absent from TeX T1+TS1 font encodings.
+;;
+;; - Characters Ŋ and ŋ are not supported by TeX font "times" used by
+;; default by Sphinx for pdflatex engine but they are supported by
+;; some TeX fonts, in particular by the default LaTeX font for T1
+;; encoding.
+;;
+;; - " and ~ must be escaped as ~" and resp. ~~ in xindy merge rules.
+;;
+;; Contributed by the Sphinx team, July 2018.
+;;
+;; See sphinx.xdy for superior figures, as they are escaped by LaTeX writer.
+(merge-rule "\IeC {\textonesuperior }" "¹" :string)
+(merge-rule "\IeC {\texttwosuperior }" "²" :string)
+(merge-rule "\IeC {\textthreesuperior }" "³" :string)
+(merge-rule "\IeC {\'a}" "á" :string)
+(merge-rule "\IeC {\'A}" "Á" :string)
+(merge-rule "\IeC {\`a}" "à" :string)
+(merge-rule "\IeC {\`A}" "À" :string)
+(merge-rule "\IeC {\^a}" "â" :string)
+(merge-rule "\IeC {\^A}" "Â" :string)
+(merge-rule "\IeC {\~"a}" "ä" :string)
+(merge-rule "\IeC {\~"A}" "Ä" :string)
+(merge-rule "\IeC {\~~a}" "ã" :string)
+(merge-rule "\IeC {\~~A}" "Ã" :string)
+(merge-rule "\IeC {\c c}" "ç" :string)
+(merge-rule "\IeC {\c C}" "Ç" :string)
+(merge-rule "\IeC {\'c}" "ć" :string)
+(merge-rule "\IeC {\'C}" "Ć" :string)
+(merge-rule "\IeC {\^c}" "ĉ" :string)
+(merge-rule "\IeC {\^C}" "Ĉ" :string)
+(merge-rule "\IeC {\.c}" "ċ" :string)
+(merge-rule "\IeC {\.C}" "Ċ" :string)
+(merge-rule "\IeC {\c s}" "ş" :string)
+(merge-rule "\IeC {\c S}" "Ş" :string)
+(merge-rule "\IeC {\c t}" "ţ" :string)
+(merge-rule "\IeC {\c T}" "Ţ" :string)
+(merge-rule "\IeC {\-}" "­" :string); soft hyphen
+(merge-rule "\IeC {\textdiv }" "÷" :string)
+(merge-rule "\IeC {\'e}" "é" :string)
+(merge-rule "\IeC {\'E}" "É" :string)
+(merge-rule "\IeC {\`e}" "è" :string)
+(merge-rule "\IeC {\`E}" "È" :string)
+(merge-rule "\IeC {\^e}" "ê" :string)
+(merge-rule "\IeC {\^E}" "Ê" :string)
+(merge-rule "\IeC {\~"e}" "ë" :string)
+(merge-rule "\IeC {\~"E}" "Ë" :string)
+(merge-rule "\IeC {\^g}" "ĝ" :string)
+(merge-rule "\IeC {\^G}" "Ĝ" :string)
+(merge-rule "\IeC {\.g}" "ġ" :string)
+(merge-rule "\IeC {\.G}" "Ġ" :string)
+(merge-rule "\IeC {\^h}" "ĥ" :string)
+(merge-rule "\IeC {\^H}" "Ĥ" :string)
+(merge-rule "\IeC {\H o}" "ő" :string)
+(merge-rule "\IeC {\H O}" "Ő" :string)
+(merge-rule "\IeC {\textacutedbl }" "˝" :string)
+(merge-rule "\IeC {\H u}" "ű" :string)
+(merge-rule "\IeC {\H U}" "Ű" :string)
+(merge-rule "\IeC {\ae }" "æ" :string)
+(merge-rule "\IeC {\AE }" "Æ" :string)
+(merge-rule "\IeC {\textcopyright }" "©" :string)
+(merge-rule "\IeC {\c \ }" "¸" :string)
+(merge-rule "\IeC {\dh }" "ð" :string)
+(merge-rule "\IeC {\DH }" "Ð" :string)
+(merge-rule "\IeC {\dj }" "đ" :string)
+(merge-rule "\IeC {\DJ }" "Đ" :string)
+(merge-rule "\IeC {\guillemotleft }" "«" :string)
+(merge-rule "\IeC {\guillemotright }" "»" :string)
+(merge-rule "\IeC {\'\i }" "í" :string)
+(merge-rule "\IeC {\`\i }" "ì" :string)
+(merge-rule "\IeC {\^\i }" "î" :string)
+(merge-rule "\IeC {\~"\i }" "ï" :string)
+(merge-rule "\IeC {\i }" "ı" :string)
+(merge-rule "\IeC {\^\j }" "ĵ" :string)
+(merge-rule "\IeC {\k {}}" "˛" :string)
+(merge-rule "\IeC {\l }" "ł" :string)
+(merge-rule "\IeC {\L }" "Ł" :string)
+(merge-rule "\IeC {\nobreakspace }" " " :string)
+(merge-rule "\IeC {\o }" "ø" :string)
+(merge-rule "\IeC {\O }" "Ø" :string)
+(merge-rule "\IeC {\textsterling }" "£" :string)
+(merge-rule "\IeC {\textparagraph }" "¶" :string)
+(merge-rule "\IeC {\ss }" "ß" :string)
+(merge-rule "\IeC {\textsection }" "§" :string)
+(merge-rule "\IeC {\textbrokenbar }" "¦" :string)
+(merge-rule "\IeC {\textcent }" "¢" :string)
+(merge-rule "\IeC {\textcurrency }" "¤" :string)
+(merge-rule "\IeC {\textdegree }" "°" :string)
+(merge-rule "\IeC {\textexclamdown }" "¡" :string)
+(merge-rule "\IeC {\texthbar }" "ħ" :string)
+(merge-rule "\IeC {\textHbar }" "Ħ" :string)
+(merge-rule "\IeC {\textonehalf }" "½" :string)
+(merge-rule "\IeC {\textonequarter }" "¼" :string)
+(merge-rule "\IeC {\textordfeminine }" "ª" :string)
+(merge-rule "\IeC {\textordmasculine }" "º" :string)
+(merge-rule "\IeC {\textperiodcentered }" "·" :string)
+(merge-rule "\IeC {\textquestiondown }" "¿" :string)
+(merge-rule "\IeC {\textregistered }" "®" :string)
+(merge-rule "\IeC {\textthreequarters }" "¾" :string)
+(merge-rule "\IeC {\textyen }" "¥" :string)
+(merge-rule "\IeC {\th }" "þ" :string)
+(merge-rule "\IeC {\TH }" "Þ" :string)
+(merge-rule "\IeC {\'I}" "Í" :string)
+(merge-rule "\IeC {\`I}" "Ì" :string)
+(merge-rule "\IeC {\^I}" "Î" :string)
+(merge-rule "\IeC {\~"I}" "Ï" :string)
+(merge-rule "\IeC {\.I}" "İ" :string)
+(merge-rule "\IeC {\^J}" "Ĵ" :string)
+(merge-rule "\IeC {\k a}" "ą" :string)
+(merge-rule "\IeC {\k A}" "Ą" :string)
+(merge-rule "\IeC {\k e}" "ę" :string)
+(merge-rule "\IeC {\k E}" "Ę" :string)
+(merge-rule "\IeC {\'l}" "ĺ" :string)
+(merge-rule "\IeC {\'L}" "Ĺ" :string)
+(merge-rule "\IeC {\textlnot }" "¬" :string)
+(merge-rule "\IeC {\textmu }" "µ" :string)
+(merge-rule "\IeC {\'n}" "ń" :string)
+(merge-rule "\IeC {\'N}" "Ń" :string)
+(merge-rule "\IeC {\~~n}" "ñ" :string)
+(merge-rule "\IeC {\~~N}" "Ñ" :string)
+(merge-rule "\IeC {\'o}" "ó" :string)
+(merge-rule "\IeC {\'O}" "Ó" :string)
+(merge-rule "\IeC {\`o}" "ò" :string)
+(merge-rule "\IeC {\`O}" "Ò" :string)
+(merge-rule "\IeC {\^o}" "ô" :string)
+(merge-rule "\IeC {\^O}" "Ô" :string)
+(merge-rule "\IeC {\~"o}" "ö" :string)
+(merge-rule "\IeC {\~"O}" "Ö" :string)
+(merge-rule "\IeC {\~~o}" "õ" :string)
+(merge-rule "\IeC {\~~O}" "Õ" :string)
+(merge-rule "\IeC {\textpm }" "±" :string)
+(merge-rule "\IeC {\r a}" "å" :string)
+(merge-rule "\IeC {\r A}" "Å" :string)
+(merge-rule "\IeC {\'r}" "ŕ" :string)
+(merge-rule "\IeC {\'R}" "Ŕ" :string)
+(merge-rule "\IeC {\r u}" "ů" :string)
+(merge-rule "\IeC {\r U}" "Ů" :string)
+(merge-rule "\IeC {\'s}" "ś" :string)
+(merge-rule "\IeC {\'S}" "Ś" :string)
+(merge-rule "\IeC {\^s}" "ŝ" :string)
+(merge-rule "\IeC {\^S}" "Ŝ" :string)
+(merge-rule "\IeC {\textasciidieresis }" "¨" :string)
+(merge-rule "\IeC {\textasciimacron }" "¯" :string)
+(merge-rule "\IeC {\.{}}" "˙" :string)
+(merge-rule "\IeC {\textasciiacute }" "´" :string)
+(merge-rule "\IeC {\texttimes }" "×" :string)
+(merge-rule "\IeC {\u a}" "ă" :string)
+(merge-rule "\IeC {\u A}" "Ă" :string)
+(merge-rule "\IeC {\u g}" "ğ" :string)
+(merge-rule "\IeC {\u G}" "Ğ" :string)
+(merge-rule "\IeC {\textasciibreve }" "˘" :string)
+(merge-rule "\IeC {\'u}" "ú" :string)
+(merge-rule "\IeC {\'U}" "Ú" :string)
+(merge-rule "\IeC {\`u}" "ù" :string)
+(merge-rule "\IeC {\`U}" "Ù" :string)
+(merge-rule "\IeC {\^u}" "û" :string)
+(merge-rule "\IeC {\^U}" "Û" :string)
+(merge-rule "\IeC {\~"u}" "ü" :string)
+(merge-rule "\IeC {\~"U}" "Ü" :string)
+(merge-rule "\IeC {\u u}" "ŭ" :string)
+(merge-rule "\IeC {\u U}" "Ŭ" :string)
+(merge-rule "\IeC {\v c}" "č" :string)
+(merge-rule "\IeC {\v C}" "Č" :string)
+(merge-rule "\IeC {\v d}" "ď" :string)
+(merge-rule "\IeC {\v D}" "Ď" :string)
+(merge-rule "\IeC {\v e}" "ě" :string)
+(merge-rule "\IeC {\v E}" "Ě" :string)
+(merge-rule "\IeC {\v l}" "ľ" :string)
+(merge-rule "\IeC {\v L}" "Ľ" :string)
+(merge-rule "\IeC {\v n}" "ň" :string)
+(merge-rule "\IeC {\v N}" "Ň" :string)
+(merge-rule "\IeC {\v r}" "ř" :string)
+(merge-rule "\IeC {\v R}" "Ř" :string)
+(merge-rule "\IeC {\v s}" "š" :string)
+(merge-rule "\IeC {\v S}" "Š" :string)
+(merge-rule "\IeC {\textasciicaron }" "ˇ" :string)
+(merge-rule "\IeC {\v t}" "ť" :string)
+(merge-rule "\IeC {\v T}" "Ť" :string)
+(merge-rule "\IeC {\v z}" "ž" :string)
+(merge-rule "\IeC {\v Z}" "Ž" :string)
+(merge-rule "\IeC {\'y}" "ý" :string)
+(merge-rule "\IeC {\'Y}" "Ý" :string)
+(merge-rule "\IeC {\~"y}" "ÿ" :string)
+(merge-rule "\IeC {\'z}" "ź" :string)
+(merge-rule "\IeC {\'Z}" "Ź" :string)
+(merge-rule "\IeC {\.z}" "ż" :string)
+(merge-rule "\IeC {\.Z}" "Ż" :string)
+;; letters not in Latin1, 2, 3 but available in TeX T1 font encoding
+(merge-rule "\IeC {\~"Y}" "Ÿ" :string)
+(merge-rule "\IeC {\NG }" "Ŋ" :string)
+(merge-rule "\IeC {\ng }" "ŋ" :string)
+(merge-rule "\IeC {\OE }" "Œ" :string)
+(merge-rule "\IeC {\oe }" "œ" :string)
+(merge-rule "\IeC {\IJ }" "IJ" :string)
+(merge-rule "\IeC {\ij }" "ij" :string)
+(merge-rule "\IeC {\j }" "ȷ" :string)
+(merge-rule "\IeC {\SS }" "ẞ" :string)
diff --git a/sphinx/texinputs/LatinRules.xdy b/sphinx/texinputs/LatinRules.xdy
new file mode 100644
index 000000000..99f14a2ee
--- /dev/null
+++ b/sphinx/texinputs/LatinRules.xdy
@@ -0,0 +1,607 @@
+;; style file for xindy
+;; filename: LatinRules.xdy
+;;
+;; It is based upon xindy's files lang/general/utf8.xdy and
+;; lang/general/utf8-lang.xdy which implement
+;; "a general sorting order for Western European languages"
+;;
+;; The aim for Sphinx is to be able to index in a Cyrillic document
+;; also terms using the Latin alphabets, inclusive of letters
+;; with diacritics. To this effect the xindy rules from lang/general
+;; got manually re-coded to avoid collisions with the encoding
+;; done by xindy for sorting words in Cyrillic languages, which was
+;; observed not to use bytes with octal encoding 0o266 or higher.
+;;
+;; So here we use only 0o266 or higher bytes.
+;; (Ŋ, ŋ, IJ, and ij are absent from
+;; lang/general/utf8.xdy and not included here)
+;; Contributed by the Sphinx team, 2018.
+
+(define-letter-group "A" :prefixes (""))
+(define-letter-group "B" :after "A" :prefixes (""))
+(define-letter-group "C" :after "B" :prefixes (""))
+(define-letter-group "D" :after "C" :prefixes (""))
+(define-letter-group "E" :after "D" :prefixes (""))
+(define-letter-group "F" :after "E" :prefixes (""))
+(define-letter-group "G" :after "F" :prefixes (""))
+(define-letter-group "H" :after "G" :prefixes (""))
+(define-letter-group "I" :after "H" :prefixes (""))
+(define-letter-group "J" :after "I" :prefixes (""))
+(define-letter-group "K" :after "J" :prefixes (""))
+(define-letter-group "L" :after "K" :prefixes (""))
+(define-letter-group "M" :after "L" :prefixes (""))
+(define-letter-group "N" :after "M" :prefixes (""))
+(define-letter-group "O" :after "N" :prefixes (""))
+(define-letter-group "P" :after "O" :prefixes (""))
+(define-letter-group "Q" :after "P" :prefixes (""))
+(define-letter-group "R" :after "Q" :prefixes (""))
+(define-letter-group "S" :after "R" :prefixes (""))
+(define-letter-group "T" :after "S" :prefixes (""))
+(define-letter-group "U" :after "T" :prefixes (""))
+(define-letter-group "V" :after "U" :prefixes (""))
+(define-letter-group "W" :after "V" :prefixes (""))
+(define-letter-group "X" :after "W" :prefixes (""))
+(define-letter-group "Y" :after "X" :prefixes (""))
+(define-letter-group "Z" :after "Y" :prefixes (""))
+
+(define-rule-set "sphinx-xy-alphabetize"
+
+ :rules (("À" "" :string)
+ ("Ă" "" :string)
+ ("â" "" :string)
+ ("Ä" "" :string)
+ ("à" "" :string)
+ ("Å" "" :string)
+ ("Ã" "" :string)
+ ("Á" "" :string)
+ ("á" "" :string)
+ ("ã" "" :string)
+ ("Â" "" :string)
+ ("ă" "" :string)
+ ("å" "" :string)
+ ("ą" "" :string)
+ ("ä" "" :string)
+ ("Ą" "" :string)
+ ("æ" "" :string)
+ ("Æ" "" :string)
+ ("ć" "" :string)
+ ("ĉ" "" :string)
+ ("ç" "" :string)
+ ("Č" "" :string)
+ ("č" "" :string)
+ ("Ĉ" "" :string)
+ ("Ç" "" :string)
+ ("Ć" "" :string)
+ ("ď" "" :string)
+ ("Đ" "" :string)
+ ("Ď" "" :string)
+ ("đ" "" :string)
+ ("ê" "" :string)
+ ("Ę" "" :string)
+ ("Ě" "" :string)
+ ("ë" "" :string)
+ ("ě" "" :string)
+ ("é" "" :string)
+ ("È" "" :string)
+ ("Ë" "" :string)
+ ("É" "" :string)
+ ("è" "" :string)
+ ("Ê" "" :string)
+ ("ę" "" :string)
+ ("ĝ" "" :string)
+ ("ğ" "" :string)
+ ("Ğ" "" :string)
+ ("Ĝ" "" :string)
+ ("ĥ" "" :string)
+ ("Ĥ" "" :string)
+ ("Ï" "" :string)
+ ("Í" "" :string)
+ ("ï" "" :string)
+ ("Î" "" :string)
+ ("î" "" :string)
+ ("ı" "" :string)
+ ("İ" "" :string)
+ ("í" "" :string)
+ ("Ì" "" :string)
+ ("ì" "" :string)
+ ("Ĵ" "" :string)
+ ("ĵ" "" :string)
+ ("ł" "" :string)
+ ("Ł" "" :string)
+ ("ľ" "" :string)
+ ("Ľ" "" :string)
+ ("ń" "" :string)
+ ("Ń" "" :string)
+ ("ñ" "" :string)
+ ("ň" "" :string)
+ ("Ñ" "" :string)
+ ("Ň" "" :string)
+ ("Õ" "" :string)
+ ("Ő" "" :string)
+ ("ó" "" :string)
+ ("ö" "" :string)
+ ("ô" "" :string)
+ ("ő" "" :string)
+ ("Ø" "" :string)
+ ("Ö" "" :string)
+ ("õ" "" :string)
+ ("Ô" "" :string)
+ ("ø" "" :string)
+ ("Ó" "" :string)
+ ("Ò" "" :string)
+ ("ò" "" :string)
+ ("œ" "ĺ" :string)
+ ("Œ" "ĺ" :string)
+ ("Ř" "" :string)
+ ("ř" "" :string)
+ ("Ŕ" "" :string)
+ ("ŕ" "" :string)
+ ("ŝ" "" :string)
+ ("Ś" "" :string)
+ ("ș" "" :string)
+ ("ş" "" :string)
+ ("Ŝ" "" :string)
+ ("ś" "" :string)
+ ("Ș" "" :string)
+ ("š" "" :string)
+ ("Ş" "" :string)
+ ("Š" "" :string)
+ ("ß" "" :string)
+ ("Ț" "" :string)
+ ("Ť" "" :string)
+ ("ț" "" :string)
+ ("ť" "" :string)
+ ("û" "" :string)
+ ("ŭ" "" :string)
+ ("ů" "" :string)
+ ("ű" "" :string)
+ ("ù" "" :string)
+ ("Ŭ" "" :string)
+ ("Ù" "" :string)
+ ("Ű" "" :string)
+ ("Ü" "" :string)
+ ("Ů" "" :string)
+ ("ú" "" :string)
+ ("Ú" "" :string)
+ ("Û" "" :string)
+ ("ü" "" :string)
+ ("ÿ" "" :string)
+ ("Ý" "" :string)
+ ("Ÿ" "" :string)
+ ("ý" "" :string)
+ ("Ż" "" :string)
+ ("Ž" "" :string)
+ ("Ź" "" :string)
+ ("ž" "" :string)
+ ("ż" "" :string)
+ ("ź" "" :string)
+ ("a" "" :string)
+ ("A" "" :string)
+ ("b" "" :string)
+ ("B" "" :string)
+ ("c" "" :string)
+ ("C" "" :string)
+ ("d" "" :string)
+ ("D" "" :string)
+ ("e" "" :string)
+ ("E" "" :string)
+ ("F" "" :string)
+ ("f" "" :string)
+ ("G" "" :string)
+ ("g" "" :string)
+ ("H" "" :string)
+ ("h" "" :string)
+ ("i" "" :string)
+ ("I" "" :string)
+ ("J" "" :string)
+ ("j" "" :string)
+ ("K" "" :string)
+ ("k" "" :string)
+ ("L" "" :string)
+ ("l" "" :string)
+ ("M" "" :string)
+ ("m" "" :string)
+ ("n" "" :string)
+ ("N" "" :string)
+ ("O" "" :string)
+ ("o" "" :string)
+ ("p" "" :string)
+ ("P" "" :string)
+ ("Q" "" :string)
+ ("q" "" :string)
+ ("r" "" :string)
+ ("R" "" :string)
+ ("S" "" :string)
+ ("s" "" :string)
+ ("t" "" :string)
+ ("T" "" :string)
+ ("u" "" :string)
+ ("U" "" :string)
+ ("v" "" :string)
+ ("V" "" :string)
+ ("W" "" :string)
+ ("w" "" :string)
+ ("x" "" :string)
+ ("X" "" :string)
+ ("Y" "" :string)
+ ("y" "" :string)
+ ("z" "" :string)
+ ("Z" "" :string)
+ ))
+
+(define-rule-set "sphinx-xy-resolve-diacritics"
+
+ :rules (("Ĥ" "" :string)
+ ("ó" "" :string)
+ ("ľ" "" :string)
+ ("Ř" "" :string)
+ ("ĝ" "" :string)
+ ("ď" "" :string)
+ ("Ě" "" :string)
+ ("ĥ" "" :string)
+ ("Č" "" :string)
+ ("Ĵ" "" :string)
+ ("ě" "" :string)
+ ("ž" "" :string)
+ ("Ď" "" :string)
+ ("ř" "" :string)
+ ("Ž" "" :string)
+ ("ı" "" :string)
+ ("Ť" "" :string)
+ ("á" "" :string)
+ ("č" "" :string)
+ ("Á" "" :string)
+ ("ň" "" :string)
+ ("Š" "" :string)
+ ("Ň" "" :string)
+ ("ĵ" "" :string)
+ ("ť" "" :string)
+ ("Ó" "" :string)
+ ("ý" "" :string)
+ ("Ĝ" "" :string)
+ ("Ú" "" :string)
+ ("Ľ" "" :string)
+ ("š" "" :string)
+ ("Ý" "" :string)
+ ("ú" "" :string)
+ ("Ś" "" :string)
+ ("ć" "" :string)
+ ("Ł" "" :string)
+ ("ł" "" :string)
+ ("ń" "" :string)
+ ("À" "" :string)
+ ("Ź" "" :string)
+ ("à" "" :string)
+ ("Ń" "" :string)
+ ("Đ" "" :string)
+ ("ÿ" "" :string)
+ ("ś" "" :string)
+ ("Ğ" "" :string)
+ ("ğ" "" :string)
+ ("Ù" "" :string)
+ ("İ" "" :string)
+ ("đ" "" :string)
+ ("ù" "" :string)
+ ("Ț" "" :string)
+ ("é" "" :string)
+ ("ŕ" "" :string)
+ ("Ć" "" :string)
+ ("ț" "" :string)
+ ("ò" "" :string)
+ ("ź" "" :string)
+ ("Ò" "" :string)
+ ("Ÿ" "" :string)
+ ("Ŕ" "" :string)
+ ("É" "" :string)
+ ("ĉ" "" :string)
+ ("ô" "" :string)
+ ("Í" "" :string)
+ ("ŝ" "" :string)
+ ("Ż" "" :string)
+ ("Ă" "" :string)
+ ("Ŝ" "" :string)
+ ("ñ" "" :string)
+ ("ŭ" "" :string)
+ ("í" "" :string)
+ ("È" "" :string)
+ ("Ô" "" :string)
+ ("Ŭ" "" :string)
+ ("ż" "" :string)
+ ("Ñ" "" :string)
+ ("è" "" :string)
+ ("Ĉ" "" :string)
+ ("ă" "" :string)
+ ("â" "" :string)
+ ("û" "" :string)
+ ("ê" "" :string)
+ ("Õ" "" :string)
+ ("õ" "" :string)
+ ("ș" "" :string)
+ ("ç" "" :string)
+ ("Â" "" :string)
+ ("Ê" "" :string)
+ ("Û" "" :string)
+ ("Ç" "" :string)
+ ("ì" "" :string)
+ ("Ì" "" :string)
+ ("Ș" "" :string)
+ ("ö" "" :string)
+ ("Ö" "" :string)
+ ("ş" "" :string)
+ ("ů" "" :string)
+ ("ë" "" :string)
+ ("ã" "" :string)
+ ("î" "" :string)
+ ("Î" "" :string)
+ ("Ã" "" :string)
+ ("Ş" "" :string)
+ ("Ů" "" :string)
+ ("Ë" "" :string)
+ ("ï" "" :string)
+ ("Ő" "" :string)
+ ("Ï" "" :string)
+ ("Ę" "" :string)
+ ("ő" "" :string)
+ ("Ü" "" :string)
+ ("Å" "" :string)
+ ("ü" "" :string)
+ ("ę" "" :string)
+ ("å" "" :string)
+ ("Ä" "" :string)
+ ("ű" "" :string)
+ ("Ø" "" :string)
+ ("ø" "" :string)
+ ("Ű" "" :string)
+ ("ä" "" :string)
+ ("Ą" "" :string)
+ ("ą" "" :string)
+ ("œ" "" :string)
+ ("ß" "" :string)
+ ("Æ" "" :string)
+ ("Œ" "" :string)
+ ("æ" "" :string)
+ ("e" "" :string)
+ ("t" "" :string)
+ ("L" "" :string)
+ ("Y" "" :string)
+ ("J" "" :string)
+ ("a" "" :string)
+ ("p" "" :string)
+ ("u" "" :string)
+ ("j" "" :string)
+ ("b" "" :string)
+ ("G" "" :string)
+ ("U" "" :string)
+ ("F" "" :string)
+ ("H" "" :string)
+ ("i" "" :string)
+ ("z" "" :string)
+ ("c" "" :string)
+ ("l" "" :string)
+ ("A" "" :string)
+ ("Q" "" :string)
+ ("w" "" :string)
+ ("D" "" :string)
+ ("R" "" :string)
+ ("d" "" :string)
+ ("s" "" :string)
+ ("r" "" :string)
+ ("k" "" :string)
+ ("v" "" :string)
+ ("m" "" :string)
+ ("P" "" :string)
+ ("y" "" :string)
+ ("K" "" :string)
+ ("q" "" :string)
+ ("S" "" :string)
+ ("I" "" :string)
+ ("C" "" :string)
+ ("M" "" :string)
+ ("Z" "" :string)
+ ("T" "" :string)
+ ("W" "" :string)
+ ("B" "" :string)
+ ("h" "" :string)
+ ("x" "" :string)
+ ("X" "" :string)
+ ("f" "" :string)
+ ("E" "" :string)
+ ("V" "" :string)
+ ("N" "" :string)
+ ("O" "" :string)
+ ("o" "" :string)
+ ("g" "" :string)
+ ("n" "" :string)
+ ))
+
+(define-rule-set "sphinx-xy-resolve-case"
+
+ :rules (("Ú" "8" :string)
+ ("Ÿ" "8" :string)
+ ("Ç" "8" :string)
+ ("Ĉ" "8" :string)
+ ("Ŕ" "8" :string)
+ ("Ľ" "8" :string)
+ ("Ů" "8" :string)
+ ("Ý" "8" :string)
+ ("É" "8" :string)
+ ("Ë" "8" :string)
+ ("Ș" "8" :string)
+ ("Ì" "8" :string)
+ ("Ê" "8" :string)
+ ("Ň" "8" :string)
+ ("Ą" "8" :string)
+ ("Š" "8" :string)
+ ("Û" "8" :string)
+ ("Ş" "8" :string)
+ ("Ć" "8" :string)
+ ("Ò" "8" :string)
+ ("Ĝ" "8" :string)
+ ("Ñ" "8" :string)
+ ("Ó" "8" :string)
+ ("Î" "8" :string)
+ ("Á" "8" :string)
+ ("Ã" "8" :string)
+ ("Ț" "8" :string)
+ ("Å" "8" :string)
+ ("Ğ" "8" :string)
+ ("Ü" "8" :string)
+ ("È" "8" :string)
+ ("Ô" "8" :string)
+ ("İ" "8" :string)
+ ("Ű" "8" :string)
+ ("Ù" "8" :string)
+ ("Ŭ" "8" :string)
+ ("Â" "8" :string)
+ ("Ť" "8" :string)
+ ("Ń" "8" :string)
+ ("Ď" "8" :string)
+ ("Ź" "8" :string)
+ ("Ž" "8" :string)
+ ("Đ" "8" :string)
+ ("Ŝ" "8" :string)
+ ("Č" "8" :string)
+ ("Ĵ" "8" :string)
+ ("Ö" "8" :string)
+ ("Ø" "8" :string)
+ ("Ż" "8" :string)
+ ("Ł" "8" :string)
+ ("Ă" "8" :string)
+ ("Ě" "8" :string)
+ ("Ő" "8" :string)
+ ("Õ" "8" :string)
+ ("Ę" "8" :string)
+ ("Ï" "8" :string)
+ ("À" "8" :string)
+ ("Ĥ" "8" :string)
+ ("Ä" "8" :string)
+ ("Ś" "8" :string)
+ ("Ř" "8" :string)
+ ("Í" "8" :string)
+ ("Œ" "89" :string)
+ ("Æ" "89" :string)
+ ("ì" "9" :string)
+ ("è" "9" :string)
+ ("ą" "9" :string)
+ ("š" "9" :string)
+ ("ú" "9" :string)
+ ("å" "9" :string)
+ ("ă" "9" :string)
+ ("ę" "9" :string)
+ ("ü" "9" :string)
+ ("ź" "9" :string)
+ ("ò" "9" :string)
+ ("ť" "9" :string)
+ ("ț" "9" :string)
+ ("ĵ" "9" :string)
+ ("ŕ" "9" :string)
+ ("ż" "9" :string)
+ ("ä" "9" :string)
+ ("ý" "9" :string)
+ ("ù" "9" :string)
+ ("á" "9" :string)
+ ("é" "9" :string)
+ ("č" "9" :string)
+ ("ň" "9" :string)
+ ("ś" "9" :string)
+ ("ø" "9" :string)
+ ("í" "9" :string)
+ ("đ" "9" :string)
+ ("ı" "9" :string)
+ ("ğ" "9" :string)
+ ("î" "9" :string)
+ ("ã" "9" :string)
+ ("à" "9" :string)
+ ("ř" "9" :string)
+ ("ő" "9" :string)
+ ("ů" "9" :string)
+ ("ș" "9" :string)
+ ("ÿ" "9" :string)
+ ("ë" "9" :string)
+ ("ŭ" "9" :string)
+ ("ç" "9" :string)
+ ("ű" "9" :string)
+ ("ñ" "9" :string)
+ ("õ" "9" :string)
+ ("ě" "9" :string)
+ ("ş" "9" :string)
+ ("ž" "9" :string)
+ ("ĝ" "9" :string)
+ ("ŝ" "9" :string)
+ ("ń" "9" :string)
+ ("û" "9" :string)
+ ("ł" "9" :string)
+ ("ď" "9" :string)
+ ("ĥ" "9" :string)
+ ("ê" "9" :string)
+ ("ô" "9" :string)
+ ("ĉ" "9" :string)
+ ("â" "9" :string)
+ ("ć" "9" :string)
+ ("ï" "9" :string)
+ ("ö" "9" :string)
+ ("ľ" "9" :string)
+ ("ó" "9" :string)
+ ("æ" "99" :string)
+ ("ß" "99" :string)
+ ("œ" "99" :string)
+ ("N" "8" :string)
+ ("V" "8" :string)
+ ("O" "8" :string)
+ ("X" "8" :string)
+ ("E" "8" :string)
+ ("P" "8" :string)
+ ("K" "8" :string)
+ ("T" "8" :string)
+ ("Z" "8" :string)
+ ("M" "8" :string)
+ ("C" "8" :string)
+ ("I" "8" :string)
+ ("S" "8" :string)
+ ("B" "8" :string)
+ ("W" "8" :string)
+ ("D" "8" :string)
+ ("R" "8" :string)
+ ("H" "8" :string)
+ ("F" "8" :string)
+ ("Q" "8" :string)
+ ("A" "8" :string)
+ ("G" "8" :string)
+ ("U" "8" :string)
+ ("J" "8" :string)
+ ("Y" "8" :string)
+ ("L" "8" :string)
+ ("o" "9" :string)
+ ("n" "9" :string)
+ ("g" "9" :string)
+ ("x" "9" :string)
+ ("f" "9" :string)
+ ("y" "9" :string)
+ ("q" "9" :string)
+ ("h" "9" :string)
+ ("w" "9" :string)
+ ("s" "9" :string)
+ ("d" "9" :string)
+ ("v" "9" :string)
+ ("k" "9" :string)
+ ("r" "9" :string)
+ ("m" "9" :string)
+ ("z" "9" :string)
+ ("c" "9" :string)
+ ("i" "9" :string)
+ ("l" "9" :string)
+ ("b" "9" :string)
+ ("j" "9" :string)
+ ("a" "9" :string)
+ ("p" "9" :string)
+ ("u" "9" :string)
+ ("t" "9" :string)
+ ("e" "9" :string)
+ ))
+
+(use-rule-set :run 0
+ :rule-set ("sphinx-xy-alphabetize"))
+(use-rule-set :run 1
+ :rule-set ("sphinx-xy-resolve-diacritics"))
+(use-rule-set :run 2
+ :rule-set ("sphinx-xy-resolve-case"))
diff --git a/sphinx/texinputs/Makefile_t b/sphinx/texinputs/Makefile_t
index 1ce4b1324..782d5ef9a 100644
--- a/sphinx/texinputs/Makefile_t
+++ b/sphinx/texinputs/Makefile_t
@@ -24,12 +24,17 @@ export LATEXOPTS =
LATEXMKOPTS =
{% if xindy_use -%}
export XINDYOPTS = {{ xindy_lang_option }} -M sphinx.xdy
-{% if latex_engine == 'pdflatex' and xindy_cyrillic -%}
-XINDYOPTS += -M cyrLICRutf8.xdy
+{% if latex_engine == 'pdflatex' -%}
+XINDYOPTS += -M LICRlatin2utf8.xdy
+{% if xindy_cyrillic -%}
+XINDYOPTS += -M LICRcyr2utf8.xdy
{% endif -%}
-{% if latex_engine == 'xelatex' or latex_engine == 'lualatex' -%}
-XINDYOPTS += -I xelatex
{% endif -%}
+{% if xindy_cyrillic -%}
+XINDYOPTS += -M LatinRules.xdy
+{% endif -%}
+# also with pdflatex as LICRlatin2utf8.xdy replaces xindy's /tex/inputenc/utf8.xdy
+XINDYOPTS += -I xelatex
{% endif -%}
# format: pdf or dvi (used only by archive targets)
FMT = pdf
diff --git a/sphinx/texinputs/sphinx.xdy b/sphinx/texinputs/sphinx.xdy
index 5f1fa3aaf..17b065600 100644
--- a/sphinx/texinputs/sphinx.xdy
+++ b/sphinx/texinputs/sphinx.xdy
@@ -26,7 +26,7 @@
;; man page says to use rather xelatex or lualatex in case of Cyrillic
;; scripts.
-;; Sphinx contributes cyrLICRutf8.xdy to provide support for Cyrillic
+;; Sphinx contributes LICRcyr2utf8.xdy to provide support for Cyrillic
;; scripts for the pdflatex engine.
;; Another issue caused by xindy ignoring all TeX macros except those
@@ -42,6 +42,14 @@
;; Rather it incorporates some suitable extracts from latex.xdy and
;; tex.xdy with additional Sphinx contributed rules.
+;; But, this means for pdflatex and Latin scripts that the xindy file
+;; tex/inputenc/uf8.xdy is not usable because it refers to the macro
+;; \IeC only sporadically, and as tex.xdy is not loaded, a rule such as
+;; (merge-rule "\'e" "é" :string)
+;; does not work, it must be
+;; (merge-rule "\IeC {\'e}" "é" :string)
+;; So Sphinx contributes LICRlatin2utf8.xdy to mitigate that problem.
+
;;;;;;;; extracts from tex.xdy (discarding most original comments):
;;;
@@ -84,12 +92,10 @@
;;;;;;;; end of extracts from latex.xdy
-;; Sphinx additions, cf sphinx.util.texescape for rationale
-;;
;; The LaTeX \index command turns \ into normal character so the TeX macros
;; written to .idx files are not followed by a blank. This is different
;; from non-ascii letters which end up (with pdflatex) as \IeC macros in .idx
-;; file, with a blank.
+;; file, with a blank space after \IeC
;; Details of the syntax are explained at
;; http://xindy.sourceforge.net/doc/manual-3.html
@@ -98,6 +104,8 @@
;; guess, for example "\\_" is not detected as RE but "\\P\{\}" is, so for
;; being sure we apply the :string switch everywhere and do not use \\ etc...
+;; Go back from sphinx.util.texescape TeX macros to UTF-8
+
(merge-rule "\sphinxleftcurlybrace{}" "{" :string)
(merge-rule "\sphinxrightcurlybrace{}" "}" :string)
(merge-rule "\_" "_" :string)
@@ -118,12 +126,66 @@
(merge-rule "\(\checkmark\)" "✓" :string)
(merge-rule "\textendash{}" "–" :string)
(merge-rule "\textbar{}" "|" :string)
+(merge-rule "\(\sp{\text{0}}\)" "⁰" :string)
+(merge-rule "\(\sp{\text{1}}\)" "¹" :string)
+(merge-rule "\(\sp{\text{2}}\)" "²" :string)
+(merge-rule "\(\sp{\text{3}}\)" "³" :string)
+(merge-rule "\(\sp{\text{4}}\)" "⁴" :string)
+(merge-rule "\(\sp{\text{5}}\)" "⁵" :string)
+(merge-rule "\(\sp{\text{6}}\)" "⁶" :string)
+(merge-rule "\(\sp{\text{7}}\)" "⁷" :string)
+(merge-rule "\(\sp{\text{8}}\)" "⁸" :string)
+(merge-rule "\(\sp{\text{9}}\)" "⁹" :string)
+(merge-rule "\(\sb{\text{0}}\)" "₀" :string)
+(merge-rule "\(\sb{\text{1}}\)" "₁" :string)
+(merge-rule "\(\sb{\text{2}}\)" "₂" :string)
+(merge-rule "\(\sb{\text{3}}\)" "₃" :string)
+(merge-rule "\(\sb{\text{4}}\)" "₄" :string)
+(merge-rule "\(\sb{\text{5}}\)" "₅" :string)
+(merge-rule "\(\sb{\text{6}}\)" "₆" :string)
+(merge-rule "\(\sb{\text{7}}\)" "₇" :string)
+(merge-rule "\(\sb{\text{8}}\)" "₈" :string)
+(merge-rule "\(\sb{\text{9}}\)" "₉" :string)
+(merge-rule "\(\alpha\)" "α" :string)
+(merge-rule "\(\beta\)" "β" :string)
+(merge-rule "\(\gamma\)" "γ" :string)
+(merge-rule "\(\delta\)" "δ" :string)
+(merge-rule "\(\epsilon\)" "ε" :string)
+(merge-rule "\(\zeta\)" "ζ" :string)
+(merge-rule "\(\eta\)" "η" :string)
+(merge-rule "\(\theta\)" "θ" :string)
+(merge-rule "\(\iota\)" "ι" :string)
+(merge-rule "\(\kappa\)" "κ" :string)
+(merge-rule "\(\lambda\)" "λ" :string)
+(merge-rule "\(\mu\)" "μ" :string)
+(merge-rule "\(\nu\)" "ν" :string)
+(merge-rule "\(\xi\)" "ξ" :string)
+(merge-rule "\(\pi\)" "π" :string)
+(merge-rule "\(\rho\)" "ρ" :string)
+(merge-rule "\(\sigma\)" "σ" :string)
+(merge-rule "\(\tau\)" "τ" :string)
+(merge-rule "\(\upsilon\)" "υ" :string)
+(merge-rule "\(\phi\)" "φ" :string)
+(merge-rule "\(\chi\)" "χ" :string)
+(merge-rule "\(\psi\)" "ψ" :string)
+(merge-rule "\(\omega\)" "ω" :string)
+(merge-rule "\(\Gamma\)" "Γ" :string)
+(merge-rule "\(\Delta\)" "Δ" :string)
+(merge-rule "\(\Theta\)" "Θ" :string)
+(merge-rule "\(\Lambda\)" "Λ" :string)
+(merge-rule "\(\Xi\)" "Ξ" :string)
+(merge-rule "\(\Pi\)" "Π" :string)
+(merge-rule "\(\Sigma\)" "Σ" :string)
+(merge-rule "\(\Upsilon\)" "Υ" :string)
+(merge-rule "\(\Phi\)" "Φ" :string)
+(merge-rule "\(\Psi\)" "Ψ" :string)
+(merge-rule "\(\Omega\)" "Ω" :string)
;; This xindy module provides some basic support for "see"
(require "makeindex.xdy")
;; This creates one-letter headings and works fine with utf-8 letters.
-;; For Cyrillic and pdflatex necessitates cyrLICRutf8.xdy to be loaded too.
+;; For Cyrillic with pdflatex works thanks to LICRcyr2utf8.xdy
(require "latin-lettergroups.xdy")
;; currently we don't (know how to easily) separate "Numbers" from
diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py
index 1a2da99d2..797b5828e 100644
--- a/sphinx/writers/html.py
+++ b/sphinx/writers/html.py
@@ -858,11 +858,29 @@ class HTMLTranslator(BaseTranslator):
def visit_math(self, node, math_env=''):
# type: (nodes.Node, unicode) -> None
- logger.warning(__('using "math" markup without a Sphinx math extension '
- 'active, please use one of the math extensions '
- 'described at http://sphinx-doc.org/en/master/ext/math.html'),
- location=(self.builder.current_docname, node.line))
- raise nodes.SkipNode
+ name = self.builder.math_renderer_name
+ visit, _ = self.builder.app.registry.html_inline_math_renderers[name]
+ visit(self, node)
+
+ def depart_math(self, node, math_env=''):
+ # type: (nodes.Node, unicode) -> None
+ name = self.builder.math_renderer_name
+ _, depart = self.builder.app.registry.html_inline_math_renderers[name]
+ if depart:
+ depart(self, node)
+
+ def visit_math_block(self, node, math_env=''):
+ # type: (nodes.Node, unicode) -> None
+ name = self.builder.math_renderer_name
+ visit, _ = self.builder.app.registry.html_block_math_renderers[name]
+ visit(self, node)
+
+ def depart_math_block(self, node, math_env=''):
+ # type: (nodes.Node, unicode) -> None
+ name = self.builder.math_renderer_name
+ _, depart = self.builder.app.registry.html_block_math_renderers[name]
+ if depart:
+ depart(self, node)
def unknown_visit(self, node):
# type: (nodes.Node) -> None
diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py
index de2591985..74dc827a1 100644
--- a/sphinx/writers/html5.py
+++ b/sphinx/writers/html5.py
@@ -809,11 +809,29 @@ class HTML5Translator(BaseTranslator):
def visit_math(self, node, math_env=''):
# type: (nodes.Node, unicode) -> None
- logger.warning(__('using "math" markup without a Sphinx math extension '
- 'active, please use one of the math extensions '
- 'described at http://sphinx-doc.org/en/master/ext/math.html'),
- location=(self.builder.current_docname, node.line))
- raise nodes.SkipNode
+ name = self.builder.math_renderer_name
+ visit, _ = self.builder.app.registry.html_inline_math_renderers[name]
+ visit(self, node)
+
+ def depart_math(self, node, math_env=''):
+ # type: (nodes.Node, unicode) -> None
+ name = self.builder.math_renderer_name
+ _, depart = self.builder.app.registry.html_inline_math_renderers[name]
+ if depart:
+ depart(self, node)
+
+ def visit_math_block(self, node, math_env=''):
+ # type: (nodes.Node, unicode) -> None
+ name = self.builder.math_renderer_name
+ visit, _ = self.builder.app.registry.html_block_math_renderers[name]
+ visit(self, node)
+
+ def depart_math_block(self, node, math_env=''):
+ # type: (nodes.Node, unicode) -> None
+ name = self.builder.math_renderer_name
+ _, depart = self.builder.app.registry.html_block_math_renderers[name]
+ if depart:
+ depart(self, node)
def unknown_visit(self, node):
# type: (nodes.Node) -> None
diff --git a/tests/roots/test-ext-viewcode-find/conf.py b/tests/roots/test-ext-viewcode-find/conf.py
index 3f5ddc175..98f9e5fbb 100644
--- a/tests/roots/test-ext-viewcode-find/conf.py
+++ b/tests/roots/test-ext-viewcode-find/conf.py
@@ -1,8 +1,5 @@
# -*- coding: utf-8 -*-
-import os
-import sys
-
extensions = ['sphinx.ext.viewcode']
master_doc = 'index'
exclude_patterns = ['_build']
diff --git a/tests/roots/test-ext-viewcode-find/not_a_package/submodule.py b/tests/roots/test-ext-viewcode-find/not_a_package/submodule.py
index fb697ab75..7bb36943b 100644
--- a/tests/roots/test-ext-viewcode-find/not_a_package/submodule.py
+++ b/tests/roots/test-ext-viewcode-find/not_a_package/submodule.py
@@ -3,6 +3,7 @@ submodule
"""
raise RuntimeError('This module should not get imported')
+
def decorator(f):
return f
diff --git a/tests/roots/test-inheritance/dummy/test.py b/tests/roots/test-inheritance/dummy/test.py
index 318a3ed42..51bd9a7a4 100644
--- a/tests/roots/test-inheritance/dummy/test.py
+++ b/tests/roots/test-inheritance/dummy/test.py
@@ -1,4 +1,4 @@
-"""
+r"""
Test with a class diagram like this::
diff --git a/tests/roots/test-latex-equations/conf.py b/tests/roots/test-latex-equations/conf.py
index 6122e9212..101e491e0 100644
--- a/tests/roots/test-latex-equations/conf.py
+++ b/tests/roots/test-latex-equations/conf.py
@@ -2,4 +2,3 @@
master_doc = 'equations'
extensions = ['sphinx.ext.imgmath']
-
diff --git a/tests/test_application.py b/tests/test_application.py
index efd55c487..27996fc91 100644
--- a/tests/test_application.py
+++ b/tests/test_application.py
@@ -12,7 +12,6 @@ import pytest
from docutils import nodes
from sphinx.errors import ExtensionError
-from sphinx.domains import Domain
from sphinx.testing.util import strip_escseq
from sphinx.util import logging
diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py
index a42210a4c..168002c52 100644
--- a/tests/test_autodoc.py
+++ b/tests/test_autodoc.py
@@ -10,6 +10,7 @@
:license: BSD, see LICENSE for details.
"""
+import re
import sys
from warnings import catch_warnings
@@ -20,12 +21,24 @@ from six import PY3
from sphinx.ext.autodoc import (
AutoDirective, ModuleLevelDocumenter, FunctionDocumenter, cut_lines, between, ALL
)
+from sphinx.ext.autodoc.directive import DocumenterBridge, process_documenter_options
from sphinx.testing.util import SphinxTestApp, Struct # NOQA
from sphinx.util import logging
+from sphinx.util.docutils import LoggingReporter
app = None
+def do_autodoc(app, objtype, name, options={}):
+ doccls = app.registry.documenters[objtype]
+ docoptions = process_documenter_options(doccls, app.config, options)
+ bridge = DocumenterBridge(app.env, LoggingReporter(''), docoptions, 1)
+ documenter = doccls(bridge, name)
+ documenter.generate()
+
+ return bridge.result
+
+
@pytest.fixture(scope='module', autouse=True)
def setup_module(rootdir, sphinx_test_tempdir):
try:
@@ -756,178 +769,549 @@ def test_generate():
assert_result_contains('.. py:class:: Class(arg)', 'module', 'target')
assert_result_contains('.. py:exception:: CustomEx', 'module', 'target')
- # test noindex flag
- options.members = []
- options.noindex = True
- assert_result_contains(' :noindex:', 'module', 'target')
- assert_result_contains(' :noindex:', 'class', 'Base')
-
- # okay, now let's get serious about mixing Python and C signature stuff
- assert_result_contains('.. py:class:: CustomDict', 'class', 'CustomDict',
- all_members=True)
-
- # test inner class handling
- assert_processes([('class', 'target.Outer'),
- ('class', 'target.Outer.Inner'),
- ('method', 'target.Outer.Inner.meth')],
- 'class', 'Outer', all_members=True)
- assert_processes([('class', 'target.Outer.Inner'),
- ('method', 'target.Outer.Inner.meth')],
- 'class', 'target.Outer.Inner', all_members=True)
-
- # test descriptor docstrings
- assert_result_contains(' Descriptor instance docstring.',
- 'attribute', 'target.Class.descr')
-
- # test generation for C modules (which have no source file)
- directive.env.ref_context['py:module'] = 'time'
- assert_processes([('function', 'time.asctime')], 'function', 'asctime')
- assert_processes([('function', 'time.asctime')], 'function', 'asctime')
-
- # test autodoc_member_order == 'source'
- directive.env.ref_context['py:module'] = 'target'
- options.private_members = True
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_autodoc_noindex(app):
+ options = {"noindex": True}
+ actual = do_autodoc(app, 'module', 'target', options)
+ assert list(actual) == [
+ '',
+ '.. py:module:: target',
+ ' :noindex:',
+ ''
+ ]
+
+ # TODO: :noindex: should be propagated to children of target item.
+
+ actual = do_autodoc(app, 'class', 'target.Base', options)
+ assert list(actual) == [
+ '',
+ '.. py:class:: Base',
+ ' :noindex:',
+ ' :module: target',
+ ''
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_autodoc_subclass_of_builtin_class(app):
+ options = {"members": None}
+ actual = do_autodoc(app, 'class', 'target.CustomDict', options)
+ assert list(actual) == [
+ '',
+ '.. py:class:: CustomDict',
+ ' :module: target',
+ '',
+ ' Docstring.',
+ ' '
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_autodoc_inner_class(app):
if PY3:
- roger_line = ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)'
+ builtins = ' alias of :class:`builtins.dict`'
else:
- roger_line = ' .. py:classmethod:: Class.roger(a, e=5, f=6)'
- assert_order(['.. py:class:: Class(arg)',
- ' .. py:attribute:: Class.descr',
- ' .. py:method:: Class.meth()',
- ' .. py:method:: Class.undocmeth()',
- ' .. py:attribute:: Class.attr',
- ' .. py:attribute:: Class.prop',
- ' .. py:attribute:: Class.docattr',
- ' .. py:attribute:: Class.udocattr',
- ' .. py:attribute:: Class.mdocattr',
- roger_line,
- ' .. py:classmethod:: Class.moore(a, e, f) -> happiness',
- ' .. py:attribute:: Class.inst_attr_comment',
- ' .. py:attribute:: Class.inst_attr_string',
- ' .. py:attribute:: Class._private_inst_attr',
- ' .. py:classmethod:: Class.inheritedclassmeth()',
- ' .. py:method:: Class.inheritedmeth()',
- ' .. py:staticmethod:: Class.inheritedstaticmeth(cls)',
- ],
- 'class', 'Class', member_order='bysource', all_members=True)
- del directive.env.ref_context['py:module']
+ builtins = ' alias of :class:`__builtin__.dict`'
- # test attribute initialized to class instance from other module
- directive.env.temp_data['autodoc:class'] = 'target.Class'
- assert_result_contains(u' should be documented as well - s\xfc\xdf',
- 'attribute', 'mdocattr')
- del directive.env.temp_data['autodoc:class']
+ options = {"members": None}
+ actual = do_autodoc(app, 'class', 'target.Outer', options)
+ assert list(actual) == [
+ '',
+ '.. py:class:: Outer',
+ ' :module: target',
+ '',
+ ' Foo',
+ ' ',
+ ' ',
+ ' .. py:class:: Outer.Inner',
+ ' :module: target',
+ ' ',
+ ' Foo',
+ ' ',
+ ' ',
+ ' .. py:method:: Outer.Inner.meth()',
+ ' :module: target',
+ ' ',
+ ' Foo',
+ ' ',
+ ' ',
+ ' .. py:attribute:: Outer.factory',
+ ' :module: target',
+ ' ',
+ builtins
+ ]
- # test autodoc_docstring_signature
- assert_result_contains(
- '.. py:method:: DocstringSig.meth(FOO, BAR=1) -> BAZ', 'method',
- 'target.DocstringSig.meth')
- assert_result_contains(
- ' rest of docstring', 'method', 'target.DocstringSig.meth')
- assert_result_contains(
- '.. py:method:: DocstringSig.meth2()', 'method',
- 'target.DocstringSig.meth2')
- assert_result_contains(
- ' indented line', 'method',
- 'target.DocstringSig.meth2')
- assert_result_contains(
- '.. py:classmethod:: Class.moore(a, e, f) -> happiness', 'method',
- 'target.Class.moore')
-
- # test new attribute documenter behavior
- directive.env.ref_context['py:module'] = 'target'
- options.undoc_members = True
- assert_processes([('class', 'target.AttCls'),
- ('attribute', 'target.AttCls.a1'),
- ('attribute', 'target.AttCls.a2'),
- ], 'class', 'AttCls')
- assert_result_contains(
- ' :annotation: = hello world', 'attribute', 'AttCls.a1')
- assert_result_contains(
- ' :annotation: = None', 'attribute', 'AttCls.a2')
-
- # test explicit members with instance attributes
- del directive.env.temp_data['autodoc:class']
- del directive.env.temp_data['autodoc:module']
- directive.env.ref_context['py:module'] = 'target'
- options.inherited_members = False
- options.undoc_members = False
- options.members = ALL
- assert_processes([
- ('class', 'target.InstAttCls'),
- ('attribute', 'target.InstAttCls.ca1'),
- ('attribute', 'target.InstAttCls.ca2'),
- ('attribute', 'target.InstAttCls.ca3'),
- ('attribute', 'target.InstAttCls.ia1'),
- ('attribute', 'target.InstAttCls.ia2'),
- ], 'class', 'InstAttCls')
- del directive.env.temp_data['autodoc:class']
- del directive.env.temp_data['autodoc:module']
- options.members = ['ca1', 'ia1']
- assert_processes([
- ('class', 'target.InstAttCls'),
- ('attribute', 'target.InstAttCls.ca1'),
- ('attribute', 'target.InstAttCls.ia1'),
- ], 'class', 'InstAttCls')
- del directive.env.temp_data['autodoc:class']
- del directive.env.temp_data['autodoc:module']
- del directive.env.ref_context['py:module']
+ actual = do_autodoc(app, 'class', 'target.Outer.Inner', options)
+ assert list(actual) == [
+ '',
+ '.. py:class:: Inner',
+ ' :module: target.Outer',
+ '',
+ ' Foo',
+ ' ',
+ ' ',
+ ' .. py:method:: Inner.meth()',
+ ' :module: target.Outer',
+ ' ',
+ ' Foo',
+ ' ',
+ ]
- # test members with enum attributes
- directive.env.ref_context['py:module'] = 'target'
- options.inherited_members = False
- options.undoc_members = True
- options.members = ALL
- assert_processes([
- ('class', 'target.EnumCls'),
- ('attribute', 'target.EnumCls.val1'),
- ('attribute', 'target.EnumCls.val2'),
- ('attribute', 'target.EnumCls.val3'),
- ('attribute', 'target.EnumCls.val4'),
- ], 'class', 'EnumCls')
- assert_result_contains(
- ' :annotation: = 12', 'attribute', 'EnumCls.val1')
- assert_result_contains(
- ' :annotation: = 23', 'attribute', 'EnumCls.val2')
- assert_result_contains(
- ' :annotation: = 34', 'attribute', 'EnumCls.val3')
- del directive.env.temp_data['autodoc:class']
- del directive.env.temp_data['autodoc:module']
- # test descriptor class documentation
- options.members = ['CustomDataDescriptor', 'CustomDataDescriptor2']
- assert_result_contains('.. py:class:: CustomDataDescriptor(doc)',
- 'module', 'target')
- assert_result_contains(' .. py:method:: CustomDataDescriptor.meth()',
- 'module', 'target')
- assert_result_contains('.. py:class:: CustomDataDescriptor2(doc)',
- 'module', 'target')
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_autodoc_descriptor(app):
+ actual = do_autodoc(app, 'attribute', 'target.Class.descr')
+ assert list(actual) == [
+ '',
+ '.. py:attribute:: Class.descr',
+ ' :module: target',
+ '',
+ ' Descriptor instance docstring.',
+ ' '
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_autodoc_c_module(app):
+ actual = do_autodoc(app, 'function', 'time.asctime')
+ assert list(actual) == [
+ '',
+ '.. py:function:: asctime([tuple]) -> string',
+ ' :module: time',
+ '',
+ " Convert a time tuple to a string, e.g. 'Sat Jun 06 16:26:11 1998'.",
+ ' When the time tuple is not present, current time as returned by localtime()',
+ ' is used.',
+ ' '
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_autodoc_member_order(app):
+ if PY3:
+ roger_method = ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)'
+ else:
+ roger_method = ' .. py:classmethod:: Class.roger(a, e=5, f=6)'
+
+ # case member-order='bysource'
+ options = {"members": None,
+ 'member-order': 'bysource',
+ "undoc-members": True,
+ 'private-members': True}
+ actual = do_autodoc(app, 'class', 'target.Class', options)
+ assert list(filter(lambda l: '::' in l, actual)) == [
+ '.. py:class:: Class(arg)',
+ ' .. py:attribute:: Class.descr',
+ ' .. py:method:: Class.meth()',
+ ' .. py:method:: Class.undocmeth()',
+ ' .. py:method:: Class.skipmeth()',
+ ' .. py:method:: Class.excludemeth()',
+ ' .. py:attribute:: Class.skipattr',
+ ' .. py:attribute:: Class.attr',
+ ' .. py:attribute:: Class.prop',
+ ' .. py:attribute:: Class.docattr',
+ ' .. py:attribute:: Class.udocattr',
+ ' .. py:attribute:: Class.mdocattr',
+ roger_method,
+ ' .. py:classmethod:: Class.moore(a, e, f) -> happiness',
+ ' .. py:attribute:: Class.inst_attr_inline',
+ ' .. py:attribute:: Class.inst_attr_comment',
+ ' .. py:attribute:: Class.inst_attr_string',
+ ' .. py:attribute:: Class._private_inst_attr'
+ ]
+
+ # case member-order='groupwise'
+ options = {"members": None,
+ 'member-order': 'groupwise',
+ "undoc-members": True,
+ 'private-members': True}
+ actual = do_autodoc(app, 'class', 'target.Class', options)
+ assert list(filter(lambda l: '::' in l, actual)) == [
+ '.. py:class:: Class(arg)',
+ ' .. py:method:: Class.excludemeth()',
+ ' .. py:method:: Class.meth()',
+ ' .. py:classmethod:: Class.moore(a, e, f) -> happiness',
+ roger_method,
+ ' .. py:method:: Class.skipmeth()',
+ ' .. py:method:: Class.undocmeth()',
+ ' .. py:attribute:: Class._private_inst_attr',
+ ' .. py:attribute:: Class.attr',
+ ' .. py:attribute:: Class.descr',
+ ' .. py:attribute:: Class.docattr',
+ ' .. py:attribute:: Class.inst_attr_comment',
+ ' .. py:attribute:: Class.inst_attr_inline',
+ ' .. py:attribute:: Class.inst_attr_string',
+ ' .. py:attribute:: Class.mdocattr',
+ ' .. py:attribute:: Class.prop',
+ ' .. py:attribute:: Class.skipattr',
+ ' .. py:attribute:: Class.udocattr'
+ ]
+
+ # case member-order=None
+ options = {"members": None,
+ "undoc-members": True,
+ 'private-members': True}
+ actual = do_autodoc(app, 'class', 'target.Class', options)
+ assert list(filter(lambda l: '::' in l, actual)) == [
+ '.. py:class:: Class(arg)',
+ ' .. py:attribute:: Class._private_inst_attr',
+ ' .. py:attribute:: Class.attr',
+ ' .. py:attribute:: Class.descr',
+ ' .. py:attribute:: Class.docattr',
+ ' .. py:method:: Class.excludemeth()',
+ ' .. py:attribute:: Class.inst_attr_comment',
+ ' .. py:attribute:: Class.inst_attr_inline',
+ ' .. py:attribute:: Class.inst_attr_string',
+ ' .. py:attribute:: Class.mdocattr',
+ ' .. py:method:: Class.meth()',
+ ' .. py:classmethod:: Class.moore(a, e, f) -> happiness',
+ ' .. py:attribute:: Class.prop',
+ roger_method,
+ ' .. py:attribute:: Class.skipattr',
+ ' .. py:method:: Class.skipmeth()',
+ ' .. py:attribute:: Class.udocattr',
+ ' .. py:method:: Class.undocmeth()'
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_autodoc_module_scope(app):
+ def convert(s):
+ return re.sub('<.*>', '<FILTERED>', s) # for py2/py3
+
+ app.env.temp_data['autodoc:module'] = 'target'
+ actual = do_autodoc(app, 'attribute', 'Class.mdocattr')
+ assert list(map(convert, actual)) == [
+ u'',
+ u'.. py:attribute:: Class.mdocattr',
+ u' :module: target',
+ u' :annotation: = <FILTERED>',
+ u'',
+ u' should be documented as well - süß',
+ u' '
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_autodoc_class_scope(app):
+ def convert(s):
+ return re.sub('<.*>', '<FILTERED>', s) # for py2/py3
+
+ app.env.temp_data['autodoc:module'] = 'target'
+ app.env.temp_data['autodoc:class'] = 'Class'
+ actual = do_autodoc(app, 'attribute', 'mdocattr')
+ assert list(map(convert, actual)) == [
+ u'',
+ u'.. py:attribute:: Class.mdocattr',
+ u' :module: target',
+ u' :annotation: = <FILTERED>',
+ u'',
+ u' should be documented as well - süß',
+ u' '
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_autodoc_docstring_signature(app):
+ options = {"members": None}
+ actual = do_autodoc(app, 'class', 'target.DocstringSig', options)
+ assert list(actual) == [
+ '',
+ '.. py:class:: DocstringSig',
+ ' :module: target',
+ '',
+ ' ',
+ ' .. py:method:: DocstringSig.meth(FOO, BAR=1) -> BAZ',
+ ' :module: target',
+ ' ',
+ ' First line of docstring',
+ ' ',
+ ' rest of docstring',
+ ' ',
+ ' ',
+ ' .. py:method:: DocstringSig.meth2()',
+ ' :module: target',
+ ' ',
+ ' First line, no signature',
+ ' Second line followed by indentation::',
+ ' ',
+ ' indented line',
+ ' ',
+ ' ',
+ ' .. py:attribute:: DocstringSig.prop1',
+ ' :module: target',
+ ' ',
+ ' First line of docstring',
+ ' ',
+ ' ',
+ ' .. py:attribute:: DocstringSig.prop2',
+ ' :module: target',
+ ' ',
+ ' First line of docstring',
+ ' Second line of docstring',
+ ' '
+ ]
+
+ # disable autodoc_docstring_signature
+ app.config.autodoc_docstring_signature = False
+ actual = do_autodoc(app, 'class', 'target.DocstringSig', options)
+ assert list(actual) == [
+ u'',
+ u'.. py:class:: DocstringSig',
+ u' :module: target',
+ u'',
+ u' ',
+ u' .. py:method:: DocstringSig.meth()',
+ u' :module: target',
+ u' ',
+ u' meth(FOO, BAR=1) -> BAZ',
+ u' First line of docstring',
+ u' ',
+ u' rest of docstring',
+ u' ',
+ u' ',
+ u' ',
+ u' .. py:method:: DocstringSig.meth2()',
+ u' :module: target',
+ u' ',
+ u' First line, no signature',
+ u' Second line followed by indentation::',
+ u' ',
+ u' indented line',
+ u' ',
+ u' ',
+ u' .. py:attribute:: DocstringSig.prop1',
+ u' :module: target',
+ u' ',
+ u' DocstringSig.prop1(self)',
+ u' First line of docstring',
+ u' ',
+ u' ',
+ u' .. py:attribute:: DocstringSig.prop2',
+ u' :module: target',
+ u' ',
+ u' First line of docstring',
+ u' Second line of docstring',
+ u' '
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_class_attributes(app):
+ options = {"members": None,
+ "undoc-members": True}
+ actual = do_autodoc(app, 'class', 'target.AttCls', options)
+ assert list(actual) == [
+ '',
+ '.. py:class:: AttCls',
+ ' :module: target',
+ '',
+ ' ',
+ ' .. py:attribute:: AttCls.a1',
+ ' :module: target',
+ ' :annotation: = hello world',
+ ' ',
+ ' ',
+ ' .. py:attribute:: AttCls.a2',
+ ' :module: target',
+ ' :annotation: = None',
+ ' '
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_instance_attributes(app):
+ options = {"members": None}
+ actual = do_autodoc(app, 'class', 'target.InstAttCls', options)
+ assert list(actual) == [
+ '',
+ '.. py:class:: InstAttCls()',
+ ' :module: target',
+ '',
+ ' Class with documented class and instance attributes.',
+ ' ',
+ ' ',
+ ' .. py:attribute:: InstAttCls.ca1',
+ ' :module: target',
+ " :annotation: = 'a'",
+ ' ',
+ ' Doc comment for class attribute InstAttCls.ca1.',
+ ' It can have multiple lines.',
+ ' ',
+ ' ',
+ ' .. py:attribute:: InstAttCls.ca2',
+ ' :module: target',
+ " :annotation: = 'b'",
+ ' ',
+ ' Doc comment for InstAttCls.ca2. One line only.',
+ ' ',
+ ' ',
+ ' .. py:attribute:: InstAttCls.ca3',
+ ' :module: target',
+ " :annotation: = 'c'",
+ ' ',
+ ' Docstring for class attribute InstAttCls.ca3.',
+ ' ',
+ ' ',
+ ' .. py:attribute:: InstAttCls.ia1',
+ ' :module: target',
+ ' :annotation: = None',
+ ' ',
+ ' Doc comment for instance attribute InstAttCls.ia1',
+ ' ',
+ ' ',
+ ' .. py:attribute:: InstAttCls.ia2',
+ ' :module: target',
+ ' :annotation: = None',
+ ' ',
+ ' Docstring for instance attribute InstAttCls.ia2.',
+ ' '
+ ]
- # test mocked module imports
- options.members = ['TestAutodoc']
- options.undoc_members = False
- assert_result_contains('.. py:class:: TestAutodoc',
- 'module', 'autodoc_missing_imports')
- assert_result_contains(' .. py:method:: TestAutodoc.decoratedMethod()',
- 'module', 'autodoc_missing_imports')
- options.members = ['decoratedFunction']
- assert_result_contains('.. py:function:: decoratedFunction()',
- 'module', 'autodoc_missing_imports')
+ # pick up arbitrary attributes
+ options = {"members": 'ca1,ia1'}
+ actual = do_autodoc(app, 'class', 'target.InstAttCls', options)
+ assert list(actual) == [
+ '',
+ '.. py:class:: InstAttCls()',
+ ' :module: target',
+ '',
+ ' Class with documented class and instance attributes.',
+ ' ',
+ ' ',
+ ' .. py:attribute:: InstAttCls.ca1',
+ ' :module: target',
+ " :annotation: = 'a'",
+ ' ',
+ ' Doc comment for class attribute InstAttCls.ca1.',
+ ' It can have multiple lines.',
+ ' ',
+ ' ',
+ ' .. py:attribute:: InstAttCls.ia1',
+ ' :module: target',
+ ' :annotation: = None',
+ ' ',
+ ' Doc comment for instance attribute InstAttCls.ia1',
+ ' '
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_enum_class(app):
+ options = {"members": None,
+ "undoc-members": True}
+ actual = do_autodoc(app, 'class', 'target.EnumCls', options)
+ assert list(actual) == [
+ '',
+ '.. py:class:: EnumCls',
+ ' :module: target',
+ '',
+ ' this is enum class',
+ ' ',
+ ' ',
+ ' .. py:attribute:: EnumCls.val1',
+ ' :module: target',
+ ' :annotation: = 12',
+ ' ',
+ ' doc for val1',
+ ' ',
+ ' ',
+ ' .. py:attribute:: EnumCls.val2',
+ ' :module: target',
+ ' :annotation: = 23',
+ ' ',
+ ' doc for val2',
+ ' ',
+ ' ',
+ ' .. py:attribute:: EnumCls.val3',
+ ' :module: target',
+ ' :annotation: = 34',
+ ' ',
+ ' doc for val3',
+ ' ',
+ ' ',
+ ' .. py:attribute:: EnumCls.val4',
+ ' :module: target',
+ ' :annotation: = 34',
+ ' '
+ ]
+
+ # checks for an attribute of EnumClass
+ actual = do_autodoc(app, 'attribute', 'target.EnumCls.val1')
+ assert list(actual) == [
+ '',
+ '.. py:attribute:: EnumCls.val1',
+ ' :module: target',
+ ' :annotation: = 12',
+ '',
+ ' doc for val1',
+ ' '
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_descriptor_class(app):
+ options = {"members": 'CustomDataDescriptor,CustomDataDescriptor2'}
+ actual = do_autodoc(app, 'module', 'target', options)
+ assert list(actual) == [
+ '',
+ '.. py:module:: target',
+ '',
+ '',
+ '.. py:class:: CustomDataDescriptor(doc)',
+ ' :module: target',
+ '',
+ ' Descriptor class docstring.',
+ ' ',
+ ' ',
+ ' .. py:method:: CustomDataDescriptor.meth()',
+ ' :module: target',
+ ' ',
+ ' Function.',
+ ' ',
+ '',
+ '.. py:class:: CustomDataDescriptor2(doc)',
+ ' :module: target',
+ '',
+ ' Descriptor class with custom metaclass docstring.',
+ ' '
+ ]
+
+
+@pytest.mark.sphinx('html', testroot='root')
+def test_mocked_module_imports(app):
+ options = {"members": 'TestAutodoc,decoratedFunction'}
+ actual = do_autodoc(app, 'module', 'autodoc_missing_imports', options)
+ assert list(actual) == [
+ '',
+ '.. py:module:: autodoc_missing_imports',
+ '',
+ '',
+ '.. py:class:: TestAutodoc',
+ ' :module: autodoc_missing_imports',
+ '',
+ ' TestAutodoc docstring.',
+ ' ',
+ ' ',
+ ' .. py:method:: TestAutodoc.decoratedMethod()',
+ ' :module: autodoc_missing_imports',
+ ' ',
+ ' TestAutodoc::decoratedMethod docstring',
+ ' ',
+ '',
+ '.. py:function:: decoratedFunction()',
+ ' :module: autodoc_missing_imports',
+ '',
+ ' decoratedFunction docstring',
+ ' '
+ ]
@pytest.mark.skipif(sys.version_info < (3, 4),
reason='functools.partialmethod is available on py34 or above')
-@pytest.mark.usefixtures('setup_test')
-def test_partialmethod():
- def call_autodoc(objtype, name):
- inst = app.registry.documenters[objtype](directive, name)
- inst.generate()
- result = list(directive.result)
- del directive.result[:]
- return result
-
- options.inherited_members = True
- options.undoc_members = True
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_partialmethod(app):
expected = [
'',
'.. py:class:: Cell',
@@ -962,4 +1346,27 @@ def test_partialmethod():
# TODO: this condition should be updated after 3.7-final release.
expected = '\n'.join(expected).replace(' -> None', '').split('\n')
- assert call_autodoc('class', 'target.partialmethod.Cell') == expected
+ options = {"members": None}
+ actual = do_autodoc(app, 'class', 'target.partialmethod.Cell', options)
+ assert list(actual) == expected
+
+
+@pytest.mark.sphinx('html', testroot='ext-autodoc')
+def test_autodoc_default_flags(app):
+ # no settings
+ actual = do_autodoc(app, 'class', 'target.EnumCls')
+ assert ' .. py:attribute:: EnumCls.val1' not in actual
+ assert ' .. py:attribute:: EnumCls.val4' not in actual
+
+ # with :members:
+ app.config.autodoc_default_flags = ['members']
+ actual = do_autodoc(app, 'class', 'target.EnumCls')
+ assert ' .. py:attribute:: EnumCls.val1' in actual
+ assert ' .. py:attribute:: EnumCls.val4' not in actual
+
+ # with :members: and :undoc-members:
+ app.config.autodoc_default_flags = ['members',
+ 'undoc-members']
+ actual = do_autodoc(app, 'class', 'target.EnumCls')
+ assert ' .. py:attribute:: EnumCls.val1' in actual
+ assert ' .. py:attribute:: EnumCls.val4' in actual
diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py
index 18731855f..ec09eaf21 100644
--- a/tests/test_build_gettext.py
+++ b/tests/test_build_gettext.py
@@ -175,8 +175,8 @@ def test_gettext_template_msgid_order_in_sphinxpot(app):
result = (app.outdir / 'sphinx.pot').text(encoding='utf-8')
assert re.search(
('msgid "Template 1".*'
- 'msgid "This is Template 1\.".*'
+ 'msgid "This is Template 1\\.".*'
'msgid "Template 2".*'
- 'msgid "This is Template 2\.".*'),
+ 'msgid "This is Template 2\\.".*'),
result,
flags=re.S)
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index 68017a80d..b8286edb3 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -18,6 +18,7 @@ import pytest
from html5lib import getTreeBuilder, HTMLParser
from six import PY3
+from sphinx.errors import ConfigError
from sphinx.testing.util import remove_unicode_literals, strip_escseq
from sphinx.util.inventory import InventoryFile
@@ -1327,3 +1328,62 @@ def test_html_baseurl_and_html_file_suffix(app, status, warning):
result = (app.outdir / 'qux' / 'index.htm').text(encoding='utf8')
assert '<link rel="canonical" href="https://example.com/subdir/qux/index.htm" />' in result
+
+
+@pytest.mark.sphinx('html', testroot='basic')
+def test_default_html_math_renderer(app, status, warning):
+ assert app.builder.math_renderer_name == 'mathjax'
+
+
+@pytest.mark.sphinx('html', testroot='basic',
+ confoverrides={'extensions': ['sphinx.ext.mathjax']})
+def test_html_math_renderer_is_mathjax(app, status, warning):
+ assert app.builder.math_renderer_name == 'mathjax'
+
+
+@pytest.mark.sphinx('html', testroot='basic',
+ confoverrides={'extensions': ['sphinx.ext.imgmath']})
+def test_html_math_renderer_is_imgmath(app, status, warning):
+ assert app.builder.math_renderer_name == 'imgmath'
+
+
+@pytest.mark.sphinx('html', testroot='basic',
+ confoverrides={'extensions': ['sphinx.ext.jsmath',
+ 'sphinx.ext.imgmath']})
+def test_html_math_renderer_is_duplicated(make_app, app_params):
+ try:
+ args, kwargs = app_params
+ make_app(*args, **kwargs)
+ assert False
+ except ConfigError as exc:
+ assert str(exc) == ('Many math_renderers are registered. '
+ 'But no math_renderer is selected.')
+
+
+@pytest.mark.sphinx('html', testroot='basic',
+ confoverrides={'extensions': ['sphinx.ext.imgmath',
+ 'sphinx.ext.mathjax']})
+def test_html_math_renderer_is_duplicated2(app, status, warning):
+ # case of both mathjax and another math_renderer is loaded
+ assert app.builder.math_renderer_name == 'imgmath' # The another one is chosen
+
+
+@pytest.mark.sphinx('html', testroot='basic',
+ confoverrides={'extensions': ['sphinx.ext.jsmath',
+ 'sphinx.ext.imgmath'],
+ 'html_math_renderer': 'imgmath'})
+def test_html_math_renderer_is_chosen(app, status, warning):
+ assert app.builder.math_renderer_name == 'imgmath'
+
+
+@pytest.mark.sphinx('html', testroot='basic',
+ confoverrides={'extensions': ['sphinx.ext.jsmath',
+ 'sphinx.ext.mathjax'],
+ 'html_math_renderer': 'imgmath'})
+def test_html_math_renderer_is_mismatched(make_app, app_params):
+ try:
+ args, kwargs = app_params
+ make_app(*args, **kwargs)
+ assert False
+ except ConfigError as exc:
+ assert str(exc) == "Unknown math_renderer 'imgmath' is given."
diff --git a/tests/test_config.py b/tests/test_config.py
index 6a910ecfc..0f0246c0b 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -172,20 +172,20 @@ def make_app_with_empty_project(make_app, tempdir):
def test_needs_sphinx(make_app_with_empty_project):
make_app = make_app_with_empty_project
# micro version
- app = make_app(confoverrides={'needs_sphinx': '1.3.3'}) # OK: less
- app = make_app(confoverrides={'needs_sphinx': '1.3.4'}) # OK: equals
+ make_app(confoverrides={'needs_sphinx': '1.3.3'}) # OK: less
+ make_app(confoverrides={'needs_sphinx': '1.3.4'}) # OK: equals
with pytest.raises(VersionRequirementError):
make_app(confoverrides={'needs_sphinx': '1.3.5'}) # NG: greater
# minor version
- app = make_app(confoverrides={'needs_sphinx': '1.2'}) # OK: less
- app = make_app(confoverrides={'needs_sphinx': '1.3'}) # OK: equals
+ make_app(confoverrides={'needs_sphinx': '1.2'}) # OK: less
+ make_app(confoverrides={'needs_sphinx': '1.3'}) # OK: equals
with pytest.raises(VersionRequirementError):
make_app(confoverrides={'needs_sphinx': '1.4'}) # NG: greater
# major version
- app = make_app(confoverrides={'needs_sphinx': '0'}) # OK: less
- app = make_app(confoverrides={'needs_sphinx': '1'}) # OK: equals
+ make_app(confoverrides={'needs_sphinx': '0'}) # OK: less
+ make_app(confoverrides={'needs_sphinx': '1'}) # OK: equals
with pytest.raises(VersionRequirementError):
make_app(confoverrides={'needs_sphinx': '2'}) # NG: greater
diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py
index ab956fefe..e13381817 100644
--- a/tests/test_domain_cpp.py
+++ b/tests/test_domain_cpp.py
@@ -760,9 +760,9 @@ def test_xref_consistency(app, status, warning):
def classes(role, tag):
pattern = (r'{role}-role:.*?'
- '<(?P<tag>{tag}) .*?class=["\'](?P<classes>.*?)["\'].*?>'
- '.*'
- '</(?P=tag)>').format(role=role, tag=tag)
+ r'<(?P<tag>{tag}) .*?class=["\'](?P<classes>.*?)["\'].*?>'
+ r'.*'
+ r'</(?P=tag)>').format(role=role, tag=tag)
result = re.search(pattern, output)
expect = '''\
Pattern for role `{role}` with tag `{tag}`
@@ -783,17 +783,17 @@ not found in `{test}`
self.content_classes[tag] = classes(role, tag)
# not actually used as a reference point
- #code_role = RoleClasses('code', 'code', [])
+ # code_role = RoleClasses('code', 'code', [])
any_role = RoleClasses('any', 'a', ['code'])
cpp_any_role = RoleClasses('cpp-any', 'a', ['code'])
# NYI: consistent looks
- #texpr_role = RoleClasses('cpp-texpr', 'span', ['a', 'code'])
+ # texpr_role = RoleClasses('cpp-texpr', 'span', ['a', 'code'])
expr_role = RoleClasses('cpp-expr', 'code', ['a'])
texpr_role = RoleClasses('cpp-texpr', 'span', ['a', 'span'])
# XRefRole-style classes
- ## any and cpp:any do not put these classes at the root
+ # any and cpp:any do not put these classes at the root
# n.b. the generic any machinery finds the specific 'cpp-class' object type
expect = 'any uses XRefRole classes'
diff --git a/tests/test_environment_toctree.py b/tests/test_environment_toctree.py
index 3fbb21856..618b7afe3 100644
--- a/tests/test_environment_toctree.py
+++ b/tests/test_environment_toctree.py
@@ -113,7 +113,8 @@ def test_glob(app):
maxdepth=-1, numbered=0, includefiles=includefiles,
entries=[(None, 'foo'), (None, 'bar/index'), (None, 'bar/bar_1'),
(None, 'bar/bar_2'), (None, 'bar/bar_3'), (None, 'baz'),
- (None, 'qux/index'), ('hyperref', 'https://sphinx-doc.org/?q=sphinx')])
+ (None, 'qux/index'),
+ ('hyperref', 'https://sphinx-doc.org/?q=sphinx')])
assert_node(toctree[0][1][1],
[list_item, ([compact_paragraph, reference, "reversed order"],
[bullet_list, addnodes.toctree])]) # [0][1][1][1][0]
diff --git a/tests/test_ext_apidoc.py b/tests/test_ext_apidoc.py
index 8c81a6e12..3d1517929 100644
--- a/tests/test_ext_apidoc.py
+++ b/tests/test_ext_apidoc.py
@@ -387,8 +387,6 @@ def extract_toc(path):
coderoot='test-apidoc-subpackage-in-toc',
options=['--separate']
)
-
-
def test_subpackage_in_toc(make_app, apidoc):
"""Make sure that empty subpackages with non-empty subpackages in them
are not skipped (issue #4520)
diff --git a/tests/test_ext_doctest.py b/tests/test_ext_doctest.py
index 4aad0d274..7137af31b 100644
--- a/tests/test_ext_doctest.py
+++ b/tests/test_ext_doctest.py
@@ -9,6 +9,7 @@
:license: BSD, see LICENSE for details.
"""
import os
+
import pytest
from packaging.specifiers import InvalidSpecifier
from packaging.version import InvalidVersion
diff --git a/tox.ini b/tox.ini
index d2f253fb3..6715fd338 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,13 +1,13 @@
[tox]
minversion = 2.0
-envlist = docs,flake8,mypy,coverage,py{27,34,35,36,py},du{11,12,13,14}
+envlist = docs,flake8,mypy,coverage,py{27,34,35,36,37,38,py},du{11,12,13,14}
[testenv]
usedevelop = True
passenv =
https_proxy http_proxy no_proxy PERL PERL5LIB PYTEST_ADDOPTS EPUBCHECK_PATH
description =
- py{27,34,35,36,py}: Run unit tests against {envname}.
+ py{27,34,35,36,37,38,py}: Run unit tests against {envname}.
du{11,12,13,14}: Run unit tests with the given version of docutils.
# TODO(stephenfin) Replace this with the 'extras' config option when tox 2.4 is