Merge branch 'master' into 789_escape_braces_on_samp_role

This commit is contained in:
Takeshi KOMIYA 2018-05-03 01:32:35 +09:00 committed by GitHub
commit 0ea06e40ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 861 additions and 551 deletions

13
CHANGES
View File

@ -23,6 +23,8 @@ Incompatible changes
:py:meth:`.Sphinx.add_transform()` :py:meth:`.Sphinx.add_transform()`
* #4827: All ``substitution_definition`` nodes are removed from doctree on * #4827: All ``substitution_definition`` nodes are removed from doctree on
reading phase reading phase
* ``docutils.conf`` on ``$HOME`` and ``/etc`` directories are ignored. Only
``docutils.conf`` on confdir is refered.
* #789: ``:samp:`` role supports to escape curly braces with backslash * #789: ``:samp:`` role supports to escape curly braces with backslash
Deprecated Deprecated
@ -44,6 +46,14 @@ Deprecated
* ``app.override_domain()`` is deprecated * ``app.override_domain()`` is deprecated
* ``app.add_stylesheet()`` is deprecated * ``app.add_stylesheet()`` is deprecated
* ``sphinx.versioning.prepare()`` is deprecated * ``sphinx.versioning.prepare()`` is deprecated
* ``Config.__init__()`` has changed; the *dirname*, *filename* and *tags*
argument has been deprecated
* ``Config.check_types()`` is deprecated
* ``Config.check_unicode()`` is deprecated
* ``sphinx.application.CONFIG_FILENAME`` is deprecated
* ``highlightlang`` directive is deprecated
* ``env.read_doc()`` is deprecated
* ``env.write_doctree()`` is deprecated
For more details, see `deprecation APIs list For more details, see `deprecation APIs list
<http://www.sphinx-doc.org/en/master/extdev/index.html#deprecated-apis>`_ <http://www.sphinx-doc.org/en/master/extdev/index.html#deprecated-apis>`_
@ -75,6 +85,9 @@ Features added
* Improve warning messages during including (refs: #4818) * Improve warning messages during including (refs: #4818)
* LaTeX: separate customizability of :rst:role:`guilabel` and * LaTeX: separate customizability of :rst:role:`guilabel` and
:rst:role:`menuselection` (refs: #4830) :rst:role:`menuselection` (refs: #4830)
* Add ``Config.read()`` classmethod to create a new config object from
configuration file
* #4866: Wrap graphviz diagrams in ``<div>`` tag
Bugs fixed Bugs fixed
---------- ----------

View File

@ -6,7 +6,7 @@
:target: https://pypi.org/project/Sphinx/ :target: https://pypi.org/project/Sphinx/
:alt: Package on PyPi :alt: Package on PyPi
.. image:: https://readthedocs.org/projects/sphinx/badge/ .. image:: https://readthedocs.org/projects/sphinx/badge/?version=master
:target: http://www.sphinx-doc.org/ :target: http://www.sphinx-doc.org/
:alt: Documentation Status :alt: Documentation Status

View File

@ -10,17 +10,17 @@ Sphinx documentation contents
usage/installation usage/installation
usage/quickstart usage/quickstart
usage/restructuredtext/index usage/restructuredtext/index
usage/markdown
usage/configuration
intro intro
man/index man/index
builders builders
config
intl intl
theming theming
setuptools setuptools
templating templating
latex latex
markdown
extensions extensions
extdev/index extdev/index
websupport websupport

View File

@ -116,11 +116,42 @@ The following is a list of deprecated interface.
- (will be) Removed - (will be) Removed
- Alternatives - Alternatives
* - :rst:dir:`highlightlang`
- 1.8
- 4.0
- :rst:dir:`highlight`
* - :meth:`~sphinx.application.Sphinx.add_stylesheet()` * - :meth:`~sphinx.application.Sphinx.add_stylesheet()`
- 1.8 - 1.8
- 4.0 - 4.0
- :meth:`~sphinx.application.Sphinx.add_css_file()` - :meth:`~sphinx.application.Sphinx.add_css_file()`
* - ``sphinx.application.CONFIG_FILENAME``
- 1.8
- 3.0
- ``sphinx.config.CONFIG_FILENAME``
* - ``Config.check_unicode()``
- 1.8
- 3.0
- ``sphinx.config.check_unicode()``
* - ``Config.check_types()``
- 1.8
- 3.0
- ``sphinx.config.check_confval_types()``
* - ``dirname``, ``filename`` and ``tags`` arguments of
``Config.__init__()``
- 1.8
- 3.0
- ``Config.read()``
* - The value of :confval:`html_search_options`
- 1.8
- 3.0
- see :confval:`html_search_options`
* - ``sphinx.versioning.prepare()`` * - ``sphinx.versioning.prepare()``
- 1.8 - 1.8
- 3.0 - 3.0
@ -172,6 +203,11 @@ The following is a list of deprecated interface.
- 3.0 - 3.0
- ``Builder.read()`` - ``Builder.read()``
* - ``BuildEnvironment.read_doc()``
- 1.8
- 3.0
- ``Builder.read_doc()``
* - ``BuildEnvironment._read_serial()`` * - ``BuildEnvironment._read_serial()``
- 1.8 - 1.8
- 3.0 - 3.0
@ -182,6 +218,11 @@ The following is a list of deprecated interface.
- 3.0 - 3.0
- ``Builder.read()`` - ``Builder.read()``
* - ``BuildEnvironment.write_doctree()``
- 1.8
- 3.0
- ``Builder.write_doctree()``
* - ``sphinx.locale.l_()`` * - ``sphinx.locale.l_()``
- 1.8 - 1.8
- 3.0 - 3.0

View File

@ -1,4 +1,4 @@
.. highlightlang:: python .. highlight:: python
.. _latex: .. _latex:

View File

@ -1,45 +0,0 @@
.. highlightlang:: python
.. _markdown:
Markdown support
================
`Markdown <https://daringfireball.net/projects/markdown/>`__ is a lightweight markup language with a simplistic plain
text formatting syntax.
It exists in many syntactically different *flavors*.
To support Markdown-based documentation, Sphinx can use
`recommonmark <https://recommonmark.readthedocs.io/en/latest/index.html>`__.
recommonmark is a Docutils bridge to `CommonMark-py <https://github.com/rtfd/CommonMark-py>`__, a
Python package for parsing the `CommonMark <http://commonmark.org/>`__ Markdown flavor.
Configuration
-------------
To configure your Sphinx project for Markdown support, proceed as follows:
#. Install recommonmark:
::
pip install recommonmark
#. Add the Markdown parser to the ``source_parsers`` configuration variable in your Sphinx configuration file:
::
source_parsers = {
'.md': 'recommonmark.parser.CommonMarkParser',
}
You can replace `.md` with a filename extension of your choice.
#. Add the Markdown filename extension to the ``source_suffix`` configuration variable:
::
source_suffix = ['.rst', '.md']
#. You can further configure recommonmark to allow custom syntax that standard CommonMark doesn't support. Read more in
the `recommonmark documentation <https://recommonmark.readthedocs.io/en/latest/auto_structify.html>`__.

View File

@ -1,4 +1,4 @@
.. highlightlang:: python .. highlight:: python
HTML theming support HTML theming support
==================== ====================

View File

@ -1,9 +1,10 @@
.. highlightlang:: python .. highlight:: python
.. _build-config: .. _build-config:
The build configuration file =============
============================ Configuration
=============
.. module:: conf .. module:: conf
:synopsis: Build configuration file. :synopsis: Build configuration file.
@ -14,11 +15,10 @@ and contains (almost) all configuration needed to customize Sphinx input
and output behavior. and output behavior.
An optional file `docutils.conf`_ can be added to the configuration An optional file `docutils.conf`_ can be added to the configuration
directory to adjust `Docutils`_ configuration if not otherwise overriden or directory to adjust `Docutils`_ configuration if not otherwise overridden or
set by Sphinx. set by Sphinx.
.. _`docutils`: http://docutils.sourceforge.net/ .. _`docutils`: http://docutils.sourceforge.net/
.. _`docutils.conf`: http://docutils.sourceforge.net/docs/user/config.html .. _`docutils.conf`: http://docutils.sourceforge.net/docs/user/config.html
The configuration file is executed as Python code at build time (using The configuration file is executed as Python code at build time (using
@ -36,8 +36,8 @@ Important points to note:
``"sphinx.builders.Builder"`` means the ``Builder`` class in the ``"sphinx.builders.Builder"`` means the ``Builder`` class in the
``sphinx.builders`` module. ``sphinx.builders`` module.
* Remember that document names use ``/`` as the path separator and don't contain * Remember that document names use ``/`` as the path separator and don't
the file name extension. contain the file name extension.
* Since :file:`conf.py` is read as a Python file, the usual rules apply for * Since :file:`conf.py` is read as a Python file, the usual rules apply for
encodings and Unicode support: declare the encoding using an encoding cookie encodings and Unicode support: declare the encoding using an encoding cookie
@ -82,8 +82,8 @@ General configuration
That way, you can load an extension called ``extname`` from the subdirectory That way, you can load an extension called ``extname`` from the subdirectory
``sphinxext``. ``sphinxext``.
The configuration file itself can be an extension; for that, you only need to The configuration file itself can be an extension; for that, you only need
provide a :func:`setup` function in it. to provide a :func:`setup` function in it.
.. confval:: source_suffix .. confval:: source_suffix
@ -136,7 +136,8 @@ General configuration
.. note:: .. note::
Read more about how to use Markdown with Sphinx at :ref:`markdown`. Refer to :doc:`/usage/markdown` for more information on using Markdown
with Sphinx.
.. versionadded:: 1.3 .. versionadded:: 1.3
@ -151,9 +152,10 @@ General configuration
.. confval:: exclude_patterns .. confval:: exclude_patterns
A list of glob-style patterns that should be excluded when looking for source A list of glob-style patterns that should be excluded when looking for
files. [1]_ They are matched against the source file names relative to the source files. [1]_ They are matched against the source file names relative
source directory, using slashes as directory separators on all platforms. to the source directory, using slashes as directory separators on all
platforms.
Example patterns: Example patterns:
@ -181,10 +183,10 @@ General configuration
.. confval:: template_bridge .. confval:: template_bridge
A string with the fully-qualified name of a callable (or simply a class) that A string with the fully-qualified name of a callable (or simply a class)
returns an instance of :class:`~sphinx.application.TemplateBridge`. This that returns an instance of :class:`~sphinx.application.TemplateBridge`.
instance is then used to render HTML documents, and possibly the output of This instance is then used to render HTML documents, and possibly the output
other builders (currently the changes builder). (Note that the template of other builders (currently the changes builder). (Note that the template
bridge must be made theme-aware if HTML themes are to be used.) bridge must be made theme-aware if HTML themes are to be used.)
.. confval:: rst_epilog .. confval:: rst_epilog
@ -247,9 +249,9 @@ General configuration
.. confval:: keep_warnings .. confval:: keep_warnings
If true, keep warnings as "system message" paragraphs in the built documents. If true, keep warnings as "system message" paragraphs in the built
Regardless of this setting, warnings are always written to the standard error documents. Regardless of this setting, warnings are always written to the
stream when ``sphinx-build`` is run. standard error stream when ``sphinx-build`` is run.
The default is ``False``, the pre-0.5 behavior was to always keep them. The default is ``False``, the pre-0.5 behavior was to always keep them.
@ -302,8 +304,8 @@ General configuration
.. confval:: needs_sphinx .. confval:: needs_sphinx
If set to a ``major.minor`` version string like ``'1.1'``, Sphinx will If set to a ``major.minor`` version string like ``'1.1'``, Sphinx will
compare it with its version and refuse to build if it is too old. Default is compare it with its version and refuse to build if it is too old. Default
no requirement. is no requirement.
.. versionadded:: 1.0 .. versionadded:: 1.0
@ -312,8 +314,8 @@ General configuration
.. confval:: needs_extensions .. confval:: needs_extensions
This value can be a dictionary specifying version requirements for extensions This value can be a dictionary specifying version requirements for
in :confval:`extensions`, e.g. ``needs_extensions = extensions in :confval:`extensions`, e.g. ``needs_extensions =
{'sphinxcontrib.something': '1.5'}``. The version strings should be in the {'sphinxcontrib.something': '1.5'}``. The version strings should be in the
form ``major.minor``. Requirements do not have to be specified for all form ``major.minor``. Requirements do not have to be specified for all
extensions, only for those you want to check. extensions, only for those you want to check.
@ -351,10 +353,10 @@ General configuration
.. confval:: nitpick_ignore .. confval:: nitpick_ignore
A list of ``(type, target)`` tuples (by default empty) that should be ignored A list of ``(type, target)`` tuples (by default empty) that should be
when generating warnings in "nitpicky mode". Note that ``type`` should ignored when generating warnings in "nitpicky mode". Note that ``type``
include the domain name if present. Example entries would be ``('py:func', should include the domain name if present. Example entries would be
'int')`` or ``('envvar', 'LD_LIBRARY_PATH')``. ``('py:func', 'int')`` or ``('envvar', 'LD_LIBRARY_PATH')``.
.. versionadded:: 1.1 .. versionadded:: 1.1
@ -366,8 +368,8 @@ General configuration
.. note:: .. note::
The LaTeX builder always assigns numbers whether this option is enabled or The LaTeX builder always assigns numbers whether this option is enabled
not. or not.
.. versionadded:: 1.3 .. versionadded:: 1.3
@ -375,7 +377,7 @@ General configuration
A dictionary mapping ``'figure'``, ``'table'``, ``'code-block'`` and A dictionary mapping ``'figure'``, ``'table'``, ``'code-block'`` and
``'section'`` to strings that are used for format of figure numbers. ``'section'`` to strings that are used for format of figure numbers.
As a special character, `%s` will be replaced to figure number. As a special character, ``%s`` will be replaced to figure number.
Default is to use ``'Fig. %s'`` for ``'figure'``, ``'Table %s'`` for Default is to use ``'Fig. %s'`` for ``'figure'``, ``'Table %s'`` for
``'table'``, ``'Listing %s'`` for ``'code-block'`` and ``'Section'`` for ``'table'``, ``'Listing %s'`` for ``'code-block'`` and ``'Section'`` for
@ -474,6 +476,7 @@ General configuration
.. versionadded:: 1.5 .. versionadded:: 1.5
Project information Project information
------------------- -------------------
@ -513,7 +516,7 @@ Project information
* Otherwise, the current time is formatted using :func:`time.strftime` and * Otherwise, the current time is formatted using :func:`time.strftime` and
the format given in :confval:`today_fmt`. the format given in :confval:`today_fmt`.
The default is no :confval:`today` and a :confval:`today_fmt` of ``'%B %d, The default is now :confval:`today` and a :confval:`today_fmt` of ``'%B %d,
%Y'`` (or, if translation is enabled with :confval:`language`, an equivalent %Y'`` (or, if translation is enabled with :confval:`language`, an equivalent
format for the selected locale). format for the selected locale).
@ -527,7 +530,7 @@ Project information
.. versionchanged:: 1.4 .. versionchanged:: 1.4
The default is now ``'default'``. It is similar to ``'python3'``; The default is now ``'default'``. It is similar to ``'python3'``;
it is mostly a superset of ``'python'``. but it fallbacks to it is mostly a superset of ``'python'`` but it fallbacks to
``'none'`` without warning if failed. ``'python3'`` and other ``'none'`` without warning if failed. ``'python3'`` and other
languages will emit warning if failed. If you prefer Python 2 languages will emit warning if failed. If you prefer Python 2
only highlighting, you can set it back to ``'python'``. only highlighting, you can set it back to ``'python'``.
@ -544,7 +547,8 @@ Project information
.. confval:: pygments_style .. confval:: pygments_style
The style name to use for Pygments highlighting of source code. If not set, The style name to use for Pygments highlighting of source code. If not set,
either the theme's default style or ``'sphinx'`` is selected for HTML output. either the theme's default style or ``'sphinx'`` is selected for HTML
output.
.. versionchanged:: 0.3 .. versionchanged:: 0.3
If the value is a fully-qualified name of a custom Pygments style class, If the value is a fully-qualified name of a custom Pygments style class,
@ -579,8 +583,8 @@ Project information
.. confval:: trim_footnote_reference_space .. confval:: trim_footnote_reference_space
Trim spaces before footnote references that are necessary for the reST parser Trim spaces before footnote references that are necessary for the reST
to recognize the footnote, but do not look too nice in the output. parser to recognize the footnote, but do not look too nice in the output.
.. versionadded:: 0.6 .. versionadded:: 0.6
@ -767,6 +771,7 @@ documentation on :ref:`intl` for details.
.. versionchanged:: 1.5 .. versionchanged:: 1.5
Added ``{path}`` and ``{basename}`` tokens. Added ``{path}`` and ``{basename}`` tokens.
.. _html-options: .. _html-options:
Options for HTML output Options for HTML output
@ -778,7 +783,7 @@ that use Sphinx's HTMLWriter class.
.. confval:: html_theme .. confval:: html_theme
The "theme" that the HTML output should use. See the :doc:`section about The "theme" that the HTML output should use. See the :doc:`section about
theming <theming>`. The default is ``'alabaster'``. theming </theming>`. The default is ``'alabaster'``.
.. versionadded:: 0.6 .. versionadded:: 0.6
@ -800,11 +805,12 @@ that use Sphinx's HTMLWriter class.
.. confval:: html_style .. confval:: html_style
The style sheet to use for HTML pages. A file of that name must exist either The style sheet to use for HTML pages. A file of that name must exist
in Sphinx's :file:`static/` path, or in one of the custom paths given in either in Sphinx's :file:`static/` path, or in one of the custom paths given
:confval:`html_static_path`. Default is the stylesheet given by the selected in :confval:`html_static_path`. Default is the stylesheet given by the
theme. If you only want to add or override a few things compared to the selected theme. If you only want to add or override a few things compared
theme's stylesheet, use CSS ``@import`` to import the theme's stylesheet. to the theme's stylesheet, use CSS ``@import`` to import the theme's
stylesheet.
.. confval:: html_title .. confval:: html_title
@ -815,8 +821,8 @@ that use Sphinx's HTMLWriter class.
.. confval:: html_short_title .. confval:: html_short_title
A shorter "title" for the HTML docs. This is used in for links in the header A shorter "title" for the HTML docs. This is used in for links in the
and in the HTML Help docs. If not given, it defaults to the value of header and in the HTML Help docs. If not given, it defaults to the value of
:confval:`html_title`. :confval:`html_title`.
.. versionadded:: 0.4 .. versionadded:: 0.4
@ -909,9 +915,9 @@ that use Sphinx's HTMLWriter class.
.. versionadded:: 1.2 .. versionadded:: 1.2
.. versionchanged:: 1.4 .. versionchanged:: 1.4
The dotfiles in the extra directory will be copied to the output directory. The dotfiles in the extra directory will be copied to the output
And it refers :confval:`exclude_patterns` on copying extra files and directory. And it refers :confval:`exclude_patterns` on copying extra
directories, and ignores if path matches to patterns. files and directories, and ignores if path matches to patterns.
.. confval:: html_last_updated_fmt .. confval:: html_last_updated_fmt
@ -1066,12 +1072,12 @@ that use Sphinx's HTMLWriter class.
.. confval:: html_use_opensearch .. confval:: html_use_opensearch
If nonempty, an `OpenSearch <http://www.opensearch.org/Home>`_ description file will be If nonempty, an `OpenSearch <http://www.opensearch.org/Home>`_ description
output, and all pages will contain a ``<link>`` tag referring to it. Since file will be output, and all pages will contain a ``<link>`` tag referring
OpenSearch doesn't support relative URLs for its search page location, the to it. Since OpenSearch doesn't support relative URLs for its search page
value of this option must be the base URL from which these documents are location, the value of this option must be the base URL from which these
served (without trailing slash), e.g. ``"https://docs.python.org"``. The documents are served (without trailing slash), e.g.
default is ``''``. ``"https://docs.python.org"``. The default is ``''``.
.. confval:: html_file_suffix .. confval:: html_file_suffix
@ -1178,25 +1184,27 @@ that use Sphinx's HTMLWriter class.
The Japanese support has these options: The Japanese support has these options:
:type: :type:
_`type` is dotted module path string to specify Splitter implementation which _`type` is dotted module path string to specify Splitter implementation
should be derived from :class:`sphinx.search.ja.BaseSplitter`. which should be derived from :class:`sphinx.search.ja.BaseSplitter`. If
If not specified or None is specified, ``'sphinx.search.ja.DefaultSplitter'`` will not specified or None is specified,
be used. ``'sphinx.search.ja.DefaultSplitter'`` will be used.
You can choose from these modules: You can choose from these modules:
:'sphinx.search.ja.DefaultSplitter': :'sphinx.search.ja.DefaultSplitter':
TinySegmenter algorithm. This is default splitter. TinySegmenter algorithm. This is default splitter.
:'sphinx.search.ja.MeCabSplitter': :'sphinx.search.ja.MeCabSplitter':
MeCab binding. To use this splitter, 'mecab' python binding or dynamic link MeCab binding. To use this splitter, 'mecab' python binding or dynamic
library ('libmecab.so' for linux, 'libmecab.dll' for windows) is required. link library ('libmecab.so' for linux, 'libmecab.dll' for windows) is
required.
:'sphinx.search.ja.JanomeSplitter': :'sphinx.search.ja.JanomeSplitter':
Janome binding. To use this splitter, Janome binding. To use this splitter,
`Janome <https://pypi.org/project/Janome/>`_ is required. `Janome <https://pypi.org/project/Janome/>`_ is required.
To keep compatibility, ``'mecab'``, ``'janome'`` and ``'default'`` are also .. deprecated:: 1.6
acceptable. However it will be deprecated in Sphinx-1.6. ``'mecab'``, ``'janome'`` and ``'default'`` is deprecated.
To keep compatibility, ``'mecab'``, ``'janome'`` and ``'default'`` are
also acceptable.
Other option values depend on splitter value which you choose. Other option values depend on splitter value which you choose.
@ -1206,8 +1214,8 @@ that use Sphinx's HTMLWriter class.
:dict: :dict:
_`dict option` is the dictionary to use for the MeCab algorithm. _`dict option` is the dictionary to use for the MeCab algorithm.
:lib: :lib:
_`lib option` is the library name for finding the MeCab library via ctypes if _`lib option` is the library name for finding the MeCab library via
the Python binding is not installed. ctypes if the Python binding is not installed.
For example:: For example::
@ -1219,17 +1227,17 @@ that use Sphinx's HTMLWriter class.
} }
Options for ``'janome'``: Options for ``'janome'``:
:user_dic: _`user_dic option` is the user dictionary file path for Janome. :user_dic:
_`user_dic option` is the user dictionary file path for Janome.
:user_dic_enc: :user_dic_enc:
_`user_dic_enc option` is the encoding for the user dictionary file specified by _`user_dic_enc option` is the encoding for the user dictionary file
``user_dic`` option. Default is 'utf8'. specified by ``user_dic`` option. Default is 'utf8'.
.. versionadded:: 1.1 .. versionadded:: 1.1
.. versionchanged:: 1.4 .. versionchanged:: 1.4
html_search_options for Japanese is re-organized and any custom splitter can be html_search_options for Japanese is re-organized and any custom splitter
used by `type`_ settings. can be used by `type`_ settings.
The Chinese support has these options: The Chinese support has these options:
@ -1255,10 +1263,12 @@ that use Sphinx's HTMLWriter class.
.. confval:: html_experimental_html5_writer .. confval:: html_experimental_html5_writer
Output is processed with HTML5 writer. This feature needs docutils 0.13 or newer. Default is ``False``. Output is processed with HTML5 writer. This feature needs docutils 0.13 or
newer. Default is ``False``.
.. versionadded:: 1.6 .. versionadded:: 1.6
.. _htmlhelp-options: .. _htmlhelp-options:
Options for HTML help output Options for HTML help output
@ -1268,6 +1278,7 @@ Options for HTML help output
Output file base name for HTML help builder. Default is ``'pydoc'``. Output file base name for HTML help builder. Default is ``'pydoc'``.
.. _applehelp-options: .. _applehelp-options:
Options for Apple Help output Options for Apple Help output
@ -1313,8 +1324,8 @@ HTML builder, so the HTML options also apply where appropriate.
.. confval:: applehelp_icon .. confval:: applehelp_icon
The help bundle icon file, or ``None`` for no icon. According to Apples The help bundle icon file, or ``None`` for no icon. According to Apple's
documentation, this should be a 16-by-16 pixel version of the applications documentation, this should be a 16-by-16 pixel version of the application's
icon with a transparent background, saved as a PNG file. icon with a transparent background, saved as a PNG file.
.. confval:: applehelp_kb_product .. confval:: applehelp_kb_product
@ -1328,14 +1339,14 @@ HTML builder, so the HTML options also apply where appropriate.
e.g. ``https://example.com/kbsearch.py?p='product'&q='query'&l='lang'``. e.g. ``https://example.com/kbsearch.py?p='product'&q='query'&l='lang'``.
Help Viewer will replace the values ``'product'``, ``'query'`` and Help Viewer will replace the values ``'product'``, ``'query'`` and
``'lang'`` at runtime with the contents of :confval:`applehelp_kb_product`, ``'lang'`` at runtime with the contents of :confval:`applehelp_kb_product`,
the text entered by the user in the search box and the users system the text entered by the user in the search box and the user's system
language respectively. language respectively.
Defaults to ``None`` for no remote search. Defaults to ``None`` for no remote search.
.. confval:: applehelp_remote_url .. confval:: applehelp_remote_url
The URL for remote content. You can place a copy of your Help Books The URL for remote content. You can place a copy of your Help Book's
``Resources`` folder at this location and Help Viewer will attempt to use ``Resources`` folder at this location and Help Viewer will attempt to use
it to fetch updated content. it to fetch updated content.
@ -1450,8 +1461,8 @@ the `Dublin Core metadata <http://dublincore.org/>`_.
The HTML theme for the epub output. Since the default themes are not The HTML theme for the epub output. Since the default themes are not
optimized for small screen space, using the same theme for HTML and epub optimized for small screen space, using the same theme for HTML and epub
output is usually not wise. This defaults to ``'epub'``, a theme designed to output is usually not wise. This defaults to ``'epub'``, a theme designed
save visual space. to save visual space.
.. confval:: epub_theme_options .. confval:: epub_theme_options
@ -1498,9 +1509,9 @@ the `Dublin Core metadata <http://dublincore.org/>`_.
.. confval:: epub_publisher .. confval:: epub_publisher
The publisher of the document. This is put in the Dublin Core metadata. You The publisher of the document. This is put in the Dublin Core metadata.
may use any sensible string, e.g. the project homepage. The defaults to the You may use any sensible string, e.g. the project homepage. The defaults to
:confval:`author` option. the :confval:`author` option.
.. confval:: epub_copyright .. confval:: epub_copyright
@ -1525,9 +1536,9 @@ the `Dublin Core metadata <http://dublincore.org/>`_.
A unique identifier for the document. This is put in the Dublin Core A unique identifier for the document. This is put in the Dublin Core
metadata. You may use a metadata. You may use a
`XML's Name format <https://www.w3.org/TR/REC-xml/#NT-NameStartChar>`_ string. `XML's Name format <https://www.w3.org/TR/REC-xml/#NT-NameStartChar>`_
You can't use hyphen, period, numbers as a first character. string. You can't use hyphen, period, numbers as a first character. The
The default value is ``'unknown'``. default value is ``'unknown'``.
.. confval:: epub_cover .. confval:: epub_cover
@ -1681,12 +1692,14 @@ the `Dublin Core metadata <http://dublincore.org/>`_.
.. [#] https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode .. [#] https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode
.. _latex-options: .. _latex-options:
Options for LaTeX output Options for LaTeX output
------------------------ ------------------------
These options influence LaTeX output. See further :doc:`latex`. These options influence LaTeX output. Refer to :doc:`/latex` for more
information.
.. confval:: latex_engine .. confval:: latex_engine
@ -1734,7 +1747,8 @@ These options influence LaTeX output. See further :doc:`latex`.
they are to be inserted literally. they are to be inserted literally.
* *author*: Author for the LaTeX document. The same LaTeX markup caveat as * *author*: Author for the LaTeX document. The same LaTeX markup caveat as
for *title* applies. Use ``\\and`` to separate multiple authors, as in: for *title* applies. Use ``\\and`` to separate multiple authors, as in:
``'John \\and Sarah'`` (backslashes must be Python-escaped to reach LaTeX). ``'John \\and Sarah'`` (backslashes must be Python-escaped to reach
LaTeX).
* *documentclass*: Normally, one of ``'manual'`` or ``'howto'`` (provided * *documentclass*: Normally, one of ``'manual'`` or ``'howto'`` (provided
by Sphinx and based on ``'report'``, resp. ``'article'``; Japanese by Sphinx and based on ``'report'``, resp. ``'article'``; Japanese
documents use ``'jsbook'``, resp. ``'jreport'``.) "howto" (non-Japanese) documents use ``'jsbook'``, resp. ``'jreport'``.) "howto" (non-Japanese)
@ -1746,7 +1760,8 @@ These options influence LaTeX output. See further :doc:`latex`.
* *toctree_only*: Must be ``True`` or ``False``. If true, the *startdoc* * *toctree_only*: Must be ``True`` or ``False``. If true, the *startdoc*
document itself is not included in the output, only the documents document itself is not included in the output, only the documents
referenced by it via TOC trees. With this option, you can put extra stuff referenced by it via TOC trees. With this option, you can put extra stuff
in the master document that shows up in the HTML, but not the LaTeX output. in the master document that shows up in the HTML, but not the LaTeX
output.
.. versionadded:: 1.2 .. versionadded:: 1.2
In the past including your own document class required you to prepend the In the past including your own document class required you to prepend the
@ -1843,29 +1858,34 @@ These options influence LaTeX output. See further :doc:`latex`.
``'papersize'`` ``'papersize'``
Paper size option of the document class (``'a4paper'`` or Paper size option of the document class (``'a4paper'`` or
``'letterpaper'``), default ``'letterpaper'``. ``'letterpaper'``), default ``'letterpaper'``.
``'pointsize'`` ``'pointsize'``
Point size option of the document class (``'10pt'``, ``'11pt'`` or Point size option of the document class (``'10pt'``, ``'11pt'`` or
``'12pt'``), default ``'10pt'``. ``'12pt'``), default ``'10pt'``.
``'pxunit'`` ``'pxunit'``
the value of the ``px`` when used in image attributes ``width`` and the value of the ``px`` when used in image attributes ``width`` and
``height``. The default value is ``'0.75bp'`` which achieves ``height``. The default value is ``'0.75bp'`` which achieves
``96px=1in`` (in TeX ``1in = 72bp = 72.27pt``.) To obtain for ``96px=1in`` (in TeX ``1in = 72bp = 72.27pt``.) To obtain for
example ``100px=1in`` use ``'0.01in'`` or ``'0.7227pt'`` (the latter example ``100px=1in`` use ``'0.01in'`` or ``'0.7227pt'`` (the latter
leads to TeX computing a more precise value, due to the smaller unit leads to TeX computing a more precise value, due to the smaller unit
used in the specification); for ``72px=1in``, used in the specification); for ``72px=1in``, simply use ``'1bp'``; for
simply use ``'1bp'``; for ``90px=1in``, use ``'0.8bp'`` or ``'0.803pt'``. ``90px=1in``, use ``'0.8bp'`` or ``'0.803pt'``.
.. versionadded:: 1.5 .. versionadded:: 1.5
``'sphinxsetup'`` ``'sphinxsetup'``
A comma separated list of ``key=value`` package options for the Sphinx A comma separated list of ``key=value`` package options for the Sphinx
LaTeX style, default empty. See :doc:`latex`. LaTeX style, default empty. See :doc:`/latex`.
.. versionadded:: 1.5 .. versionadded:: 1.5
``'passoptionstopackages'`` ``'passoptionstopackages'``
A string which will be positioned early in the preamble, designed to A string which will be positioned early in the preamble, designed to
contain ``\\PassOptionsToPackage{options}{foo}`` commands. Default empty. contain ``\\PassOptionsToPackage{options}{foo}`` commands. Default empty.
.. versionadded:: 1.4 .. versionadded:: 1.4
``'babel'`` ``'babel'``
"babel" package inclusion, default ``'\\usepackage{babel}'`` (the "babel" package inclusion, default ``'\\usepackage{babel}'`` (the
suitable document language string is passed as class option, and suitable document language string is passed as class option, and
@ -1877,6 +1897,7 @@ These options influence LaTeX output. See further :doc:`latex`.
is ``'\\usepackage{polyglossia}\n\\setmainlanguage{<language>}'``. is ``'\\usepackage{polyglossia}\n\\setmainlanguage{<language>}'``.
.. versionchanged:: 1.6 .. versionchanged:: 1.6
``'lualatex'`` uses same default setting as ``'xelatex'`` ``'lualatex'`` uses same default setting as ``'xelatex'``
``'fontpkg'`` ``'fontpkg'``
Font package inclusion, default ``'\\usepackage{times}'`` (which uses Font package inclusion, default ``'\\usepackage{times}'`` (which uses
Times for text, Helvetica for sans serif and Courier for code-blocks). Times for text, Helvetica for sans serif and Courier for code-blocks).
@ -1906,12 +1927,15 @@ These options influence LaTeX output. See further :doc:`latex`.
the "Bjarne" style uses numbers spelled out in English). Other the "Bjarne" style uses numbers spelled out in English). Other
"fncychap" styles you can try are "Lenny", "Glenn", "Conny", "Rejne" and "fncychap" styles you can try are "Lenny", "Glenn", "Conny", "Rejne" and
"Bjornstrup". You can also set this to ``''`` to disable fncychap. "Bjornstrup". You can also set this to ``''`` to disable fncychap.
``'preamble'`` ``'preamble'``
Additional preamble content, default empty. See :doc:`latex`. Additional preamble content, default empty. See :doc:`/latex`.
``'atendofbody'`` ``'atendofbody'``
Additional document content (right before the indices), default empty. Additional document content (right before the indices), default empty.
.. versionadded:: 1.5 .. versionadded:: 1.5
``'figure_align'`` ``'figure_align'``
Latex figure float alignment, default 'htbp' (here, top, bottom, page). Latex figure float alignment, default 'htbp' (here, top, bottom, page).
Whenever an image doesn't fit into the current page, it will be Whenever an image doesn't fit into the current page, it will be
@ -1920,6 +1944,7 @@ These options influence LaTeX output. See further :doc:`latex`.
and position figures strictly in the order they appear in the source. and position figures strictly in the order they appear in the source.
.. versionadded:: 1.3 .. versionadded:: 1.3
``'footer'`` ``'footer'``
Additional footer content (before the indices), default empty. Additional footer content (before the indices), default empty.
@ -1936,6 +1961,7 @@ These options influence LaTeX output. See further :doc:`latex`.
.. versionadded:: 1.2 .. versionadded:: 1.2
.. versionchanged:: 1.6 .. versionchanged:: 1.6
Added this documentation. Added this documentation.
``'maxlistdepth'`` ``'maxlistdepth'``
LaTeX allows by default at most 6 levels for nesting list and LaTeX allows by default at most 6 levels for nesting list and
quote-like environments, with at most 4 enumerated lists, and 4 bullet quote-like environments, with at most 4 enumerated lists, and 4 bullet
@ -1953,6 +1979,7 @@ These options influence LaTeX output. See further :doc:`latex`.
dedicated commands of this LaTeX package. dedicated commands of this LaTeX package.
.. versionadded:: 1.5 .. versionadded:: 1.5
``'inputenc'`` ``'inputenc'``
"inputenc" package inclusion, defaults to "inputenc" package inclusion, defaults to
``'\\usepackage[utf8]{inputenc}'`` when using pdflatex. ``'\\usepackage[utf8]{inputenc}'`` when using pdflatex.
@ -1961,10 +1988,12 @@ These options influence LaTeX output. See further :doc:`latex`.
.. versionchanged:: 1.4.3 .. versionchanged:: 1.4.3
Previously ``'\\usepackage[utf8]{inputenc}'`` was used for all Previously ``'\\usepackage[utf8]{inputenc}'`` was used for all
compilers. compilers.
``'cmappkg'`` ``'cmappkg'``
"cmap" package inclusion, default ``'\\usepackage{cmap}'``. "cmap" package inclusion, default ``'\\usepackage{cmap}'``.
.. versionadded:: 1.2 .. versionadded:: 1.2
``'fontenc'`` ``'fontenc'``
"fontenc" package inclusion, default ``'\\usepackage[T1]{fontenc}'``. "fontenc" package inclusion, default ``'\\usepackage[T1]{fontenc}'``.
@ -1973,6 +2002,7 @@ These options influence LaTeX output. See further :doc:`latex`.
:confval:`latex_engine` is ``'xelatex'``. :confval:`latex_engine` is ``'xelatex'``.
.. versionchanged:: 1.6 .. versionchanged:: 1.6
``'lualatex'`` also uses ``fontspec`` per default. ``'lualatex'`` also uses ``fontspec`` per default.
``'geometry'`` ``'geometry'``
"geometry" package inclusion, the default definition is: "geometry" package inclusion, the default definition is:
@ -2003,6 +2033,7 @@ These options influence LaTeX output. See further :doc:`latex`.
width will be set to an integer multiple of the *zenkaku* width, and width will be set to an integer multiple of the *zenkaku* width, and
the text height to an integer multiple of the baseline. See the the text height to an integer multiple of the baseline. See the
:ref:`hmargin <latexsphinxsetuphmargin>` documentation for more. :ref:`hmargin <latexsphinxsetuphmargin>` documentation for more.
``'hyperref'`` ``'hyperref'``
"hyperref" package inclusion; also loads package "hypcap" and issues "hyperref" package inclusion; also loads package "hypcap" and issues
``\urlstyle{same}``. This is done after :file:`sphinx.sty` file is ``\urlstyle{same}``. This is done after :file:`sphinx.sty` file is
@ -2014,15 +2045,17 @@ These options influence LaTeX output. See further :doc:`latex`.
.. versionadded:: 1.5 .. versionadded:: 1.5
Previously this was done from inside :file:`sphinx.sty`. Previously this was done from inside :file:`sphinx.sty`.
``'maketitle'`` ``'maketitle'``
"maketitle" call, default ``'\\maketitle'`` (but it has been "maketitle" call, default ``'\\maketitle'`` (but it has been
redefined by the Sphinx ``manual`` and ``howto`` classes.) Override redefined by the Sphinx ``manual`` and ``howto`` classes.) Override
if you want to if you want to generate a differently-styled title page.
generate a differently-styled title page.
``'releasename'`` ``'releasename'``
value that prefixes ``'release'`` element on title page, default value that prefixes ``'release'`` element on title page, default
``'Release'``. As for *title* and *author* used in the tuples of ``'Release'``. As for *title* and *author* used in the tuples of
:confval:`latex_documents`, it is inserted as LaTeX markup. :confval:`latex_documents`, it is inserted as LaTeX markup.
``'tableofcontents'`` ``'tableofcontents'``
"tableofcontents" call, default ``'\\sphinxtableofcontents'`` (it is a "tableofcontents" call, default ``'\\sphinxtableofcontents'`` (it is a
wrapper of unmodified ``\tableofcontents``, which may itself be wrapper of unmodified ``\tableofcontents``, which may itself be
@ -2035,6 +2068,7 @@ These options influence LaTeX output. See further :doc:`latex`.
Previously the meaning of ``\tableofcontents`` itself was modified Previously the meaning of ``\tableofcontents`` itself was modified
by Sphinx. This created an incompatibility with dedicated packages by Sphinx. This created an incompatibility with dedicated packages
modifying it also such as "tocloft" or "etoc". modifying it also such as "tocloft" or "etoc".
``'transition'`` ``'transition'``
Commands used to display transitions, default Commands used to display transitions, default
``'\n\n\\bigskip\\hrule\\bigskip\n\n'``. Override if you want to ``'\n\n\\bigskip\\hrule\\bigskip\n\n'``. Override if you want to
@ -2043,12 +2077,14 @@ These options influence LaTeX output. See further :doc:`latex`.
.. versionadded:: 1.2 .. versionadded:: 1.2
.. versionchanged:: 1.6 .. versionchanged:: 1.6
Remove unneeded ``{}`` after ``\\hrule``. Remove unneeded ``{}`` after ``\\hrule``.
``'printindex'`` ``'printindex'``
"printindex" call, the last thing in the file, default "printindex" call, the last thing in the file, default
``'\\printindex'``. Override if you want to generate the index ``'\\printindex'``. Override if you want to generate the index
differently or append some content after the index. For example differently or append some content after the index. For example
``'\\footnotesize\\raggedright\\printindex'`` is advisable when the ``'\\footnotesize\\raggedright\\printindex'`` is advisable when the
index is full of long entries. index is full of long entries.
``'fvset'`` ``'fvset'``
Customization of ``fancyvrb`` LaTeX package. Defaults to Customization of ``fancyvrb`` LaTeX package. Defaults to
``'\\fvset{fontsize=\\small}'``, because default font (Courier) used in ``'\\fvset{fontsize=\\small}'``, because default font (Courier) used in
@ -2059,6 +2095,7 @@ These options influence LaTeX output. See further :doc:`latex`.
one unified typeface family (Latin Modern OpenType). one unified typeface family (Latin Modern OpenType).
.. versionadded:: 1.8 .. versionadded:: 1.8
* Keys that are set by other options and therefore should not be overridden * Keys that are set by other options and therefore should not be overridden
are: are:
@ -2087,19 +2124,20 @@ These options influence LaTeX output. See further :doc:`latex`.
.. confval:: latex_additional_files .. confval:: latex_additional_files
A list of file names, relative to the configuration directory, to copy to the A list of file names, relative to the configuration directory, to copy to
build directory when building LaTeX output. This is useful to copy files the build directory when building LaTeX output. This is useful to copy
that Sphinx doesn't copy automatically, e.g. if they are referenced in custom files that Sphinx doesn't copy automatically, e.g. if they are referenced in
LaTeX added in ``latex_elements``. Image files that are referenced in source custom LaTeX added in ``latex_elements``. Image files that are referenced
files (e.g. via ``.. image::``) are copied automatically. in source files (e.g. via ``.. image::``) are copied automatically.
You have to make sure yourself that the filenames don't collide with those of You have to make sure yourself that the filenames don't collide with those
any automatically copied files. of any automatically copied files.
.. versionadded:: 0.6 .. versionadded:: 0.6
.. versionchanged:: 1.2 .. versionchanged:: 1.2
This overrides the files which is provided from Sphinx such as sphinx.sty. This overrides the files which is provided from Sphinx such as
``sphinx.sty``.
.. _text-options: .. _text-options:
@ -2141,8 +2179,8 @@ These options influence text output.
.. confval:: text_secnumber_suffix .. confval:: text_secnumber_suffix
Suffix for section numbers in text output. Default: ``". "``. Set to ``" "`` Suffix for section numbers in text output. Default: ``". "``. Set to
to suppress the final dot on section numbers. ``" "`` to suppress the final dot on section numbers.
.. versionadded:: 1.7 .. versionadded:: 1.7
@ -2160,20 +2198,27 @@ These options influence manual page output.
must be a list of tuples ``(startdocname, name, description, authors, must be a list of tuples ``(startdocname, name, description, authors,
section)``, where the items are: section)``, where the items are:
* *startdocname*: document name that is the "root" of the manual page. All *startdocname*
documents referenced by it in TOC trees will be included in the manual file Document name that is the "root" of the manual page. All documents
too. (If you want one master manual page, use your :confval:`master_doc` referenced by it in TOC trees will be included in the manual file too.
here.) (If you want one master manual page, use your :confval:`master_doc` here.)
* *name*: name of the manual page. This should be a short string without
spaces or special characters. It is used to determine the file name as *name*
well as the name of the manual page (in the NAME section). Name of the manual page. This should be a short string without spaces or
* *description*: description of the manual page. This is used in the NAME special characters. It is used to determine the file name as well as the
section. name of the manual page (in the NAME section).
* *authors*: A list of strings with authors, or a single string. Can be an
empty string or list if you do not want to automatically generate an *description*
AUTHORS section in the manual page. Description of the manual page. This is used in the NAME section.
* *section*: The manual page section. Used for the output file name as well
as in the manual page header. *authors*
A list of strings with authors, or a single string. Can be an empty
string or list if you do not want to automatically generate an AUTHORS
section in the manual page.
*section*
The manual page section. Used for the output file name as well as in the
manual page header.
.. versionadded:: 1.0 .. versionadded:: 1.0
@ -2198,27 +2243,38 @@ These options influence Texinfo output.
author, dir_entry, description, category, toctree_only)``, where the items author, dir_entry, description, category, toctree_only)``, where the items
are: are:
* *startdocname*: document name that is the "root" of the Texinfo file. All *startdocname*
documents referenced by it in TOC trees will be included in the Texinfo Document name that is the "root" of the Texinfo file. All documents
file too. (If you want only one Texinfo file, use your referenced by it in TOC trees will be included in the Texinfo file too.
:confval:`master_doc` here.) (If you want only one Texinfo file, use your :confval:`master_doc` here.)
* *targetname*: file name (no extension) of the Texinfo file in the output
directory. *targetname*
* *title*: Texinfo document title. Can be empty to use the title of the File name (no extension) of the Texinfo file in the output directory.
*startdoc*. Inserted as Texinfo markup, so special characters like @ and
{} will need to be escaped to be inserted literally. *title*
* *author*: Author for the Texinfo document. Inserted as Texinfo markup. Texinfo document title. Can be empty to use the title of the *startdoc*.
Use ``@*`` to separate multiple authors, as in: ``'John@*Sarah'``. Inserted as Texinfo markup, so special characters like ``@`` and ``{}``
* *dir_entry*: The name that will appear in the top-level ``DIR`` menu file. will need to be escaped to be inserted literally.
* *description*: Descriptive text to appear in the top-level ``DIR`` menu
file. *author*
* *category*: Specifies the section which this entry will appear in the Author for the Texinfo document. Inserted as Texinfo markup. Use ``@*``
top-level ``DIR`` menu file. to separate multiple authors, as in: ``'John@*Sarah'``.
* *toctree_only*: Must be ``True`` or ``False``. If true, the *startdoc*
document itself is not included in the output, only the documents *dir_entry*
referenced by it via TOC trees. With this option, you can put extra stuff The name that will appear in the top-level ``DIR`` menu file.
in the master document that shows up in the HTML, but not the Texinfo
output. *description*
Descriptive text to appear in the top-level ``DIR`` menu file.
*category*
Specifies the section which this entry will appear in the top-level
``DIR`` menu file.
*toctree_only*
Must be ``True`` or ``False``. If true, the *startdoc* document itself is
not included in the output, only the documents referenced by it via TOC
trees. With this option, you can put extra stuff in the master document
that shows up in the HTML, but not the Texinfo output.
.. versionadded:: 1.1 .. versionadded:: 1.1
@ -2304,7 +2360,8 @@ builder, the HTML options also apply where appropriate.
.. confval:: qthelp_basename .. confval:: qthelp_basename
The basename for the qthelp file. It defaults to the :confval:`project` name. The basename for the qthelp file. It defaults to the :confval:`project`
name.
.. confval:: qthelp_namespace .. confval:: qthelp_namespace
@ -2369,7 +2426,7 @@ Options for the linkcheck builder
A list of regular expressions that match URIs that should skip checking A list of regular expressions that match URIs that should skip checking
the validity of anchors in links. This allows skipping entire sites, where the validity of anchors in links. This allows skipping entire sites, where
anchors are used to control dynamic pages, or just specific anchors within anchors are used to control dynamic pages, or just specific anchors within
a page, where javascript is used to add anchors dynamically, or use the a page, where JavaScript is used to add anchors dynamically, or use the
fragment as part of to trigger an internal REST request. Default is fragment as part of to trigger an internal REST request. Default is
``["/#!"]``. ``["/#!"]``.
@ -2390,8 +2447,8 @@ Options for the XML builder
.. [1] A note on available globbing syntax: you can use the standard shell .. [1] A note on available globbing syntax: you can use the standard shell
constructs ``*``, ``?``, ``[...]`` and ``[!...]`` with the feature that constructs ``*``, ``?``, ``[...]`` and ``[!...]`` with the feature that
these all don't match slashes. A double star ``**`` can be used to match these all don't match slashes. A double star ``**`` can be used to
any sequence of characters *including* slashes. match any sequence of characters *including* slashes.
.. _cpp-config: .. _cpp-config:
@ -2401,29 +2458,32 @@ Options for the C++ domain
.. confval:: cpp_index_common_prefix .. confval:: cpp_index_common_prefix
A list of prefixes that will be ignored when sorting C++ objects in the global index. A list of prefixes that will be ignored when sorting C++ objects in the
For example ``['awesome_lib::']``. global index. For example ``['awesome_lib::']``.
.. versionadded:: 1.5 .. versionadded:: 1.5
.. confval:: cpp_id_attributes .. confval:: cpp_id_attributes
A list of strings that the parser additionally should accept as attributes. A list of strings that the parser additionally should accept as attributes.
This can for example be used when attributes have been ``#define`` d for portability. This can for example be used when attributes have been ``#define`` d for
portability.
.. versionadded:: 1.5 .. versionadded:: 1.5
.. confval:: cpp_paren_attributes .. confval:: cpp_paren_attributes
A list of strings that the parser additionally should accept as attributes with one argument. A list of strings that the parser additionally should accept as attributes
That is, if ``my_align_as`` is in the list, then ``my_align_as(X)`` is parsed as an attribute with one argument. That is, if ``my_align_as`` is in the list, then
for all strings ``X`` that have balanced brances (``()``, ``[]``, and ``{}``). ``my_align_as(X)`` is parsed as an attribute for all strings ``X`` that have
This can for example be used when attributes have been ``#define`` d for portability. balanced brances (``()``, ``[]``, and ``{}``). This can for example be used
when attributes have been ``#define`` d for portability.
.. versionadded:: 1.5 .. versionadded:: 1.5
Example of configuration file Example of configuration file
============================= =============================
.. literalinclude:: _static/conf.py.txt .. literalinclude:: /_static/conf.py.txt
:language: python :language: python

47
doc/usage/markdown.rst Normal file
View File

@ -0,0 +1,47 @@
.. highlight:: python
.. _markdown:
========
Markdown
========
`Markdown`__ is a lightweight markup language with a simplistic plain text
formatting syntax. It exists in many syntactically different *flavors*. To
support Markdown-based documentation, Sphinx can use `recommonmark`__.
recommonmark is a Docutils bridge to `CommonMark-py`__, a Python package for
parsing the `CommonMark`__ Markdown flavor.
__ https://daringfireball.net/projects/markdown/
__ https://recommonmark.readthedocs.io/en/latest/index.html
__ https://github.com/rtfd/CommonMark-py
__ http://commonmark.org/
Configuration
-------------
To configure your Sphinx project for Markdown support, proceed as follows:
#. Install *recommonmark*::
pip install recommonmark
#. Add the Markdown parser to the ``source_parsers`` configuration variable in
your Sphinx configuration file::
source_parsers = {
'.md': 'recommonmark.parser.CommonMarkParser',
}
You can replace ``.md`` with a filename extension of your choice.
#. Add the Markdown filename extension to the ``source_suffix`` configuration
variable::
source_suffix = ['.rst', '.md']
#. You can further configure *recommonmark* to allow custom syntax that
standard *CommonMark* doesn't support. Read more in the `recommonmark
documentation`__.
__ https://recommonmark.readthedocs.io/en/latest/auto_structify.html

View File

@ -236,7 +236,8 @@ so on. The file is saved in UTF-8 by default, as indicated by the encoding
declaration in the first line. If you use non-ASCII characters in any string declaration in the first line. If you use non-ASCII characters in any string
value, you need to use Python Unicode strings (like ``project = u'Exposé'``). value, you need to use Python Unicode strings (like ``project = u'Exposé'``).
|more| See :ref:`build-config` for documentation of all available config values. |more| See :doc:`/usage/configuration` for documentation of all available
config values.
.. todo:: Move this entire doc to a different section .. todo:: Move this entire doc to a different section

View File

@ -1,4 +1,4 @@
.. highlightlang:: rst .. highlight:: rst
.. _rst-primer: .. _rst-primer:

View File

@ -1,4 +1,4 @@
.. highlightlang:: rst .. highlight:: rst
=========== ===========
Field Lists Field Lists

View File

@ -26,14 +26,13 @@ from six.moves import cStringIO
import sphinx import sphinx
from sphinx import package_dir, locale from sphinx import package_dir, locale
from sphinx.config import Config from sphinx.config import Config, check_unicode
from sphinx.config import CONFIG_FILENAME # NOQA # for compatibility (RemovedInSphinx30)
from sphinx.deprecation import ( from sphinx.deprecation import (
RemovedInSphinx20Warning, RemovedInSphinx30Warning, RemovedInSphinx40Warning RemovedInSphinx20Warning, RemovedInSphinx30Warning, RemovedInSphinx40Warning
) )
from sphinx.environment import BuildEnvironment from sphinx.environment import BuildEnvironment
from sphinx.errors import ( from sphinx.errors import ApplicationError, ConfigError, VersionRequirementError
ApplicationError, ConfigError, ExtensionError, VersionRequirementError
)
from sphinx.events import EventManager from sphinx.events import EventManager
from sphinx.locale import __ from sphinx.locale import __
from sphinx.registry import SphinxComponentRegistry from sphinx.registry import SphinxComponentRegistry
@ -110,7 +109,6 @@ builtin_extensions = (
'alabaster', 'alabaster',
) # type: Tuple[unicode, ...] ) # type: Tuple[unicode, ...]
CONFIG_FILENAME = 'conf.py'
ENV_PICKLE_FILENAME = 'environment.pickle' ENV_PICKLE_FILENAME = 'environment.pickle'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -189,10 +187,11 @@ class Sphinx(object):
# read config # read config
self.tags = Tags(tags) self.tags = Tags(tags)
self.config = Config(self.confdir, CONFIG_FILENAME, if self.confdir is None:
confoverrides or {}, self.tags) self.config = Config({}, confoverrides or {})
self.config.check_unicode() else:
# defer checking types until i18n has been initialized self.config = Config.read(self.confdir, confoverrides or {}, self.tags)
check_unicode(self.config)
# initialize some limited config variables before initialize i18n and loading # initialize some limited config variables before initialize i18n and loading
# extensions # extensions
@ -250,8 +249,6 @@ class Sphinx(object):
# create the builder # create the builder
self.builder = self.create_builder(buildername) self.builder = self.create_builder(buildername)
# check all configuration values for permissible types
self.config.check_types()
# set up the build environment # set up the build environment
self._init_env(freshenv) self._init_env(freshenv)
# set up the builder # set up the builder
@ -561,8 +558,6 @@ class Sphinx(object):
""" """
logger.debug('[app] adding config value: %r', logger.debug('[app] adding config value: %r',
(name, default, rebuild) + ((types,) if types else ())) # type: ignore (name, default, rebuild) + ((types,) if types else ())) # type: ignore
if name in self.config:
raise ExtensionError(__('Config value %r already present') % name)
if rebuild in (False, True): if rebuild in (False, True):
rebuild = rebuild and 'env' or '' rebuild = rebuild and 'env' or ''
self.config.add(name, default, rebuild, types) self.config.add(name, default, rebuild, types)

View File

@ -9,19 +9,23 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import time
import warnings import warnings
from os import path from os import path
from docutils import nodes from docutils import nodes
from six.moves import cPickle as pickle
from sphinx.deprecation import RemovedInSphinx20Warning from sphinx.deprecation import RemovedInSphinx20Warning
from sphinx.environment import BuildEnvironment from sphinx.environment import BuildEnvironment
from sphinx.environment.adapters.asset import ImageAdapter from sphinx.environment.adapters.asset import ImageAdapter
from sphinx.errors import SphinxError from sphinx.errors import SphinxError
from sphinx.io import read_doc
from sphinx.locale import __ from sphinx.locale import __
from sphinx.util import i18n, import_object, logging, status_iterator from sphinx.util import i18n, import_object, logging, rst, status_iterator
from sphinx.util.build_phase import BuildPhase from sphinx.util.build_phase import BuildPhase
from sphinx.util.console import bold # type: ignore from sphinx.util.console import bold # type: ignore
from sphinx.util.docutils import sphinx_domains
from sphinx.util.i18n import find_catalog from sphinx.util.i18n import find_catalog
from sphinx.util.osutil import SEP, ensuredir, relative_uri, relpath from sphinx.util.osutil import SEP, ensuredir, relative_uri, relpath
from sphinx.util.parallel import ParallelTasks, SerialTasks, make_chunks, \ from sphinx.util.parallel import ParallelTasks, SerialTasks, make_chunks, \
@ -478,7 +482,7 @@ class Builder(object):
# remove all inventory entries for that file # remove all inventory entries for that file
self.app.emit('env-purge-doc', self.env, docname) self.app.emit('env-purge-doc', self.env, docname)
self.env.clear_doc(docname) self.env.clear_doc(docname)
self.env.read_doc(docname, self.app) self.read_doc(docname)
def _read_parallel(self, docnames, nproc): def _read_parallel(self, docnames, nproc):
# type: (List[unicode], int) -> None # type: (List[unicode], int) -> None
@ -491,7 +495,7 @@ class Builder(object):
# type: (List[unicode]) -> unicode # type: (List[unicode]) -> unicode
self.env.app = self.app self.env.app = self.app
for docname in docs: for docname in docs:
self.env.read_doc(docname, self.app) self.read_doc(docname)
# allow pickling self to send it back # allow pickling self to send it back
return BuildEnvironment.dumps(self.env) return BuildEnvironment.dumps(self.env)
@ -511,6 +515,47 @@ class Builder(object):
logger.info(bold('waiting for workers...')) logger.info(bold('waiting for workers...'))
tasks.join() tasks.join()
def read_doc(self, docname):
# type: (unicode) -> None
"""Parse a file and add/update inventory entries for the doctree."""
self.env.prepare_settings(docname)
# Add confdir/docutils.conf to dependencies list if exists
docutilsconf = path.join(self.confdir, 'docutils.conf')
if path.isfile(docutilsconf):
self.env.note_dependency(docutilsconf)
with sphinx_domains(self.env), rst.default_role(docname, self.config.default_role):
doctree = read_doc(self.app, self.env, self.env.doc2path(docname))
# store time of reading, for outdated files detection
# (Some filesystems have coarse timestamp resolution;
# therefore time.time() can be older than filesystem's timestamp.
# For example, FAT32 has 2sec timestamp resolution.)
self.env.all_docs[docname] = max(time.time(),
path.getmtime(self.env.doc2path(docname)))
# cleanup
self.env.temp_data.clear()
self.env.ref_context.clear()
self.write_doctree(docname, doctree)
def write_doctree(self, docname, doctree):
# type: (unicode, nodes.Node) -> None
"""Write the doctree to a file."""
# make it picklable
doctree.reporter = None
doctree.transformer = None
doctree.settings.warning_stream = None
doctree.settings.env = None
doctree.settings.record_dependencies = None
doctree_filename = self.env.doc2path(docname, self.env.doctreedir, '.doctree')
ensuredir(path.dirname(doctree_filename))
with open(doctree_filename, 'wb') as f:
pickle.dump(doctree, f, pickle.HIGHEST_PROTOCOL)
def write(self, build_docnames, updated_docnames, method='update'): def write(self, build_docnames, updated_docnames, method='update'):
# type: (Iterable[unicode], Sequence[unicode], unicode) -> None # type: (Iterable[unicode], Sequence[unicode], unicode) -> None
if build_docnames is None or build_docnames == ['__all__']: if build_docnames is None or build_docnames == ['__all__']:

View File

@ -355,7 +355,7 @@ class StandaloneHTMLBuilder(Builder):
continue continue
if '://' not in filename: if '://' not in filename:
filename = path.join('_static', filename) filename = posixpath.join('_static', filename)
self.css_files.append(Stylesheet(filename, **attrs)) # type: ignore self.css_files.append(Stylesheet(filename, **attrs)) # type: ignore

View File

@ -323,7 +323,7 @@ def setup(app):
app.add_config_value('latex_appendices', [], None) app.add_config_value('latex_appendices', [], None)
app.add_config_value('latex_use_latex_multicolumn', False, None) app.add_config_value('latex_use_latex_multicolumn', False, None)
app.add_config_value('latex_toplevel_sectioning', None, None, app.add_config_value('latex_toplevel_sectioning', None, None,
ENUM('part', 'chapter', 'section')) ENUM(None, 'part', 'chapter', 'section'))
app.add_config_value('latex_domain_indices', True, None, [list]) app.add_config_value('latex_domain_indices', True, None, [list])
app.add_config_value('latex_show_urls', 'no', None) app.add_config_value('latex_show_urls', 'no', None)
app.add_config_value('latex_show_pagerefs', False, None) app.add_config_value('latex_show_pagerefs', False, None)

View File

@ -269,7 +269,7 @@ class QtHelpBuilder(StandaloneHTMLBuilder):
resourcedir = root.startswith((staticdir, imagesdir)) resourcedir = root.startswith((staticdir, imagesdir))
for fn in sorted(files): for fn in sorted(files):
if (resourcedir and not fn.endswith('.js')) or fn.endswith('.html'): if (resourcedir and not fn.endswith('.js')) or fn.endswith('.html'):
filename = path.join(root, fn)[olen:] filename = posixpath.join(root, fn)[olen:]
project_files.append(filename) project_files.append(filename)
return project_files return project_files

View File

@ -293,7 +293,8 @@ def build_main(argv=sys.argv[1:]): # type: ignore
app = None app = None
try: try:
with patch_docutils(), docutils_namespace(): confdir = args.confdir or args.sourcedir
with patch_docutils(confdir), docutils_namespace():
app = Sphinx(args.sourcedir, args.confdir, args.outputdir, app = Sphinx(args.sourcedir, args.confdir, args.outputdir,
args.doctreedir, args.builder, confoverrides, status, args.doctreedir, args.builder, confoverrides, status,
warning, args.freshenv, args.warningiserror, warning, args.freshenv, args.warningiserror,

View File

@ -11,13 +11,15 @@
import re import re
import traceback import traceback
import warnings
from collections import OrderedDict from collections import OrderedDict
from os import path, getenv from os import path, getenv
from typing import Any, NamedTuple, Union from typing import Any, NamedTuple, Union
from six import PY2, PY3, iteritems, string_types, binary_type, text_type, integer_types from six import PY2, PY3, iteritems, string_types, binary_type, text_type, integer_types
from sphinx.errors import ConfigError from sphinx.deprecation import RemovedInSphinx30Warning
from sphinx.errors import ConfigError, ExtensionError
from sphinx.locale import _, __ from sphinx.locale import _, __
from sphinx.util import logging from sphinx.util import logging
from sphinx.util.i18n import format_date from sphinx.util.i18n import format_date
@ -26,28 +28,15 @@ from sphinx.util.pycompat import execfile_, NoneType
if False: if False:
# For type annotation # For type annotation
from typing import Any, Callable, Dict, Iterable, Iterator, List, Tuple, Union # NOQA from typing import Any, Callable, Dict, Generator, Iterator, List, Tuple, Union # NOQA
from sphinx.application import Sphinx # NOQA from sphinx.application import Sphinx # NOQA
from sphinx.util.tags import Tags # NOQA from sphinx.util.tags import Tags # NOQA
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
nonascii_re = re.compile(br'[\x80-\xff]') CONFIG_FILENAME = 'conf.py'
copyright_year_re = re.compile(r'^((\d{4}-)?)(\d{4})(?=[ ,])') copyright_year_re = re.compile(r'^((\d{4}-)?)(\d{4})(?=[ ,])')
CONFIG_SYNTAX_ERROR = __("There is a syntax error in your configuration file: %s")
if PY3:
CONFIG_SYNTAX_ERROR += __("\nDid you change the syntax from 2.x to 3.x?")
CONFIG_ERROR = __("There is a programable error in your configuration file:\n\n%s")
CONFIG_EXIT_ERROR = __("The configuration file (or one of the modules it imports) "
"called sys.exit()")
CONFIG_ENUM_WARNING = __("The config value `{name}` has to be a one of {candidates}, "
"but `{current}` is given.")
CONFIG_PERMITTED_TYPE_WARNING = __("The config value `{name}' has type `{current.__name__}', "
"expected to {permitted}.")
CONFIG_TYPE_WARNING = __("The config value `{name}' has type `{current.__name__}', "
"defaults to `{default.__name__}'.")
if PY3: if PY3:
unicode = str # special alias for static typing... unicode = str # special alias for static typing...
@ -155,30 +144,30 @@ class Config(object):
'env'), 'env'),
) # type: Dict[unicode, Tuple] ) # type: Dict[unicode, Tuple]
def __init__(self, dirname, filename, overrides, tags): def __init__(self, *args):
# type: (unicode, unicode, Dict, Tags) -> None # type: (Any) -> None
if len(args) == 4:
# old style arguments: (dirname, filename, overrides, tags)
warnings.warn('The argument of Config() class has been changed. '
'Use Config.read() to read configuration from conf.py.',
RemovedInSphinx30Warning)
dirname, filename, overrides, tags = args
if dirname is None:
config = {} # type: Dict[unicode, Any]
else:
config = eval_config_file(path.join(dirname, filename), tags)
else:
# new style arguments: (config={}, overrides={})
if len(args) == 0:
config, overrides = {}, {}
elif len(args) == 1:
config, overrides = args[0], {}
else:
config, overrides = args[:2]
self.overrides = overrides self.overrides = overrides
self.values = Config.config_values.copy() self.values = Config.config_values.copy()
config = {} # type: Dict[unicode, Any]
if dirname is not None:
config_file = path.join(dirname, filename)
config['__file__'] = config_file
config['tags'] = tags
with cd(dirname):
# we promise to have the config dir as current dir while the
# config file is executed
try:
execfile_(filename, config)
except SyntaxError as err:
raise ConfigError(CONFIG_SYNTAX_ERROR % err)
except SystemExit:
raise ConfigError(CONFIG_EXIT_ERROR)
except Exception:
raise ConfigError(CONFIG_ERROR % traceback.format_exc())
self._raw_config = config self._raw_config = config
# these two must be preinitialized because extensions can add their
# own config values
self.setup = config.get('setup', None) # type: Callable self.setup = config.get('setup', None) # type: Callable
if 'extensions' in overrides: if 'extensions' in overrides:
@ -188,69 +177,25 @@ class Config(object):
config['extensions'] = overrides.pop('extensions') config['extensions'] = overrides.pop('extensions')
self.extensions = config.get('extensions', []) # type: List[unicode] self.extensions = config.get('extensions', []) # type: List[unicode]
# correct values of copyright year that are not coherent with @classmethod
# the SOURCE_DATE_EPOCH environment variable (if set) def read(cls, confdir, overrides=None, tags=None):
# See https://reproducible-builds.org/specs/source-date-epoch/ # type: (unicode, Dict, Tags) -> Config
if getenv('SOURCE_DATE_EPOCH') is not None: """Create a Config object from configuration file."""
for k in ('copyright', 'epub_copyright'): filename = path.join(confdir, CONFIG_FILENAME)
if k in config: namespace = eval_config_file(filename, tags)
config[k] = copyright_year_re.sub(r'\g<1>%s' % format_date('%Y'), return cls(namespace, overrides or {})
config[k])
def check_types(self): def check_types(self):
# type: () -> None # type: () -> None
# check all values for deviation from the default value's type, since warnings.warn('Config.check_types() is deprecated. Use check_confval_types() instead.',
# that can result in TypeErrors all over the place RemovedInSphinx30Warning)
# NB. since config values might use _() we have to wait with calling check_confval_types(None, self)
# this method until i18n is initialized
for name in self._raw_config:
if name not in self.values:
continue # we don't know a default value
settings = self.values[name]
default, dummy_rebuild = settings[:2]
permitted = settings[2] if len(settings) == 3 else ()
if hasattr(default, '__call__'):
default = default(self) # could invoke _()
if default is None and not permitted:
continue # neither inferrable nor expliclitly permitted types
current = self[name]
if permitted is Any:
# any type of value is accepted
pass
elif isinstance(permitted, ENUM):
if not permitted.match(current):
logger.warning(CONFIG_ENUM_WARNING.format(
name=name, current=current, candidates=permitted.candidates))
else:
if type(current) is type(default):
continue
if type(current) in permitted:
continue
common_bases = (set(type(current).__bases__ + (type(current),)) &
set(type(default).__bases__))
common_bases.discard(object)
if common_bases:
continue # at least we share a non-trivial base class
if permitted:
logger.warning(CONFIG_PERMITTED_TYPE_WARNING.format(
name=name, current=type(current),
permitted=str([cls.__name__ for cls in permitted])))
else:
logger.warning(CONFIG_TYPE_WARNING.format(
name=name, current=type(current), default=type(default)))
def check_unicode(self): def check_unicode(self):
# type: () -> None # type: () -> None
# check all string values for non-ASCII characters in bytestrings, warnings.warn('Config.check_unicode() is deprecated. Use check_unicode() instead.',
# since that can result in UnicodeErrors all over the place RemovedInSphinx30Warning)
for name, value in iteritems(self._raw_config): check_unicode(self)
if isinstance(value, binary_type) and nonascii_re.search(value):
logger.warning(__('the config value %r is set to a string with non-ASCII '
'characters; this can lead to Unicode errors occurring. '
'Please use Unicode strings, e.g. %r.'), name, u'Content')
def convert_overrides(self, name, value): def convert_overrides(self, name, value):
# type: (unicode, Any) -> Any # type: (unicode, Any) -> Any
@ -346,19 +291,49 @@ class Config(object):
return name in self.values return name in self.values
def __iter__(self): def __iter__(self):
# type: () -> Iterable[ConfigValue] # type: () -> Generator[ConfigValue, None, None]
for name, value in iteritems(self.values): for name, value in iteritems(self.values):
yield ConfigValue(name, getattr(self, name), value[1]) # type: ignore yield ConfigValue(name, getattr(self, name), value[1]) # type: ignore
def add(self, name, default, rebuild, types): def add(self, name, default, rebuild, types):
# type: (unicode, Any, Union[bool, unicode], Any) -> None # type: (unicode, Any, Union[bool, unicode], Any) -> None
self.values[name] = (default, rebuild, types) if name in self.values:
raise ExtensionError(__('Config value %r already present') % name)
else:
self.values[name] = (default, rebuild, types)
def filter(self, rebuild): def filter(self, rebuild):
# type: (Union[unicode, List[unicode]]) -> Iterator[ConfigValue] # type: (Union[unicode, List[unicode]]) -> Iterator[ConfigValue]
if isinstance(rebuild, string_types): if isinstance(rebuild, string_types):
rebuild = [rebuild] rebuild = [rebuild]
return (value for value in self if value.rebuild in rebuild) # type: ignore return (value for value in self if value.rebuild in rebuild)
def eval_config_file(filename, tags):
# type: (unicode, Tags) -> Dict[unicode, Any]
"""Evaluate a config file."""
namespace = {} # type: Dict[unicode, Any]
namespace['__file__'] = filename
namespace['tags'] = tags
with cd(path.dirname(filename)):
# during executing config file, current dir is changed to ``confdir``.
try:
execfile_(filename, namespace)
except SyntaxError as err:
msg = __("There is a syntax error in your configuration file: %s")
if PY3:
msg += __("\nDid you change the syntax from 2.x to 3.x?")
raise ConfigError(msg % err)
except SystemExit:
msg = __("The configuration file (or one of the modules it imports) "
"called sys.exit()")
raise ConfigError(msg)
except Exception:
msg = __("There is a programable error in your configuration file:\n\n%s")
raise ConfigError(msg % traceback.format_exc())
return namespace
def convert_source_suffix(app, config): def convert_source_suffix(app, config):
@ -400,10 +375,91 @@ def init_numfig_format(app, config):
config.numfig_format = numfig_format # type: ignore config.numfig_format = numfig_format # type: ignore
def correct_copyright_year(app, config):
# type: (Sphinx, Config) -> None
"""correct values of copyright year that are not coherent with
the SOURCE_DATE_EPOCH environment variable (if set)
See https://reproducible-builds.org/specs/source-date-epoch/
"""
if getenv('SOURCE_DATE_EPOCH') is not None:
for k in ('copyright', 'epub_copyright'):
if k in config:
replace = r'\g<1>%s' % format_date('%Y')
config[k] = copyright_year_re.sub(replace, config[k]) # type: ignore
def check_confval_types(app, config):
# type: (Sphinx, Config) -> None
"""check all values for deviation from the default value's type, since
that can result in TypeErrors all over the place NB.
"""
for confval in config:
settings = config.values[confval.name]
default = settings[0]
annotations = settings[2] if len(settings) == 3 else ()
if hasattr(default, '__call__'):
default = default(config) # evaluate default value
if default is None and not annotations:
continue # neither inferrable nor expliclitly annotated types
if annotations is Any:
# any type of value is accepted
pass
elif isinstance(annotations, ENUM):
if not annotations.match(confval.value):
msg = __("The config value `{name}` has to be a one of {candidates}, "
"but `{current}` is given.")
logger.warning(msg.format(name=confval.name,
current=confval.value,
candidates=annotations.candidates))
else:
if type(confval.value) is type(default):
continue
if type(confval.value) in annotations:
continue
common_bases = (set(type(confval.value).__bases__ + (type(confval.value),)) &
set(type(default).__bases__))
common_bases.discard(object)
if common_bases:
continue # at least we share a non-trivial base class
if annotations:
msg = __("The config value `{name}' has type `{current.__name__}', "
"expected to {permitted}.")
logger.warning(msg.format(name=confval.name,
current=type(confval.value),
permitted=str([c.__name__ for c in annotations])))
else:
msg = __("The config value `{name}' has type `{current.__name__}', "
"defaults to `{default.__name__}'.")
logger.warning(msg.format(name=confval.name,
current=type(confval.value),
default=type(default)))
def check_unicode(config):
# type: (Config) -> None
"""check all string values for non-ASCII characters in bytestrings,
since that can result in UnicodeErrors all over the place
"""
nonascii_re = re.compile(br'[\x80-\xff]')
for name, value in iteritems(config._raw_config):
if isinstance(value, binary_type) and nonascii_re.search(value):
logger.warning(__('the config value %r is set to a string with non-ASCII '
'characters; this can lead to Unicode errors occurring. '
'Please use Unicode strings, e.g. %r.'), name, u'Content')
def setup(app): def setup(app):
# type: (Sphinx) -> Dict[unicode, Any] # type: (Sphinx) -> Dict[unicode, Any]
app.connect('config-inited', convert_source_suffix) app.connect('config-inited', convert_source_suffix)
app.connect('config-inited', init_numfig_format) app.connect('config-inited', init_numfig_format)
app.connect('config-inited', correct_copyright_year)
app.connect('config-inited', check_confval_types)
return { return {
'version': 'builtin', 'version': 'builtin',

View File

@ -9,6 +9,7 @@
import codecs import codecs
import sys import sys
import warnings
from difflib import unified_diff from difflib import unified_diff
from docutils import nodes from docutils import nodes
@ -17,6 +18,7 @@ from docutils.statemachine import ViewList
from six import text_type from six import text_type
from sphinx import addnodes from sphinx import addnodes
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.locale import __ from sphinx.locale import __
from sphinx.util import logging from sphinx.util import logging
from sphinx.util import parselinenos from sphinx.util import parselinenos
@ -59,6 +61,17 @@ class Highlight(SphinxDirective):
linenothreshold=linenothreshold)] linenothreshold=linenothreshold)]
class HighlightLang(Highlight):
"""highlightlang directive (deprecated)"""
def run(self):
# type: () -> List[nodes.Node]
warnings.warn('highlightlang directive is deprecated. '
'Please use highlight directive instead.',
RemovedInSphinx40Warning)
return Highlight.run(self)
def dedent_lines(lines, dedent, location=None): def dedent_lines(lines, dedent, location=None):
# type: (List[unicode], int, Any) -> List[unicode] # type: (List[unicode], int, Any) -> List[unicode]
if not dedent: if not dedent:
@ -462,7 +475,7 @@ class LiteralInclude(SphinxDirective):
def setup(app): def setup(app):
# type: (Sphinx) -> Dict[unicode, Any] # type: (Sphinx) -> Dict[unicode, Any]
directives.register_directive('highlight', Highlight) directives.register_directive('highlight', Highlight)
directives.register_directive('highlightlang', Highlight) # old directives.register_directive('highlightlang', HighlightLang)
directives.register_directive('code-block', CodeBlock) directives.register_directive('code-block', CodeBlock)
directives.register_directive('sourcecode', CodeBlock) directives.register_directive('sourcecode', CodeBlock)
directives.register_directive('literalinclude', LiteralInclude) directives.register_directive('literalinclude', LiteralInclude)

