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 + + 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 -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 `, 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. - +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 `, 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 similarity index 99% rename from sphinx/texinputs/cyrLICRutf8.xdy rename to 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,13 +24,18 @@ 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' -%} +{% 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 -%} -{% 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) +@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:', + '' + ] - # 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) + # TODO: :noindex: should be propagated to children of target item. - # test descriptor docstrings - assert_result_contains(' Descriptor instance docstring.', - 'attribute', 'target.Class.descr') + actual = do_autodoc(app, 'class', 'target.Base', options) + assert list(actual) == [ + '', + '.. py:class:: Base', + ' :noindex:', + ' :module: target', + '' + ] - # 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_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') + 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 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'] +@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.', + ' ' + ] - # 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_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.', + ' ' + ] - # 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') + +@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('<.*>', '', 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: = ', + 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('<.*>', '', 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: = ', + 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.', + ' ' + ] + + # 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 '' 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}) .*?class=["\'](?P.*?)["\'].*?>' - '.*' - '').format(role=role, tag=tag) + r'<(?P{tag}) .*?class=["\'](?P.*?)["\'].*?>' + r'.*' + r'').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