View File

@ -12,14 +12,12 @@
import os import os
import re import re
import sys import sys
import time
import types import types
import warnings import warnings
from collections import defaultdict from collections import defaultdict
from copy import copy from copy import copy
from os import path from os import path
from docutils.frontend import OptionParser
from docutils.utils import Reporter, get_source_line from docutils.utils import Reporter, get_source_line
from six import BytesIO, class_types, next from six import BytesIO, class_types, next
from six.moves import cPickle as pickle from six.moves import cPickle as pickle
@ -29,16 +27,15 @@ from sphinx.deprecation import RemovedInSphinx20Warning, RemovedInSphinx30Warnin
from sphinx.environment.adapters.indexentries import IndexEntries from sphinx.environment.adapters.indexentries import IndexEntries
from sphinx.environment.adapters.toctree import TocTree from sphinx.environment.adapters.toctree import TocTree
from sphinx.errors import SphinxError, ExtensionError from sphinx.errors import SphinxError, ExtensionError
from sphinx.io import read_doc
from sphinx.locale import __ from sphinx.locale import __
from sphinx.transforms import SphinxTransformer from sphinx.transforms import SphinxTransformer
from sphinx.util import get_matching_docs, FilenameUniqDict from sphinx.util import get_matching_docs, FilenameUniqDict
from sphinx.util import logging, rst from sphinx.util import logging
from sphinx.util.docutils import sphinx_domains, WarningStream from sphinx.util.docutils import WarningStream
from sphinx.util.i18n import find_catalog_files from sphinx.util.i18n import find_catalog_files
from sphinx.util.matching import compile_matchers from sphinx.util.matching import compile_matchers
from sphinx.util.nodes import is_translatable from sphinx.util.nodes import is_translatable
from sphinx.util.osutil import SEP, ensuredir, relpath from sphinx.util.osutil import SEP, relpath
from sphinx.util.websupport import is_commentable from sphinx.util.websupport import is_commentable
if False: if False:
@ -556,33 +553,6 @@ class BuildEnvironment(object):
self.temp_data['default_domain'] = \ self.temp_data['default_domain'] = \
self.domains.get(self.config.primary_domain) self.domains.get(self.config.primary_domain)
def read_doc(self, docname, app=None):
# type: (unicode, Sphinx) -> None
"""Parse a file and add/update inventory entries for the doctree."""
self.prepare_settings(docname)
docutilsconf = path.join(self.srcdir, 'docutils.conf')
# read docutils.conf from source dir, not from current dir
OptionParser.standard_config_files[1] = docutilsconf
if path.isfile(docutilsconf):
self.note_dependency(docutilsconf)
with sphinx_domains(self), rst.default_role(docname, self.config.default_role):
doctree = read_doc(self.app, self, self.doc2path(docname))
# store time of reading, for outdated files detection
# (Some filesystems have coarse timestamp resolution;
# therefore time.time() can be older than filesystem's timestamp.
# For example, FAT32 has 2sec timestamp resolution.)
self.all_docs[docname] = max(
time.time(), path.getmtime(self.doc2path(docname)))
# cleanup
self.temp_data.clear()
self.ref_context.clear()
self.write_doctree(docname, doctree)
# utilities to use while reading a document # utilities to use while reading a document
@property @property
@ -685,21 +655,6 @@ class BuildEnvironment(object):
doctree.reporter = Reporter(self.doc2path(docname), 2, 5, stream=WarningStream()) doctree.reporter = Reporter(self.doc2path(docname), 2, 5, stream=WarningStream())
return doctree return doctree
def write_doctree(self, docname, doctree):
# type: (unicode, nodes.Node) -> None
"""Write the doctree to a file."""
# make it picklable
doctree.reporter = None
doctree.transformer = None
doctree.settings.warning_stream = None
doctree.settings.env = None
doctree.settings.record_dependencies = None
doctree_filename = self.doc2path(docname, self.doctreedir, '.doctree')
ensuredir(path.dirname(doctree_filename))
with open(doctree_filename, 'wb') as f:
pickle.dump(doctree, f, pickle.HIGHEST_PROTOCOL)
def get_and_resolve_doctree(self, docname, builder, doctree=None, def get_and_resolve_doctree(self, docname, builder, doctree=None,
prune_toctrees=True, includehidden=False): prune_toctrees=True, includehidden=False):
# type: (unicode, Builder, nodes.Node, bool, bool) -> nodes.Node # type: (unicode, Builder, nodes.Node, bool, bool) -> nodes.Node
@ -849,6 +804,19 @@ class BuildEnvironment(object):
RemovedInSphinx30Warning) RemovedInSphinx30Warning)
return self.app.builder._read_parallel(docnames, nproc) return self.app.builder._read_parallel(docnames, nproc)
def read_doc(self, docname, app=None):
# type: (unicode, Sphinx) -> None
warnings.warn('env.read_doc() is deprecated. Please use builder.read_doc() instead.',
RemovedInSphinx30Warning)
self.app.builder.read_doc(docname)
def write_doctree(self, docname, doctree):
# type: (unicode, nodes.Node) -> None
warnings.warn('env.write_doctree() is deprecated. '
'Please use builder.write_doctree() instead.',
RemovedInSphinx30Warning)
self.app.builder.write_doctree(docname, doctree)
@property @property
def _nitpick_ignore(self): def _nitpick_ignore(self):
# type: () -> List[unicode] # type: () -> List[unicode]

View File

@ -20,8 +20,8 @@ from docutils.statemachine import ViewList
from six import iteritems, itervalues, text_type, class_types, string_types from six import iteritems, itervalues, text_type, class_types, string_types
import sphinx import sphinx
from sphinx.application import ExtensionError
from sphinx.deprecation import RemovedInSphinx20Warning from sphinx.deprecation import RemovedInSphinx20Warning
from sphinx.errors import ExtensionError
from sphinx.ext.autodoc.importer import mock, import_object, get_object_members from sphinx.ext.autodoc.importer import mock, import_object, get_object_members
from sphinx.ext.autodoc.importer import _MockImporter # to keep compatibility # NOQA from sphinx.ext.autodoc.importer import _MockImporter # to keep compatibility # NOQA
from sphinx.ext.autodoc.inspector import format_annotation, formatargspec # to keep compatibility # NOQA from sphinx.ext.autodoc.inspector import format_annotation, formatargspec # to keep compatibility # NOQA

View File

@ -290,21 +290,27 @@ def render_dot_html(self, node, code, options, prefix='graphviz',
self.body.append('<div align="%s" class="align-%s">' % self.body.append('<div align="%s" class="align-%s">' %
(node['align'], node['align'])) (node['align'], node['align']))
if format == 'svg': if format == 'svg':
svgtag = '''<object data="%s" type="image/svg+xml"> self.body.append('<div class="graphviz">')
<p class="warning">%s</p></object>\n''' % (fname, alt) self.body.append('<object data="%s" type="image/svg+xml" %s>\n' %
self.body.append(svgtag) (fname, imgcss))
self.body.append('<p class="warning">%s</p>' % alt)
self.body.append('</object></div>\n')
else: else:
with codecs.open(outfn + '.map', 'r', encoding='utf-8') as mapfile: # type: ignore with codecs.open(outfn + '.map', 'r', encoding='utf-8') as mapfile: # type: ignore
imgmap = ClickableMapDefinition(outfn + '.map', mapfile.read(), dot=code) imgmap = ClickableMapDefinition(outfn + '.map', mapfile.read(), dot=code)
if imgmap.clickable: if imgmap.clickable:
# has a map # has a map
self.body.append('<img src="%s" alt="%s" usemap="#%s" %s/>\n' % self.body.append('<div class="graphviz">')
self.body.append('<img src="%s" alt="%s" usemap="#%s" %s/>' %
(fname, alt, imgmap.id, imgcss)) (fname, alt, imgmap.id, imgcss))
self.body.append('</div>\n')
self.body.append(imgmap.generate_clickable_map()) self.body.append(imgmap.generate_clickable_map())
else: else:
# nothing in image map # nothing in image map
self.body.append('<img src="%s" alt="%s" %s/>\n' % self.body.append('<div class="graphviz">')
self.body.append('<img src="%s" alt="%s" %s/>' %
(fname, alt, imgcss)) (fname, alt, imgcss))
self.body.append('</div>\n')
if 'align' in node: if 'align' in node:
self.body.append('</div>\n') self.body.append('</div>\n')

View File

@ -57,7 +57,7 @@ class IfConfig(Directive):
def process_ifconfig_nodes(app, doctree, docname): def process_ifconfig_nodes(app, doctree, docname):
# type: (Sphinx, nodes.Node, unicode) -> None # type: (Sphinx, nodes.Node, unicode) -> None
ns = dict((confval.name, confval.value) for confval in app.config) # type: ignore ns = dict((confval.name, confval.value) for confval in app.config)
ns.update(app.config.__dict__.copy()) ns.update(app.config.__dict__.copy())
ns['builder'] = app.builder.name ns['builder'] = app.builder.name
for node in doctree.traverse(ifconfig): for node in doctree.traverse(ifconfig):

View File

@ -13,7 +13,7 @@
from docutils import nodes from docutils import nodes
import sphinx import sphinx
from sphinx.application import ExtensionError from sphinx.errors import ExtensionError
from sphinx.ext.mathbase import get_node_equation_number from sphinx.ext.mathbase import get_node_equation_number
from sphinx.ext.mathbase import setup_math as mathbase_setup from sphinx.ext.mathbase import setup_math as mathbase_setup
from sphinx.locale import _ from sphinx.locale import _

View File

@ -20,6 +20,7 @@
import os import os
import re import re
import sys import sys
import warnings
from six import iteritems, PY3 from six import iteritems, PY3
@ -35,6 +36,7 @@ try:
except ImportError: except ImportError:
janome_module = False janome_module = False
from sphinx.deprecation import RemovedInSphinx30Warning
from sphinx.errors import SphinxError, ExtensionError from sphinx.errors import SphinxError, ExtensionError
from sphinx.search import SearchLanguage from sphinx.search import SearchLanguage
from sphinx.util import import_object from sphinx.util import import_object
@ -556,9 +558,12 @@ class SearchJapanese(SearchLanguage):
def init(self, options): def init(self, options):
# type: (Dict) -> None # type: (Dict) -> None
type = options.get('type', 'default') type = options.get('type', 'sphinx.search.ja.DefaultSplitter')
if type in self.splitters: if type in self.splitters:
dotted_path = self.splitters[type] dotted_path = self.splitters[type]
warnings.warn('html_search_options["type"]: %s is deprecated. '
'Please give "%s" instead.' % (type, dotted_path),
RemovedInSphinx30Warning)
else: else:
dotted_path = type dotted_path = type
try: try:

View File

@ -179,7 +179,8 @@ class BuildDoc(Command):
app = None app = None
try: try:
with patch_docutils(), docutils_namespace(): confdir = self.config_dir or self.source_dir
with patch_docutils(confdir), docutils_namespace():
app = Sphinx(self.source_dir, self.config_dir, app = Sphinx(self.source_dir, self.config_dir,
builder_target_dir, self.doctree_dir, builder_target_dir, self.doctree_dir,
builder, confoverrides, status_stream, builder, confoverrides, status_stream,

View File

@ -74,7 +74,7 @@ language = {{ language | repr }}
# List of patterns, relative to source directory, that match files and # List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files. # directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path . # This pattern also affects html_static_path and html_extra_path.
exclude_patterns = [{{ exclude_patterns }}] exclude_patterns = [{{ exclude_patterns }}]
# The name of the Pygments (syntax highlighting) style to use. # The name of the Pygments (syntax highlighting) style to use.

View File

@ -10,16 +10,17 @@
""" """
from __future__ import absolute_import from __future__ import absolute_import
import os
import re import re
import types import types
import warnings import warnings
from contextlib import contextmanager from contextlib import contextmanager
from copy import copy from copy import copy
from distutils.version import LooseVersion from distutils.version import LooseVersion
from os import path
import docutils import docutils
from docutils import nodes from docutils import nodes
from docutils.languages import get_language
from docutils.parsers.rst import Directive, directives, roles, convert_directive_function from docutils.parsers.rst import Directive, directives, roles, convert_directive_function
from docutils.statemachine import StateMachine from docutils.statemachine import StateMachine
from docutils.utils import Reporter from docutils.utils import Reporter
@ -34,7 +35,7 @@ report_re = re.compile('^(.+?:(?:\\d+)?): \\((DEBUG|INFO|WARNING|ERROR|SEVERE)/(
if False: if False:
# For type annotation # For type annotation
from typing import Any, Callable, Generator, Iterator, List, Set, Tuple # NOQA from typing import Any, Callable, Generator, List, Set, Tuple # NOQA
from docutils.statemachine import State, ViewList # NOQA from docutils.statemachine import State, ViewList # NOQA
from sphinx.config import Config # NOQA from sphinx.config import Config # NOQA
from sphinx.environment import BuildEnvironment # NOQA from sphinx.environment import BuildEnvironment # NOQA
@ -47,7 +48,7 @@ additional_nodes = set() # type: Set[nodes.Node]
@contextmanager @contextmanager
def docutils_namespace(): def docutils_namespace():
# type: () -> Iterator[None] # type: () -> Generator[None, None, None]
"""Create namespace for reST parsers.""" """Create namespace for reST parsers."""
try: try:
_directives = copy(directives._directives) _directives = copy(directives._directives)
@ -94,29 +95,53 @@ def unregister_node(node):
delattr(nodes.SparseNodeVisitor, 'depart_' + node.__name__) delattr(nodes.SparseNodeVisitor, 'depart_' + node.__name__)
def patched_get_language(language_code, reporter=None): @contextmanager
# type: (unicode, Reporter) -> Any def patched_get_language():
"""A wrapper for docutils.languages.get_language(). # type: () -> Generator[None, None, None]
"""Patch docutils.languages.get_language() temporarily.
This ignores the second argument ``reporter`` to suppress warnings. This ignores the second argument ``reporter`` to suppress warnings.
refs: https://github.com/sphinx-doc/sphinx/issues/3788 refs: https://github.com/sphinx-doc/sphinx/issues/3788
""" """
return get_language(language_code) from docutils.languages import get_language
def patched_get_language(language_code, reporter=None):
# type: (unicode, Reporter) -> Any
return get_language(language_code)
@contextmanager
def patch_docutils():
# type: () -> Iterator[None]
"""Patch to docutils temporarily."""
try: try:
docutils.languages.get_language = patched_get_language docutils.languages.get_language = patched_get_language
yield yield
finally: finally:
# restore original implementations # restore original implementations
docutils.languages.get_language = get_language docutils.languages.get_language = get_language
@contextmanager
def using_user_docutils_conf(confdir):
# type: (unicode) -> Generator[None, None, None]
"""Let docutils know the location of ``docutils.conf`` for Sphinx."""
try:
docutilsconfig = os.environ.get('DOCUTILSCONFIG', None)
if confdir:
os.environ['DOCUTILSCONFIG'] = path.join(path.abspath(confdir), 'docutils.conf') # type: ignore # NOQA
yield
finally:
if docutilsconfig is None:
os.environ.pop('DOCUTILSCONFIG')
else:
os.environ['DOCUTILSCONFIG'] = docutilsconfig
@contextmanager
def patch_docutils(confdir=None):
# type: (unicode) -> Generator[None, None, None]
"""Patch to docutils temporarily."""
with patched_get_language(), using_user_docutils_conf(confdir):
yield
class ElementLookupError(Exception): class ElementLookupError(Exception):
pass pass
@ -257,7 +282,7 @@ def directive_helper(obj, has_content=None, argument_spec=None, **option_spec):
@contextmanager @contextmanager
def switch_source_input(state, content): def switch_source_input(state, content):
# type: (State, ViewList) -> Generator # type: (State, ViewList) -> Generator[None, None, None]
"""Switch current source input of state temporarily.""" """Switch current source input of state temporarily."""
try: try:
# remember the original ``get_source_and_line()`` method # remember the original ``get_source_and_line()`` method

View File

@ -74,6 +74,10 @@ def copy_asset(source, destination, excluded=lambda path: False, context=None, r
if not os.path.exists(source): if not os.path.exists(source):
return return
if renderer is None:
from sphinx.util.template import SphinxRenderer
renderer = SphinxRenderer()
ensuredir(destination) ensuredir(destination)
if os.path.isfile(source): if os.path.isfile(source):
copy_asset_file(source, destination, context, renderer) copy_asset_file(source, destination, context, renderer)

View File

@ -17,14 +17,14 @@ from contextlib import contextmanager
from docutils import nodes from docutils import nodes
from docutils.utils import get_source_line from docutils.utils import get_source_line
from six import PY2, StringIO from six import PY2, StringIO, text_type
from sphinx.errors import SphinxWarning from sphinx.errors import SphinxWarning
from sphinx.util.console import colorize from sphinx.util.console import colorize
if False: if False:
# For type annotation # For type annotation
from typing import Any, Dict, Generator, IO, List, Tuple, Union # NOQA from typing import Any, Dict, Generator, IO, List, Tuple, Union, Optional # NOQA
from docutils import nodes # NOQA from docutils import nodes # NOQA
from sphinx.application import Sphinx # NOQA from sphinx.application import Sphinx # NOQA
@ -94,6 +94,36 @@ def convert_serializable(records):
r.location = get_node_location(location) # type: ignore r.location = get_node_location(location) # type: ignore
def get_full_module_name(node):
# type: (nodes.Node) -> str
"""
return full module dotted path like: 'docutils.nodes.paragraph'
:param nodes.Node node: target node
:return: full module dotted path
"""
return '{}.{}'.format(node.__module__, node.__class__.__name__)
def repr_domxml(node, length=80):
# type: (nodes.Node, Optional[int]) -> unicode
"""
return DOM XML representation of the specified node like:
'<paragraph translatable="False"><inline classes="versionmodified">New in version...'
:param nodes.Node node: target node
:param int length:
length of return value to be striped. if false-value is specified, repr_domxml
returns full of DOM XML representation.
:return: DOM XML representation
"""
# text = node.asdom().toxml() # #4919 crush if node has secnumber with tuple value
text = text_type(node) # workaround for #4919
if length and len(text) > length:
text = text[:length] + '...'
return text
class SphinxWarningLogRecord(logging.LogRecord): class SphinxWarningLogRecord(logging.LogRecord):
"""Log record class supporting location""" """Log record class supporting location"""
location = None # type: Any location = None # type: Any

View File

@ -21,7 +21,7 @@ from sphinx.util import logging
if False: if False:
# For type annotation # For type annotation
from typing import Any, Callable, Iterable, List, Set, Tuple, Union # NOQA from typing import Any, Callable, Iterable, List, Set, Tuple # NOQA
from sphinx.builders import Builder # NOQA from sphinx.builders import Builder # NOQA
from sphinx.utils.tags import Tags # NOQA from sphinx.utils.tags import Tags # NOQA
@ -41,13 +41,19 @@ def apply_source_workaround(node):
# * rawsource of term node will have: ``term text : classifier1 : classifier2`` # * rawsource of term node will have: ``term text : classifier1 : classifier2``
# * rawsource of classifier node will be None # * rawsource of classifier node will be None
if isinstance(node, nodes.classifier) and not node.rawsource: if isinstance(node, nodes.classifier) and not node.rawsource:
logger.debug('[i18n] PATCH: %r to have source, line and rawsource: %s',
logging.get_full_module_name(node), logging.repr_domxml(node))
definition_list_item = node.parent definition_list_item = node.parent
node.source = definition_list_item.source node.source = definition_list_item.source
node.line = definition_list_item.line - 1 node.line = definition_list_item.line - 1
node.rawsource = node.astext() # set 'classifier1' (or 'classifier2') node.rawsource = node.astext() # set 'classifier1' (or 'classifier2')
if isinstance(node, nodes.image) and node.source is None: if isinstance(node, nodes.image) and node.source is None:
logger.debug('[i18n] PATCH: %r to have source, line: %s',
logging.get_full_module_name(node), logging.repr_domxml(node))
node.source, node.line = node.parent.source, node.parent.line node.source, node.line = node.parent.source, node.parent.line
if isinstance(node, nodes.term): if isinstance(node, nodes.term):
logger.debug('[i18n] PATCH: %r to have rawsource: %s',
logging.get_full_module_name(node), logging.repr_domxml(node))
# strip classifier from rawsource of term # strip classifier from rawsource of term
for classifier in reversed(node.parent.traverse(nodes.classifier)): for classifier in reversed(node.parent.traverse(nodes.classifier)):
node.rawsource = re.sub(r'\s*:\s*%s' % re.escape(classifier.astext()), node.rawsource = re.sub(r'\s*:\s*%s' % re.escape(classifier.astext()),
@ -67,6 +73,8 @@ def apply_source_workaround(node):
nodes.image, # #3093 image directive in substitution nodes.image, # #3093 image directive in substitution
nodes.field_name, # #3335 field list syntax nodes.field_name, # #3335 field list syntax
))): ))):
logger.debug('[i18n] PATCH: %r to have source and line: %s',
logging.get_full_module_name(node), logging.repr_domxml(node))
node.source = find_source_node(node) node.source = find_source_node(node)
node.line = 0 # need fix docutils to get `node.line` node.line = 0 # need fix docutils to get `node.line`
return return
@ -74,7 +82,6 @@ def apply_source_workaround(node):
IGNORED_NODES = ( IGNORED_NODES = (
nodes.Invisible, nodes.Invisible,
nodes.Inline,
nodes.literal_block, nodes.literal_block,
nodes.doctest_block, nodes.doctest_block,
addnodes.versionmodified, addnodes.versionmodified,
@ -96,17 +103,30 @@ def is_translatable(node):
if isinstance(node, addnodes.translatable): if isinstance(node, addnodes.translatable):
return True return True
if isinstance(node, nodes.Inline) and 'translatable' not in node:
# inline node must not be translated if 'translatable' is not set
return False
if isinstance(node, nodes.TextElement): if isinstance(node, nodes.TextElement):
if not node.source: if not node.source:
logger.debug('[i18n] SKIP %r because no node.source: %s',
logging.get_full_module_name(node), logging.repr_domxml(node))
return False # built-in message return False # built-in message
if isinstance(node, IGNORED_NODES) and 'translatable' not in node: if isinstance(node, IGNORED_NODES) and 'translatable' not in node:
logger.debug("[i18n] SKIP %r because node is in IGNORED_NODES "
"and no node['translatable']: %s",
logging.get_full_module_name(node), logging.repr_domxml(node))
return False return False
if not node.get('translatable', True): if not node.get('translatable', True):
# not(node['translatable'] == True or node['translatable'] is None) # not(node['translatable'] == True or node['translatable'] is None)
logger.debug("[i18n] SKIP %r because not node['translatable']: %s",
logging.get_full_module_name(node), logging.repr_domxml(node))
return False return False
# <field_name>orphan</field_name> # <field_name>orphan</field_name>
# XXX ignore all metadata (== docinfo) # XXX ignore all metadata (== docinfo)
if isinstance(node, nodes.field_name) and node.children[0] == 'orphan': if isinstance(node, nodes.field_name) and node.children[0] == 'orphan':
logger.debug('[i18n] SKIP %r because orphan node: %s',
logging.get_full_module_name(node), logging.repr_domxml(node))
return False return False
return True return True

View File

@ -1,54 +1,3 @@
from sphinx.config import string_classes, ENUM project = 'Sphinx <Tests>'
release = '0.6alpha1'
value1 = 123 # wrong type templates_path = ['_templates']
value2 = 123 # lambda with wrong type
value3 = [] # lambda with correct type
value4 = True # child type
value5 = 3 # parent type
value6 = () # other sequence type, also raises
value7 = ['foo'] # explicitly permitted
class A(object):
pass
class B(A):
pass
class C(A):
pass
value8 = C() # sibling type
# both have no default or permissible types
value9 = 'foo'
value10 = 123
value11 = u'bar'
value12 = u'bar'
value13 = 'bar'
value14 = u'bar'
value15 = 'bar'
value16 = u'bar'
def setup(app):
app.add_config_value('value1', 'string', False)
app.add_config_value('value2', lambda conf: [], False)
app.add_config_value('value3', [], False)
app.add_config_value('value4', 100, False)
app.add_config_value('value5', False, False)
app.add_config_value('value6', [], False)
app.add_config_value('value7', 'string', False, [list])
app.add_config_value('value8', B(), False)
app.add_config_value('value9', None, False)
app.add_config_value('value10', None, False)
app.add_config_value('value11', None, False, [str])
app.add_config_value('value12', 'string', False)
app.add_config_value('value13', None, False, string_classes)
app.add_config_value('value14', None, False, string_classes)
app.add_config_value('value15', u'unicode', False)
app.add_config_value('value16', u'unicode', False)
app.add_config_value('value17', 'default', False, ENUM('default', 'one', 'two'))

View File

@ -12,7 +12,7 @@ from sphinx import addnodes
sys.path.append(os.path.abspath('.')) sys.path.append(os.path.abspath('.'))
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.jsmath', 'sphinx.ext.todo', extensions = ['sphinx.ext.autodoc', 'sphinx.ext.jsmath', 'sphinx.ext.todo',
'sphinx.ext.coverage', 'sphinx.ext.extlinks', 'ext'] 'sphinx.ext.coverage', 'sphinx.ext.extlinks']
jsmath_path = 'dummy.js' jsmath_path = 'dummy.js'
@ -65,8 +65,6 @@ man_pages = [
'Georg Brandl and someone else', 1), 'Georg Brandl and someone else', 1),
] ]
value_from_conf_py = 84
coverage_c_path = ['special/*.h'] coverage_c_path = ['special/*.h']
coverage_c_regexes = {'function': r'^PyAPI_FUNC\(.*\)\s+([^_][\w_]+)'} coverage_c_regexes = {'function': r'^PyAPI_FUNC\(.*\)\s+([^_][\w_]+)'}
@ -104,7 +102,6 @@ class ClassDirective(Directive):
def setup(app): def setup(app):
import parsermod import parsermod
app.add_config_value('value_from_conf_py', 42, False)
app.add_directive('clsdir', ClassDirective) app.add_directive('clsdir', ClassDirective)
app.add_object_type('userdesc', 'userdescrole', '%s (userdesc)', app.add_object_type('userdesc', 'userdescrole', '%s (userdesc)',
userdesc_parse, objname='user desc') userdesc_parse, objname='user desc')

View File

@ -1,5 +0,0 @@
# Test extension module
def setup(app):
app.add_config_value('value_from_ext', [], False)

View File

@ -11,7 +11,7 @@
import pytest import pytest
from docutils import nodes from docutils import nodes
from sphinx.application import ExtensionError from sphinx.errors import ExtensionError
from sphinx.domains import Domain from sphinx.domains import Domain
from sphinx.testing.util import strip_escseq from sphinx.testing.util import strip_escseq
from sphinx.util import logging from sphinx.util import logging

View File

@ -11,15 +11,15 @@
""" """
import mock import mock
import pytest import pytest
from six import PY3, iteritems from six import PY3
import sphinx import sphinx
from sphinx.config import Config from sphinx.config import Config, ENUM, string_classes, check_confval_types
from sphinx.errors import ExtensionError, ConfigError, VersionRequirementError from sphinx.errors import ExtensionError, ConfigError, VersionRequirementError
from sphinx.testing.path import path from sphinx.testing.path import path
@pytest.mark.sphinx(confoverrides={ @pytest.mark.sphinx(testroot='config', confoverrides={
'master_doc': 'master', 'master_doc': 'master',
'nonexisting_value': 'True', 'nonexisting_value': 'True',
'latex_elements.docclass': 'scrartcl', 'latex_elements.docclass': 'scrartcl',
@ -74,36 +74,64 @@ def test_core_config(app, status, warning):
assert cfg['project'] == cfg.project == 'Sphinx Tests' assert cfg['project'] == cfg.project == 'Sphinx Tests'
def test_extension_values(app, status, warning): def test_extension_values():
cfg = app.config config = Config()
# default value # check standard settings
assert cfg.value_from_ext == [] assert config.master_doc == 'contents'
# non-default value
assert cfg.value_from_conf_py == 84
# no duplicate values allowed # can't override it by add_config_value()
with pytest.raises(ExtensionError) as excinfo: with pytest.raises(ExtensionError) as excinfo:
app.add_config_value('html_title', 'x', True) config.add('master_doc', 'index', 'env', None)
assert 'already present' in str(excinfo.value) assert 'already present' in str(excinfo.value)
# add a new config value
config.add('value_from_ext', [], 'env', None)
assert config.value_from_ext == []
# can't override it by add_config_value()
with pytest.raises(ExtensionError) as excinfo: with pytest.raises(ExtensionError) as excinfo:
app.add_config_value('value_from_ext', 'x', True) config.add('value_from_ext', [], 'env', None)
assert 'already present' in str(excinfo.value) assert 'already present' in str(excinfo.value)
def test_overrides():
config = Config({'value1': '1', 'value2': 2, 'value6': {'default': 6}},
{'value2': 999, 'value3': '999', 'value5.attr1': 999, 'value6.attr1': 999,
'value7': 'abc,def,ghi', 'value8': 'abc,def,ghi'})
config.add('value1', None, 'env', ())
config.add('value2', None, 'env', ())
config.add('value3', 0, 'env', ())
config.add('value4', 0, 'env', ())
config.add('value5', {'default': 0}, 'env', ())
config.add('value6', {'default': 0}, 'env', ())
config.add('value7', None, 'env', ())
config.add('value8', [], 'env', ())
config.init_values()
assert config.value1 == '1'
assert config.value2 == 999
assert config.value3 == 999
assert config.value4 == 0
assert config.value5 == {'attr1': 999}
assert config.value6 == {'default': 6, 'attr1': 999}
assert config.value7 == 'abc,def,ghi'
assert config.value8 == ['abc', 'def', 'ghi']
@mock.patch("sphinx.config.logger") @mock.patch("sphinx.config.logger")
def test_errors_warnings(logger, tempdir): def test_errors_warnings(logger, tempdir):
# test the error for syntax errors in the config file # test the error for syntax errors in the config file
(tempdir / 'conf.py').write_text(u'project = \n', encoding='ascii') (tempdir / 'conf.py').write_text(u'project = \n', encoding='ascii')
with pytest.raises(ConfigError) as excinfo: with pytest.raises(ConfigError) as excinfo:
Config(tempdir, 'conf.py', {}, None) Config.read(tempdir, {}, None)
assert 'conf.py' in str(excinfo.value) assert 'conf.py' in str(excinfo.value)
# test the automatic conversion of 2.x only code in configs # test the automatic conversion of 2.x only code in configs
(tempdir / 'conf.py').write_text( (tempdir / 'conf.py').write_text(
u'# -*- coding: utf-8\n\nproject = u"Jägermeister"\n', u'# -*- coding: utf-8\n\nproject = u"Jägermeister"\n',
encoding='utf-8') encoding='utf-8')
cfg = Config(tempdir, 'conf.py', {}, None) cfg = Config.read(tempdir, {}, None)
cfg.init_values() cfg.init_values()
assert cfg.project == u'Jägermeister' assert cfg.project == u'Jägermeister'
assert logger.called is False assert logger.called is False
@ -115,7 +143,7 @@ def test_errors_warnings(logger, tempdir):
return return
(tempdir / 'conf.py').write_text( (tempdir / 'conf.py').write_text(
u'# -*- coding: latin-1\nproject = "fooä"\n', encoding='latin-1') u'# -*- coding: latin-1\nproject = "fooä"\n', encoding='latin-1')
cfg = Config(tempdir, 'conf.py', {}, None) cfg = Config.read(tempdir, {}, None)
assert logger.warning.called is False assert logger.warning.called is False
cfg.check_unicode() cfg.check_unicode()
@ -174,7 +202,7 @@ def test_config_eol(logger, tempdir):
configfile = tempdir / 'conf.py' configfile = tempdir / 'conf.py'
for eol in (b'\n', b'\r\n'): for eol in (b'\n', b'\r\n'):
configfile.write_bytes(b'project = "spam"' + eol) configfile.write_bytes(b'project = "spam"' + eol)
cfg = Config(tempdir, 'conf.py', {}, None) cfg = Config.read(tempdir, {}, None)
cfg.init_values() cfg.init_values()
assert cfg.project == u'spam' assert cfg.project == u'spam'
assert logger.called is False assert logger.called is False
@ -195,60 +223,81 @@ def test_builtin_conf(app, status, warning):
'warning') 'warning')
# See roots/test-config/conf.py. # example classes for type checking
TYPECHECK_WARNINGS = { class A(object):
'value1': True, pass
'value2': True,
'value3': False,
'value4': True,
'value5': False,
'value6': True,
'value7': False,
'value8': False,
'value9': False,
'value10': False,
'value11': False if PY3 else True,
'value12': False,
'value13': False,
'value14': False,
'value15': False,
'value16': False,
}
@pytest.mark.parametrize("key,should", iteritems(TYPECHECK_WARNINGS)) class B(A):
@pytest.mark.sphinx(testroot='config') pass
def test_check_types(warning, key, should):
warn = warning.getvalue()
if should:
assert key in warn, (
'override on "%s" should raise a type warning' % key
)
else:
assert key not in warn, (
'override on "%s" should NOT raise a type warning' % key
)
@pytest.mark.sphinx(testroot='config') class C(A):
def test_check_enum(app, status, warning): pass
assert "The config value `value17` has to be a one of ('default', 'one', 'two'), " \
not in warning.getvalue()
@pytest.mark.sphinx(testroot='config', confoverrides={'value17': 'invalid'}) # name, default, annotation, actual, warned
def test_check_enum_failed(app, status, warning): TYPECHECK_WARNINGS = [
assert "The config value `value17` has to be a one of ('default', 'one', 'two'), " \ ('value1', 'string', None, 123, True), # wrong type
"but `invalid` is given." in warning.getvalue() ('value2', lambda _: [], None, 123, True), # lambda with wrong type
('value3', lambda _: [], None, [], False), # lambda with correct type
('value4', 100, None, True, True), # child type
('value5', False, None, True, False), # parent type
('value6', [], None, (), True), # other sequence type
('value7', 'string', [list], ['foo'], False), # explicit type annotation
('value8', B(), None, C(), False), # sibling type
('value9', None, None, 'foo', False), # no default or no annotations
('value10', None, None, 123, False), # no default or no annotations
('value11', None, [str], u'bar', False if PY3 else True), # str vs unicode
('value12', 'string', None, u'bar', False), # str vs unicode
('value13', None, string_classes, 'bar', False), # string_classes
('value14', None, string_classes, u'bar', False), # string_classes
('value15', u'unicode', None, 'bar', False), # str vs unicode
('value16', u'unicode', None, u'bar', False), # str vs unicode
]
@pytest.mark.sphinx(testroot='config', confoverrides={'value17': ['one', 'two']}) @mock.patch("sphinx.config.logger")
def test_check_enum_for_list(app, status, warning): @pytest.mark.parametrize("name,default,annotation,actual,warned", TYPECHECK_WARNINGS)
assert "The config value `value17` has to be a one of ('default', 'one', 'two'), " \ def test_check_types(logger, name, default, annotation, actual, warned):
not in warning.getvalue() config = Config({name: actual})
config.add(name, default, 'env', annotation or ())
config.init_values()
check_confval_types(None, config)
assert logger.warning.called == warned
@pytest.mark.sphinx(testroot='config', confoverrides={'value17': ['one', 'two', 'invalid']}) @mock.patch("sphinx.config.logger")
def test_check_enum_for_list_failed(app, status, warning): def test_check_enum(logger):
assert "The config value `value17` has to be a one of ('default', 'one', 'two'), " \ config = Config()
"but `['one', 'two', 'invalid']` is given." in warning.getvalue() config.add('value', 'default', False, ENUM('default', 'one', 'two'))
config.init_values()
check_confval_types(None, config)
logger.warning.assert_not_called() # not warned
@mock.patch("sphinx.config.logger")
def test_check_enum_failed(logger):
config = Config({'value': 'invalid'})
config.add('value', 'default', False, ENUM('default', 'one', 'two'))
config.init_values()
check_confval_types(None, config)
logger.warning.assert_called()
@mock.patch("sphinx.config.logger")
def test_check_enum_for_list(logger):
config = Config({'value': ['one', 'two']})
config.add('value', 'default', False, ENUM('default', 'one', 'two'))
config.init_values()
check_confval_types(None, config)
logger.warning.assert_not_called() # not warned
@mock.patch("sphinx.config.logger")
def test_check_enum_for_list_failed(logger):
config = Config({'value': ['one', 'two', 'invalid']})
config.add('value', 'default', False, ENUM('default', 'one', 'two'))
config.init_values()
check_confval_types(None, config)
logger.warning.assert_called()

View File

@ -17,7 +17,7 @@ from sphinx.config import Config
from sphinx.directives.code import LiteralIncludeReader from sphinx.directives.code import LiteralIncludeReader
from sphinx.testing.util import etree_parse from sphinx.testing.util import etree_parse
DUMMY_CONFIG = Config(None, None, {}, '') DUMMY_CONFIG = Config({}, {})
@pytest.fixture(scope='module') @pytest.fixture(scope='module')

View File

@ -15,6 +15,7 @@ import sys
import pytest import pytest
from sphinx.testing.path import path from sphinx.testing.path import path
from sphinx.util.docutils import patch_docutils
def regex_count(expr, result): def regex_count(expr, result):
@ -23,7 +24,9 @@ def regex_count(expr, result):
@pytest.mark.sphinx('html', testroot='docutilsconf', freshenv=True, docutilsconf='') @pytest.mark.sphinx('html', testroot='docutilsconf', freshenv=True, docutilsconf='')
def test_html_with_default_docutilsconf(app, status, warning): def test_html_with_default_docutilsconf(app, status, warning):
app.builder.build(['contents']) with patch_docutils(app.confdir):
app.builder.build(['contents'])
result = (app.outdir / 'contents.html').text(encoding='utf-8') result = (app.outdir / 'contents.html').text(encoding='utf-8')
assert regex_count(r'<th class="field-name">', result) == 1 assert regex_count(r'<th class="field-name">', result) == 1
@ -39,7 +42,9 @@ def test_html_with_default_docutilsconf(app, status, warning):
'\n') '\n')
) )
def test_html_with_docutilsconf(app, status, warning): def test_html_with_docutilsconf(app, status, warning):
app.builder.build(['contents']) with patch_docutils(app.confdir):
app.builder.build(['contents'])
result = (app.outdir / 'contents.html').text(encoding='utf-8') result = (app.outdir / 'contents.html').text(encoding='utf-8')
assert regex_count(r'<th class="field-name">', result) == 0 assert regex_count(r'<th class="field-name">', result) == 0
@ -50,25 +55,29 @@ def test_html_with_docutilsconf(app, status, warning):
@pytest.mark.sphinx('html', testroot='docutilsconf') @pytest.mark.sphinx('html', testroot='docutilsconf')
def test_html(app, status, warning): def test_html(app, status, warning):
app.builder.build(['contents']) with patch_docutils(app.confdir):
app.builder.build(['contents'])
assert warning.getvalue() == '' assert warning.getvalue() == ''
@pytest.mark.sphinx('latex', testroot='docutilsconf') @pytest.mark.sphinx('latex', testroot='docutilsconf')
def test_latex(app, status, warning): def test_latex(app, status, warning):
app.builder.build(['contents']) with patch_docutils(app.confdir):
app.builder.build(['contents'])
assert warning.getvalue() == '' assert warning.getvalue() == ''
@pytest.mark.sphinx('man', testroot='docutilsconf') @pytest.mark.sphinx('man', testroot='docutilsconf')
def test_man(app, status, warning): def test_man(app, status, warning):
app.builder.build(['contents']) with patch_docutils(app.confdir):
app.builder.build(['contents'])
assert warning.getvalue() == '' assert warning.getvalue() == ''
@pytest.mark.sphinx('texinfo', testroot='docutilsconf') @pytest.mark.sphinx('texinfo', testroot='docutilsconf')
def test_texinfo(app, status, warning): def test_texinfo(app, status, warning):
app.builder.build(['contents']) with patch_docutils(app.confdir):
app.builder.build(['contents'])
@pytest.mark.sphinx('html', testroot='docutilsconf', @pytest.mark.sphinx('html', testroot='docutilsconf',
@ -87,4 +96,5 @@ def test_docutils_source_link_with_nonascii_file(app, status, warning):
'nonascii filename not supported on this filesystem encoding: ' 'nonascii filename not supported on this filesystem encoding: '
'%s', FILESYSTEMENCODING) '%s', FILESYSTEMENCODING)
app.builder.build_all() with patch_docutils(app.confdir):
app.builder.build_all()

View File

@ -8,6 +8,7 @@
:copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import os
import pytest import pytest
from packaging.specifiers import InvalidSpecifier from packaging.specifiers import InvalidSpecifier
from packaging.version import InvalidVersion from packaging.version import InvalidVersion
@ -68,7 +69,7 @@ def test_reporting_with_autodoc(app, status, warning, capfd):
written = [] written = []
app.builder._warn_out = written.append app.builder._warn_out = written.append
app.builder.build_all() app.builder.build_all()
lines = '\n'.join(written).split('\n') lines = '\n'.join(written).replace(os.sep, '/').split('\n')
failures = [l for l in lines if l.startswith('File')] failures = [l for l in lines if l.startswith('File')]
expected = [ expected = [
'File "dir/inner.rst", line 1, in default', 'File "dir/inner.rst", line 1, in default',

View File

@ -22,24 +22,26 @@ def test_graphviz_png_html(app, status, warning):
app.builder.build_all() app.builder.build_all()
content = (app.outdir / 'index.html').text() content = (app.outdir / 'index.html').text()
html = (r'<div class="figure" .*?>\s*<img .*?/>\s*<p class="caption">' html = (r'<div class="figure" .*?>\s*'
r'<div class="graphviz"><img .*?/></div>\s*<p class="caption">'
r'<span class="caption-text">caption of graph</span>.*</p>\s*</div>') r'<span class="caption-text">caption of graph</span>.*</p>\s*</div>')
assert re.search(html, content, re.S) assert re.search(html, content, re.S)
html = 'Hello <img .*?/>\n graphviz world' html = 'Hello <div class="graphviz"><img .*?/></div>\n graphviz world'
assert re.search(html, content, re.S) assert re.search(html, content, re.S)
html = '<img src=".*?" alt="digraph {\n bar -&gt; baz\n}" />' html = '<img src=".*?" alt="digraph {\n bar -&gt; baz\n}" />'
assert re.search(html, content, re.M) assert re.search(html, content, re.M)
html = (r'<div class="figure align-right" .*?>\s*<img .*?/>\s*<p class="caption">' html = (r'<div class="figure align-right" .*?>\s*'
r'<div class="graphviz"><img .*?/></div>\s*<p class="caption">'
r'<span class="caption-text">on right</span>.*</p>\s*</div>') r'<span class="caption-text">on right</span>.*</p>\s*</div>')
assert re.search(html, content, re.S) assert re.search(html, content, re.S)
html = (r'<div align=\"center\" class=\"align-center\">' html = (r'<div align=\"center\" class=\"align-center\">'
r'<img src=\".*\.png\" alt=\"digraph foo {\n' r'<div class="graphviz"><img src=\".*\.png\" alt=\"digraph foo {\n'
r'centered\n' r'centered\n'
r'}\" />\n</div>') r'}\" /></div>\n</div>')
assert re.search(html, content, re.S) assert re.search(html, content, re.S)
@ -52,34 +54,34 @@ def test_graphviz_svg_html(app, status, warning):
content = (app.outdir / 'index.html').text() content = (app.outdir / 'index.html').text()
html = (r'<div class=\"figure\" .*?>\n' html = (r'<div class=\"figure\" .*?>\n'
r'<object data=\".*\.svg\".*>\n' r'<div class="graphviz"><object data=\".*\.svg\".*>\n'
r'\s+<p class=\"warning\">digraph foo {\n' r'\s*<p class=\"warning\">digraph foo {\n'
r'bar -&gt; baz\n' r'bar -&gt; baz\n'
r'}</p></object>\n' r'}</p></object></div>\n'
r'<p class=\"caption\"><span class=\"caption-text\">' r'<p class=\"caption\"><span class=\"caption-text\">'
r'caption of graph</span>.*</p>\n</div>') r'caption of graph</span>.*</p>\n</div>')
assert re.search(html, content, re.S) assert re.search(html, content, re.S)
html = (r'Hello <object.*>\n' html = (r'Hello <div class="graphviz"><object.*>\n'
r'\s+<p class=\"warning\">graph</p></object>\n' r'\s*<p class=\"warning\">graph</p></object></div>\n'
r' graphviz world') r' graphviz world')
assert re.search(html, content, re.S) assert re.search(html, content, re.S)
html = (r'<div class=\"figure align-right\" .*\>\n' html = (r'<div class=\"figure align-right\" .*\>\n'
r'<object data=\".*\.svg\".*>\n' r'<div class="graphviz"><object data=\".*\.svg\".*>\n'
r'\s+<p class=\"warning\">digraph bar {\n' r'\s*<p class=\"warning\">digraph bar {\n'
r'foo -&gt; bar\n' r'foo -&gt; bar\n'
r'}</p></object>\n' r'}</p></object></div>\n'
r'<p class=\"caption\"><span class=\"caption-text\">' r'<p class=\"caption\"><span class=\"caption-text\">'
r'on right</span>.*</p>\n' r'on right</span>.*</p>\n'
r'</div>') r'</div>')
assert re.search(html, content, re.S) assert re.search(html, content, re.S)
html = (r'<div align=\"center\" class=\"align-center\">' html = (r'<div align=\"center\" class=\"align-center\">'
r'<object data=\".*\.svg\".*>\n' r'<div class="graphviz"><object data=\".*\.svg\".*>\n'
r'\s+<p class=\"warning\">digraph foo {\n' r'\s*<p class=\"warning\">digraph foo {\n'
r'centered\n' r'centered\n'
r'}</p></object>\n' r'}</p></object></div>\n'
r'</div>') r'</div>')
assert re.search(html, content, re.S) assert re.search(html, content, re.S)

View File

@ -19,14 +19,34 @@ from sphinx.ext.inheritance_diagram import InheritanceException, import_classes
@pytest.mark.sphinx('html', testroot='ext-inheritance_diagram') @pytest.mark.sphinx('html', testroot='ext-inheritance_diagram')
@pytest.mark.usefixtures('if_graphviz_found') @pytest.mark.usefixtures('if_graphviz_found')
def test_inheritance_diagram_html(app, status, warning): def test_inheritance_diagram_png_html(app, status, warning):
app.builder.build_all() app.builder.build_all()
content = (app.outdir / 'index.html').text() content = (app.outdir / 'index.html').text()
pattern = ('<div class="figure" id="id1">\n' pattern = ('<div class="figure" id="id1">\n'
'<div class="graphviz">'
'<img src="_images/inheritance-\\w+.png" alt="Inheritance diagram of test.Foo" ' '<img src="_images/inheritance-\\w+.png" alt="Inheritance diagram of test.Foo" '
'class="inheritance"/>\n<p class="caption"><span class="caption-text">' 'class="inheritance"/></div>\n<p class="caption"><span class="caption-text">'
'Test Foo!</span><a class="headerlink" href="#id1" '
'title="Permalink to this image">\xb6</a></p>')
assert re.search(pattern, content, re.M)
@pytest.mark.sphinx('html', testroot='ext-inheritance_diagram',
confoverrides={'graphviz_output_format': 'svg'})
@pytest.mark.usefixtures('if_graphviz_found')
def test_inheritance_diagram_svg_html(app, status, warning):
app.builder.build_all()
content = (app.outdir / 'index.html').text()
pattern = ('<div class="figure" id="id1">\n'
'<div class="graphviz">'
'<object data="_images/inheritance-\\w+.svg" '
'type="image/svg\\+xml" class="inheritance">\n'
'<p class=\"warning\">Inheritance diagram of test.Foo</p>'
'</object></div>\n<p class="caption"><span class="caption-text">'
'Test Foo!</span><a class="headerlink" href="#id1" ' 'Test Foo!</span><a class="headerlink" href="#id1" '
'title="Permalink to this image">\xb6</a></p>') 'title="Permalink to this image">\xb6</a></p>')
assert re.search(pattern, content, re.M) assert re.search(pattern, content, re.M)
@ -62,8 +82,9 @@ def test_inheritance_diagram_latex_alias(app, status, warning):
content = (app.outdir / 'index.html').text() content = (app.outdir / 'index.html').text()
pattern = ('<div class="figure" id="id1">\n' pattern = ('<div class="figure" id="id1">\n'
'<div class="graphviz">'
'<img src="_images/inheritance-\\w+.png" alt="Inheritance diagram of test.Foo" ' '<img src="_images/inheritance-\\w+.png" alt="Inheritance diagram of test.Foo" '
'class="inheritance"/>\n<p class="caption"><span class="caption-text">' 'class="inheritance"/></div>\n<p class="caption"><span class="caption-text">'
'Test Foo!</span><a class="headerlink" href="#id1" ' 'Test Foo!</span><a class="headerlink" href="#id1" '
'title="Permalink to this image">\xb6</a></p>') 'title="Permalink to this image">\xb6</a></p>')
assert re.search(pattern, content, re.M) assert re.search(pattern, content, re.M)