mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch 'master' into deprecate_highlightlang_directive
This commit is contained in:
commit
8e6c04c630
53
CHANGES
53
CHANGES
@ -17,6 +17,12 @@ Incompatible changes
|
||||
a ``conf.py`` file sphinx-build generates.
|
||||
* The ``gettext_compact`` attribute is removed from ``document.settings``
|
||||
object. Please use ``config.gettext_compact`` instead.
|
||||
* The processing order on reading phase is changed. smart_quotes, sphinx
|
||||
domains, :event:`doctree-read` event and versioning doctrees are invoked
|
||||
earlier than so far. For more details, please read a description of
|
||||
:py:meth:`.Sphinx.add_transform()`
|
||||
* #4827: All ``substitution_definition`` nodes are removed from doctree on
|
||||
reading phase
|
||||
|
||||
Deprecated
|
||||
----------
|
||||
@ -36,6 +42,12 @@ Deprecated
|
||||
* ``env._nitpick_ignore`` is deprecated
|
||||
* ``app.override_domain()`` is deprecated
|
||||
* ``app.add_stylesheet()`` 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
|
||||
|
||||
For more details, see `deprecation APIs list
|
||||
@ -63,6 +75,13 @@ Features added
|
||||
* Add :confval:`html_css_files` and :confval:`epub_css_files` for adding CSS
|
||||
files from configuration
|
||||
* #4834: Ensure set object descriptions are reproducible.
|
||||
* #4828: Allow to override :confval:`numfig_format` partially. Full definition
|
||||
is not needed.
|
||||
* Improve warning messages during including (refs: #4818)
|
||||
* LaTeX: separate customizability of :rst:role:`guilabel` and
|
||||
:rst:role:`menuselection` (refs: #4830)
|
||||
* Add ``Config.read()`` classmethod to create a new config object from
|
||||
configuration file
|
||||
|
||||
Bugs fixed
|
||||
----------
|
||||
@ -77,7 +96,7 @@ Features removed
|
||||
|
||||
* ``sphinx.ext.pngmath`` extension
|
||||
|
||||
Release 1.7.3 (in development)
|
||||
Release 1.7.5 (in development)
|
||||
==============================
|
||||
|
||||
Dependencies
|
||||
@ -95,6 +114,24 @@ Features added
|
||||
Bugs fixed
|
||||
----------
|
||||
|
||||
Testing
|
||||
--------
|
||||
|
||||
Release 1.7.4 (released Apr 25, 2018)
|
||||
=====================================
|
||||
|
||||
Bugs fixed
|
||||
----------
|
||||
|
||||
* #4885, #4887: domains: Crashed with duplicated objects
|
||||
* #4889: latex: sphinx.writers.latex causes recusrive import
|
||||
|
||||
Release 1.7.3 (released Apr 23, 2018)
|
||||
=====================================
|
||||
|
||||
Bugs fixed
|
||||
----------
|
||||
|
||||
* #4769: autodoc loses the first staticmethod parameter
|
||||
* #4790: autosummary: too wide two column tables in PDF builds
|
||||
* #4795: Latex customization via ``_templates/longtable.tex_t`` is broken
|
||||
@ -105,9 +142,14 @@ Bugs fixed
|
||||
* #4817: wrong URLs on warning messages
|
||||
* #4784: latex: :confval:`latex_show_urls` assigns incorrect footnote numbers if
|
||||
hyperlinks exists inside substitutions
|
||||
|
||||
Testing
|
||||
--------
|
||||
* #4837: latex with class memoir Error: Font command ``\sf`` is not supported
|
||||
* #4803: latex: too slow in proportion to number of auto numbered footnotes
|
||||
* #4838: htmlhelp: The entries in .hhp file is not ordered
|
||||
* toctree directive tries to glob for URL having query_string
|
||||
* #4871: html search: Upper characters problem in German
|
||||
* #4717: latex: Compilation for German docs failed with LuaLaTeX and XeLaTeX
|
||||
* #4459: duplicated labels detector does not work well in parallel build
|
||||
* #4878: Crashed with extension which returns invalid metadata
|
||||
|
||||
Release 1.7.2 (released Mar 21, 2018)
|
||||
=====================================
|
||||
@ -139,6 +181,7 @@ Bugs fixed
|
||||
* #1435: qthelp builder should htmlescape keywords
|
||||
* epub: Fix docTitle elements of toc.ncx is not escaped
|
||||
* #4520: apidoc: Subpackage not in toc (introduced in 1.6.6) now fixed
|
||||
* #4767: html: search highlighting breaks mathjax equations
|
||||
|
||||
Release 1.7.1 (released Feb 23, 2018)
|
||||
=====================================
|
||||
@ -214,6 +257,8 @@ Incompatible changes
|
||||
(refs: #4295)
|
||||
* #4246: Limit width of text body for all themes. Conifigurable via theme
|
||||
options ``body_min_width`` and ``body_max_width``.
|
||||
* #4771: apidoc: The ``exclude_patterns`` arguments are ignored if they are
|
||||
placed just after command line options
|
||||
|
||||
1.7.0b2
|
||||
|
||||
|
2
EXAMPLES
2
EXAMPLES
@ -317,7 +317,7 @@ Documentation using a custom theme or integrated in a website
|
||||
* PSI4: http://www.psicode.org/psi4manual/master/index.html
|
||||
* PyMOTW: https://pymotw.com/2/
|
||||
* python-aspectlib: https://python-aspectlib.readthedocs.io/
|
||||
(`sphinx_py3doc_enhanced_theme <https://pypi.python.org/pypi/sphinx_py3doc_enhanced_theme>`__)
|
||||
(`sphinx_py3doc_enhanced_theme <https://pypi.org/project/sphinx_py3doc_enhanced_theme/>`__)
|
||||
* QGIS: https://qgis.org/en/docs/index.html
|
||||
* qooxdoo: http://www.qooxdoo.org/current/
|
||||
* Roundup: http://www.roundup-tracker.org/
|
||||
|
@ -3,7 +3,7 @@
|
||||
========
|
||||
|
||||
.. image:: https://img.shields.io/pypi/v/sphinx.svg
|
||||
:target: https://pypi.python.org/pypi/Sphinx
|
||||
:target: https://pypi.org/project/Sphinx/
|
||||
:alt: Package on PyPi
|
||||
|
||||
.. image:: https://readthedocs.org/projects/sphinx/badge/
|
||||
@ -71,7 +71,7 @@ We also publish beta releases::
|
||||
If you wish to install `Sphinx` for development purposes, refer to `the
|
||||
contributors guide`__.
|
||||
|
||||
__ https://pypi.python.org/pypi/Sphinx
|
||||
__ https://pypi.org/project/Sphinx/
|
||||
__ http://www.sphinx-doc.org/en/master/devguide.html
|
||||
|
||||
Documentation
|
||||
|
4
doc/_templates/indexsidebar.html
vendored
4
doc/_templates/indexsidebar.html
vendored
@ -8,11 +8,11 @@
|
||||
not released yet.{%endtrans%}</p>
|
||||
<p>{%trans%}You can use it from the
|
||||
<a href="https://github.com/sphinx-doc/sphinx/">Git repo</a> or look for
|
||||
released versions in the <a href="https://pypi.python.org/pypi/Sphinx">Python
|
||||
released versions in the <a href="https://pypi.org/project/Sphinx/">Python
|
||||
Package Index</a>.{%endtrans%}</p>
|
||||
{% else %}
|
||||
<p>{%trans%}Current version: <b><a href="changes.html">{{ version }}</a></b>{%endtrans%}</p>
|
||||
<p>{%trans%}Get Sphinx from the <a href="https://pypi.python.org/pypi/Sphinx">Python Package
|
||||
<p>{%trans%}Get Sphinx from the <a href="https://pypi.org/project/Sphinx/">Python Package
|
||||
Index</a>, or install it with:{%endtrans%}</p>
|
||||
<pre>pip install -U Sphinx</pre>
|
||||
{% endif %}
|
||||
|
4
doc/_themes/sphinx13/static/sphinx13.css
vendored
4
doc/_themes/sphinx13/static/sphinx13.css
vendored
@ -302,6 +302,10 @@ cite, code, tt {
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
table.deprecated code.literal {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
tt {
|
||||
background-color: #f2f2f2;
|
||||
border: 1px solid #ddd;
|
||||
|
@ -288,7 +288,7 @@ name is ``rinoh``. Refer to the `rinohtype manual`_ for details.
|
||||
globalcontext_filename = 'globalcontext.phpdump'
|
||||
searchindex_filename = 'searchindex.phpdump'
|
||||
|
||||
.. _PHP serialization: https://pypi.python.org/pypi/phpserialize
|
||||
.. _PHP serialization: https://pypi.org/project/phpserialize/
|
||||
|
||||
.. attribute:: implementation
|
||||
|
||||
|
@ -192,8 +192,9 @@ General configuration
|
||||
.. index:: pair: global; substitutions
|
||||
|
||||
A string of reStructuredText that will be included at the end of every source
|
||||
file that is read. This is the right place to add substitutions that should
|
||||
be available in every file. An example::
|
||||
file that is read. This is a possible place to add substitutions that should
|
||||
be available in every file (another being :confval:`rst_prolog`). An
|
||||
example::
|
||||
|
||||
rst_epilog = """
|
||||
.. |psf| replace:: Python Software Foundation
|
||||
@ -203,8 +204,16 @@ General configuration
|
||||
|
||||
.. confval:: rst_prolog
|
||||
|
||||
.. index:: pair: global; substitutions
|
||||
|
||||
A string of reStructuredText that will be included at the beginning of every
|
||||
source file that is read.
|
||||
source file that is read. This is a possible place to add substitutions that
|
||||
should be available in every file (another being :confval:`rst_epilog`). An
|
||||
example::
|
||||
|
||||
rst_prolog = """
|
||||
.. |psf| replace:: Python Software Foundation
|
||||
"""
|
||||
|
||||
.. versionadded:: 1.0
|
||||
|
||||
@ -868,6 +877,18 @@ that use Sphinx's HTMLWriter class.
|
||||
named :file:`default.css` will overwrite the theme's
|
||||
:file:`default.css`.
|
||||
|
||||
.. note::
|
||||
|
||||
For security reason, dotfiles under ``html_static_path`` will
|
||||
not be copied. If you'd like to copy them intentionally, please
|
||||
add them each filepath to this setting::
|
||||
|
||||
html_static_path = ['_static', '_static/.htaccess']
|
||||
|
||||
Another way to do that, you can also use
|
||||
:confval:`html_extra_path`. It allows to copy dotfiles under
|
||||
the directories.
|
||||
|
||||
.. versionchanged:: 0.4
|
||||
The paths in :confval:`html_static_path` can now contain subdirectories.
|
||||
|
||||
@ -1138,8 +1159,8 @@ that use Sphinx's HTMLWriter class.
|
||||
Sphinx uses a Python implementation by default. You can use a C
|
||||
implementation to accelerate building the index file.
|
||||
|
||||
* `PorterStemmer <https://pypi.python.org/pypi/PorterStemmer>`_ (``en``)
|
||||
* `PyStemmer <https://pypi.python.org/pypi/PyStemmer>`_ (all languages)
|
||||
* `PorterStemmer <https://pypi.org/project/PorterStemmer/>`_ (``en``)
|
||||
* `PyStemmer <https://pypi.org/project/PyStemmer/>`_ (all languages)
|
||||
|
||||
.. versionadded:: 1.1
|
||||
With support for ``en`` and ``ja``.
|
||||
@ -1171,10 +1192,12 @@ that use Sphinx's HTMLWriter class.
|
||||
library ('libmecab.so' for linux, 'libmecab.dll' for windows) is required.
|
||||
:'sphinx.search.ja.JanomeSplitter':
|
||||
Janome binding. To use this splitter,
|
||||
`Janome <https://pypi.python.org/pypi/Janome>`_ is required.
|
||||
`Janome <https://pypi.org/project/Janome/>`_ is required.
|
||||
|
||||
To keep compatibility, ``'mecab'``, ``'janome'`` and ``'default'`` are also
|
||||
acceptable. However it will be deprecated in Sphinx-1.6.
|
||||
.. deprecated:: 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.
|
||||
|
@ -141,5 +141,5 @@ own extensions.
|
||||
.. _inlinesyntaxhighlight: https://sphinxcontrib-inlinesyntaxhighlight.readthedocs.io/
|
||||
.. _CMake: https://cmake.org
|
||||
.. _domaintools: https://bitbucket.org/klorenz/sphinxcontrib-domaintools
|
||||
.. _restbuilder: https://pypi.python.org/pypi/sphinxcontrib-restbuilder
|
||||
.. _restbuilder: https://pypi.org/project/sphinxcontrib-restbuilder/
|
||||
.. _Lasso: http://www.lassosoft.com/
|
||||
|
@ -1,7 +1,9 @@
|
||||
.. highlight:: rest
|
||||
|
||||
:mod:`sphinx.ext.imgconverter` -- Convert images to appropriate format for builders
|
||||
===================================================================================
|
||||
.. _sphinx.ext.imgconverter:
|
||||
|
||||
:mod:`sphinx.ext.imgconverter` -- A reference implementation for image converter using Imagemagick
|
||||
==================================================================================================
|
||||
|
||||
.. module:: sphinx.ext.imgconverter
|
||||
:synopsis: Convert images to appropriate format for builders
|
||||
@ -16,6 +18,12 @@ Internally, this extension uses Imagemagick_ to convert images.
|
||||
|
||||
.. _Imagemagick: https://www.imagemagick.org/script/index.php
|
||||
|
||||
.. note:: Imagemagick rasterizes a SVG image on conversion. As a result, the image
|
||||
becomes not scalable. To avoid that, please use other image converters
|
||||
like sphinxcontrib-svg2pdfconverter_ (which uses Inkscape or rsvg-convert).
|
||||
|
||||
.. _sphinxcontrib-svg2pdfconverter: https://github.com/missinglinkelectronics/sphinxcontrib-svg2pdfconverter
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
|
@ -108,6 +108,8 @@ The following is a list of deprecated interface.
|
||||
|
||||
.. list-table:: deprecated APIs
|
||||
:header-rows: 1
|
||||
:class: deprecated
|
||||
:widths: 40, 10, 10, 40
|
||||
|
||||
* - Target
|
||||
- Deprecated
|
||||
@ -124,6 +126,37 @@ The following is a list of deprecated interface.
|
||||
- 4.0
|
||||
- :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()``
|
||||
- 1.8
|
||||
- 3.0
|
||||
- ``sphinx.versioning.UIDTransform``
|
||||
|
||||
* - ``sphinx.application.Sphinx.override_domain()``
|
||||
- 1.8
|
||||
- 3.0
|
||||
@ -273,7 +306,7 @@ The following is a list of deprecated interface.
|
||||
* - ``sphinx.websupport``
|
||||
- 1.6
|
||||
- 2.0
|
||||
- `sphinxcontrib-websupport <https://pypi.python.org/pypi/sphinxcontrib-websupport>`_
|
||||
- `sphinxcontrib-websupport <https://pypi.org/project/sphinxcontrib-websupport/>`_
|
||||
|
||||
* - ``StandaloneHTMLBuilder.css_files``
|
||||
- 1.6
|
||||
|
@ -17,3 +17,6 @@ components (e.g. :class:`.Config`, :class:`.BuildEnvironment` and so on) easily.
|
||||
|
||||
.. autoclass:: sphinx.util.docutils.SphinxDirective
|
||||
:members:
|
||||
|
||||
.. autoclass:: sphinx.transforms.post_transforms.images.ImageConverter
|
||||
:members:
|
||||
|
@ -23,7 +23,7 @@ How do I...
|
||||
Use themes, see :doc:`theming`.
|
||||
|
||||
... add global substitutions or includes?
|
||||
Add them in the :confval:`rst_epilog` config value.
|
||||
Add them in the :confval:`rst_prolog` or :confval:`rst_epilog` config value.
|
||||
|
||||
... display the whole TOC tree in the sidebar?
|
||||
Use the :data:`toctree` callable in a custom layout template, probably in the
|
||||
@ -72,7 +72,7 @@ SCons
|
||||
|
||||
PyPI
|
||||
Jannis Leidel wrote a `setuptools command
|
||||
<https://pypi.python.org/pypi/Sphinx-PyPI-upload>`_ that automatically
|
||||
<https://pypi.org/project/Sphinx-PyPI-upload/>`_ that automatically
|
||||
uploads Sphinx documentation to the PyPI package documentation area at
|
||||
https://pythonhosted.org/.
|
||||
|
||||
|
@ -322,8 +322,8 @@ There is `sphinx translation page`_ for Sphinx (master) documentation.
|
||||
.. [2] Because nobody expects the Spanish Inquisition!
|
||||
|
||||
|
||||
.. _`transifex-client`: https://pypi.python.org/pypi/transifex-client
|
||||
.. _`sphinx-intl`: https://pypi.python.org/pypi/sphinx-intl
|
||||
.. _`transifex-client`: https://pypi.org/project/transifex-client/
|
||||
.. _`sphinx-intl`: https://pypi.org/project/sphinx-intl/
|
||||
.. _Transifex: https://www.transifex.com/
|
||||
.. _`sphinx translation page`: https://www.transifex.com/sphinx-doc/sphinx-doc/
|
||||
.. _`Transifex Client documentation`: http://docs.transifex.com/developer/client/
|
||||
|
@ -29,7 +29,7 @@ This section is intended to collect helpful hints for those wanting to migrate
|
||||
to reStructuredText/Sphinx from other documentation systems.
|
||||
|
||||
* Gerard Flanagan has written a script to convert pure HTML to reST; it can be
|
||||
found at the `Python Package Index <https://pypi.python.org/pypi/html2rest>`_.
|
||||
found at the `Python Package Index <https://pypi.org/project/html2rest/>`_.
|
||||
|
||||
* For converting the old Python docs to Sphinx, a converter was written which
|
||||
can be found at `the Python SVN repository
|
||||
@ -40,7 +40,7 @@ to reStructuredText/Sphinx from other documentation systems.
|
||||
markup; it is at `GitHub <https://github.com/wojdyr/db2rst>`_.
|
||||
|
||||
* Christophe de Vienne wrote a tool to convert from Open/LibreOffice documents
|
||||
to Sphinx: `odt2sphinx <https://pypi.python.org/pypi/odt2sphinx/>`_.
|
||||
to Sphinx: `odt2sphinx <https://pypi.org/project/odt2sphinx/>`_.
|
||||
|
||||
* To convert different markups, `Pandoc <https://pandoc.org/>`_ is
|
||||
a very helpful tool.
|
||||
|
@ -134,7 +134,7 @@ These themes are:
|
||||
Check out at its `installation page`_ how to set up properly
|
||||
:confval:`html_sidebars` for its use.
|
||||
|
||||
.. _Alabaster theme: https://pypi.python.org/pypi/alabaster
|
||||
.. _Alabaster theme: https://pypi.org/project/alabaster/
|
||||
.. _installation page: https://alabaster.readthedocs.io/en/latest/installation.html
|
||||
|
||||
* **classic** -- This is the classic theme, which looks like `the Python 2
|
||||
@ -418,7 +418,7 @@ Third Party Themes
|
||||
View a working demo over on readthedocs.org. You can get install and options
|
||||
information at `Read the Docs Sphinx Theme`_ page.
|
||||
|
||||
.. _Read the Docs Sphinx Theme: https://pypi.python.org/pypi/sphinx_rtd_theme
|
||||
.. _Read the Docs Sphinx Theme: https://pypi.org/project/sphinx_rtd_theme/
|
||||
|
||||
.. versionchanged:: 1.4
|
||||
**sphinx_rtd_theme** has become optional.
|
||||
@ -427,6 +427,6 @@ Third Party Themes
|
||||
Besides this, there are a lot of third party themes. You can find them on
|
||||
PyPI__, GitHub__, sphinx-themes.org__ and so on.
|
||||
|
||||
.. __: https://pypi.python.org/pypi?:action=browse&c=599
|
||||
.. __: https://pypi.org/search/?q=&o=&c=Framework+%3A%3A+Sphinx+%3A%3A+Theme
|
||||
.. __: https://github.com/search?utf8=%E2%9C%93&q=sphinx+theme&type=
|
||||
.. __: https://sphinx-themes.org/
|
||||
|
@ -132,7 +132,7 @@ Installation from PyPI
|
||||
----------------------
|
||||
|
||||
Sphinx packages are published on the `Python Package Index
|
||||
<https://pypi.python.org/pypi/Sphinx>`_. The preferred tool for installing
|
||||
<https://pypi.org/project/Sphinx/>`_. The preferred tool for installing
|
||||
packages from *PyPI* is :command:`pip`. This tool is provided with all modern
|
||||
versions of Python.
|
||||
|
||||
|
@ -508,11 +508,14 @@ or this::
|
||||
See the :duref:`reST reference for substitutions <substitution-definitions>`
|
||||
for details.
|
||||
|
||||
.. index:: ! pair: global; substitutions
|
||||
|
||||
If you want to use some substitutions for all documents, put them into
|
||||
:confval:`rst_prolog` or put them into a separate file and include it into all
|
||||
documents you want to use them in, using the :rst:dir:`include` directive. (Be
|
||||
sure to give the include file a file name extension differing from that of
|
||||
other source files, to avoid Sphinx finding it as a standalone document.)
|
||||
:confval:`rst_prolog` or :confval:`rst_epilog` or put them into a separate file
|
||||
and include it into all documents you want to use them in, using the
|
||||
:rst:dir:`include` directive. (Be sure to give the include file a file name
|
||||
extension differing from that of other source files, to avoid Sphinx finding it
|
||||
as a standalone document.)
|
||||
|
||||
Sphinx defines some default substitutions, see :ref:`default-substitutions`.
|
||||
|
||||
|
@ -1287,18 +1287,18 @@ Jinja_, Operation_, and Scala_.
|
||||
|
||||
.. _sphinx-contrib: https://bitbucket.org/birkenfeld/sphinx-contrib/
|
||||
|
||||
.. _Ada: https://pypi.python.org/pypi/sphinxcontrib-adadomain
|
||||
.. _Chapel: https://pypi.python.org/pypi/sphinxcontrib-chapeldomain
|
||||
.. _CoffeeScript: https://pypi.python.org/pypi/sphinxcontrib-coffee
|
||||
.. _Common Lisp: https://pypi.python.org/pypi/sphinxcontrib-cldomain
|
||||
.. _dqn: https://pypi.python.org/pypi/sphinxcontrib-dqndomain
|
||||
.. _Erlang: https://pypi.python.org/pypi/sphinxcontrib-erlangdomain
|
||||
.. _Go: https://pypi.python.org/pypi/sphinxcontrib-golangdomain
|
||||
.. _HTTP: https://pypi.python.org/pypi/sphinxcontrib-httpdomain
|
||||
.. _Jinja: https://pypi.python.org/pypi/sphinxcontrib-jinjadomain
|
||||
.. _Lasso: https://pypi.python.org/pypi/sphinxcontrib-lassodomain
|
||||
.. _MATLAB: https://pypi.python.org/pypi/sphinxcontrib-matlabdomain
|
||||
.. _Operation: https://pypi.python.org/pypi/sphinxcontrib-operationdomain
|
||||
.. _PHP: https://pypi.python.org/pypi/sphinxcontrib-phpdomain
|
||||
.. _Ada: https://pypi.org/project/sphinxcontrib-adadomain/
|
||||
.. _Chapel: https://pypi.org/project/sphinxcontrib-chapeldomain/
|
||||
.. _CoffeeScript: https://pypi.org/project/sphinxcontrib-coffee/
|
||||
.. _Common Lisp: https://pypi.org/project/sphinxcontrib-cldomain/
|
||||
.. _dqn: https://pypi.org/project/sphinxcontrib-dqndomain/
|
||||
.. _Erlang: https://pypi.org/project/sphinxcontrib-erlangdomain/
|
||||
.. _Go: https://pypi.org/project/sphinxcontrib-golangdomain/
|
||||
.. _HTTP: https://pypi.org/project/sphinxcontrib-httpdomain/
|
||||
.. _Jinja: https://pypi.org/project/sphinxcontrib-jinjadomain/
|
||||
.. _Lasso: https://pypi.org/project/sphinxcontrib-lassodomain/
|
||||
.. _MATLAB: https://pypi.org/project/sphinxcontrib-matlabdomain/
|
||||
.. _Operation: https://pypi.org/project/sphinxcontrib-operationdomain/
|
||||
.. _PHP: https://pypi.org/project/sphinxcontrib-phpdomain/
|
||||
.. _Ruby: https://bitbucket.org/birkenfeld/sphinx-contrib/src/default/rubydomain
|
||||
.. _Scala: https://pypi.python.org/pypi/sphinxcontrib-scaladomain
|
||||
.. _Scala: https://pypi.org/project/sphinxcontrib-scaladomain/
|
||||
|
2
setup.py
2
setup.py
@ -176,7 +176,7 @@ setup(
|
||||
name='Sphinx',
|
||||
version=sphinx.__version__,
|
||||
url='http://sphinx-doc.org/',
|
||||
download_url='https://pypi.python.org/pypi/Sphinx',
|
||||
download_url='https://pypi.org/project/Sphinx/',
|
||||
license='BSD',
|
||||
author='Georg Brandl',
|
||||
author_email='georg@python.org',
|
||||
|
@ -26,14 +26,13 @@ from six.moves import cStringIO
|
||||
|
||||
import sphinx
|
||||
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 (
|
||||
RemovedInSphinx20Warning, RemovedInSphinx30Warning, RemovedInSphinx40Warning
|
||||
)
|
||||
from sphinx.environment import BuildEnvironment
|
||||
from sphinx.errors import (
|
||||
ApplicationError, ConfigError, ExtensionError, VersionRequirementError
|
||||
)
|
||||
from sphinx.errors import ApplicationError, ConfigError, VersionRequirementError
|
||||
from sphinx.events import EventManager
|
||||
from sphinx.locale import __
|
||||
from sphinx.registry import SphinxComponentRegistry
|
||||
@ -110,7 +109,6 @@ builtin_extensions = (
|
||||
'alabaster',
|
||||
) # type: Tuple[unicode, ...]
|
||||
|
||||
CONFIG_FILENAME = 'conf.py'
|
||||
ENV_PICKLE_FILENAME = 'environment.pickle'
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -189,10 +187,11 @@ class Sphinx(object):
|
||||
|
||||
# read config
|
||||
self.tags = Tags(tags)
|
||||
self.config = Config(self.confdir, CONFIG_FILENAME,
|
||||
confoverrides or {}, self.tags)
|
||||
self.config.check_unicode()
|
||||
# defer checking types until i18n has been initialized
|
||||
if self.confdir is None:
|
||||
self.config = Config({}, confoverrides or {})
|
||||
else:
|
||||
self.config = Config.read(self.confdir, confoverrides or {}, self.tags)
|
||||
check_unicode(self.config)
|
||||
|
||||
# initialize some limited config variables before initialize i18n and loading
|
||||
# extensions
|
||||
@ -250,8 +249,6 @@ class Sphinx(object):
|
||||
|
||||
# create the builder
|
||||
self.builder = self.create_builder(buildername)
|
||||
# check all configuration values for permissible types
|
||||
self.config.check_types()
|
||||
# set up the build environment
|
||||
self._init_env(freshenv)
|
||||
# set up the builder
|
||||
@ -561,8 +558,6 @@ class Sphinx(object):
|
||||
"""
|
||||
logger.debug('[app] adding config value: %r',
|
||||
(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):
|
||||
rebuild = rebuild and 'env' or ''
|
||||
self.config.add(name, default, rebuild, types)
|
||||
@ -970,7 +965,30 @@ class Sphinx(object):
|
||||
Add the standard docutils :class:`Transform` subclass *transform* to
|
||||
the list of transforms that are applied after Sphinx parses a reST
|
||||
document.
|
||||
"""
|
||||
|
||||
.. list-table:: priority range categories for Sphinx transforms
|
||||
|
||||
* - Priority
|
||||
- Main purpose in Sphinx
|
||||
* - 0-99
|
||||
- Fix invalid nodes by docutils. Translate a doctree.
|
||||
* - 100-299
|
||||
- Preparation
|
||||
* - 300-399
|
||||
- early
|
||||
* - 400-699
|
||||
- main
|
||||
* - 700-799
|
||||
- Post processing. Deadline to modify text and referencing.
|
||||
* - 800-899
|
||||
- Collect referencing and referenced nodes. Domain processing.
|
||||
* - 900-999
|
||||
- Finalize and clean up.
|
||||
|
||||
refs: `Transform Priority Range Categories`__
|
||||
|
||||
__ http://docutils.sourceforge.net/docs/ref/transforms.html#transform-priority-range-categories
|
||||
""" # NOQA
|
||||
self.registry.add_transform(transform)
|
||||
|
||||
def add_post_transform(self, transform):
|
||||
|
@ -248,6 +248,8 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder):
|
||||
outdir += os.sep
|
||||
olen = len(outdir)
|
||||
for root, dirs, files in os.walk(outdir):
|
||||
dirs.sort()
|
||||
files.sort()
|
||||
staticdir = root.startswith(path.join(outdir, '_static'))
|
||||
for fn in sorted(files):
|
||||
if (staticdir and not fn.endswith('.js')) or \
|
||||
|
@ -25,7 +25,6 @@ from sphinx.environment.adapters.asset import ImageAdapter
|
||||
from sphinx.errors import SphinxError, ConfigError
|
||||
from sphinx.locale import _, __
|
||||
from sphinx.transforms import SphinxTransformer
|
||||
from sphinx.transforms.references import SubstitutionDefinitionsRemover
|
||||
from sphinx.util import texescape, logging, status_iterator
|
||||
from sphinx.util.console import bold, darkgreen # type: ignore
|
||||
from sphinx.util.docutils import new_document
|
||||
@ -217,7 +216,6 @@ class LaTeXBuilder(Builder):
|
||||
# type: (nodes.document) -> None
|
||||
transformer = SphinxTransformer(doctree)
|
||||
transformer.set_environment(self.env)
|
||||
transformer.add_transforms([SubstitutionDefinitionsRemover])
|
||||
transformer.apply_transforms()
|
||||
|
||||
def finish(self):
|
||||
@ -325,7 +323,7 @@ def setup(app):
|
||||
app.add_config_value('latex_appendices', [], None)
|
||||
app.add_config_value('latex_use_latex_multicolumn', False, 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_show_urls', 'no', None)
|
||||
app.add_config_value('latex_show_pagerefs', False, None)
|
161
sphinx/builders/latex/transforms.py
Normal file
161
sphinx/builders/latex/transforms.py
Normal file
@ -0,0 +1,161 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
sphinx.builders.latex.transforms
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Transforms for LaTeX builder.
|
||||
|
||||
:copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
from docutils import nodes
|
||||
|
||||
from sphinx.transforms import SphinxTransform
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Dict, List, Set, Union # NOQA
|
||||
|
||||
URI_SCHEMES = ('mailto:', 'http:', 'https:', 'ftp:')
|
||||
|
||||
|
||||
class ShowUrlsTransform(SphinxTransform):
|
||||
"""Expand references to inline text or footnotes.
|
||||
|
||||
For more information, see :confval:`latex_show_urls`.
|
||||
"""
|
||||
default_priority = 400
|
||||
|
||||
# references are expanded to footnotes (or not)
|
||||
expanded = False
|
||||
|
||||
def apply(self):
|
||||
# type: () -> None
|
||||
# replace id_prefix temporarily
|
||||
id_prefix = self.document.settings.id_prefix
|
||||
self.document.settings.id_prefix = 'show_urls'
|
||||
|
||||
self.expand_show_urls()
|
||||
if self.expanded:
|
||||
self.renumber_footnotes()
|
||||
|
||||
# restore id_prefix
|
||||
self.document.settings.id_prefix = id_prefix
|
||||
|
||||
def expand_show_urls(self):
|
||||
# type: () -> None
|
||||
show_urls = self.document.settings.env.config.latex_show_urls
|
||||
if show_urls is False or show_urls == 'no':
|
||||
return
|
||||
|
||||
for node in self.document.traverse(nodes.reference):
|
||||
uri = node.get('refuri', '')
|
||||
if uri.startswith(URI_SCHEMES):
|
||||
if uri.startswith('mailto:'):
|
||||
uri = uri[7:]
|
||||
if node.astext() != uri:
|
||||
index = node.parent.index(node)
|
||||
if show_urls == 'footnote':
|
||||
footnote_nodes = self.create_footnote(uri)
|
||||
for i, fn in enumerate(footnote_nodes):
|
||||
node.parent.insert(index + i + 1, fn)
|
||||
|
||||
self.expanded = True
|
||||
else: # all other true values (b/w compat)
|
||||
textnode = nodes.Text(" (%s)" % uri)
|
||||
node.parent.insert(index + 1, textnode)
|
||||
|
||||
def create_footnote(self, uri):
|
||||
# type: (unicode) -> List[Union[nodes.footnote, nodes.footnote_ref]]
|
||||
label = nodes.label('', '#')
|
||||
para = nodes.paragraph()
|
||||
para.append(nodes.reference('', nodes.Text(uri), refuri=uri, nolinkurl=True))
|
||||
footnote = nodes.footnote(uri, label, para, auto=1)
|
||||
footnote['names'].append('#')
|
||||
self.document.note_autofootnote(footnote)
|
||||
|
||||
label = nodes.Text('#')
|
||||
footnote_ref = nodes.footnote_reference('[#]_', label, auto=1,
|
||||
refid=footnote['ids'][0])
|
||||
self.document.note_autofootnote_ref(footnote_ref)
|
||||
footnote.add_backref(footnote_ref['ids'][0])
|
||||
|
||||
return [footnote, footnote_ref]
|
||||
|
||||
def renumber_footnotes(self):
|
||||
# type: () -> None
|
||||
collector = FootnoteCollector(self.document)
|
||||
self.document.walkabout(collector)
|
||||
|
||||
num = 0
|
||||
for document, footnote in collector.auto_footnotes:
|
||||
# search unused footnote number
|
||||
while True:
|
||||
num += 1
|
||||
if str(num) not in collector.used_footnote_numbers:
|
||||
break
|
||||
|
||||
# assign new footnote number
|
||||
old_label = footnote[0].astext()
|
||||
footnote[0].replace_self(nodes.label('', str(num)))
|
||||
if old_label in footnote['names']:
|
||||
footnote['names'].remove(old_label)
|
||||
footnote['names'].append(str(num))
|
||||
|
||||
# update footnote_references by new footnote number
|
||||
for ref in collector.footnote_refs.get(document, []):
|
||||
if footnote['ids'][0] == ref['refid']:
|
||||
ref.remove(ref[0])
|
||||
ref += nodes.Text(str(num))
|
||||
|
||||
|
||||
class FootnoteCollector(nodes.NodeVisitor):
|
||||
"""Collect footnotes and footnote references on the document"""
|
||||
|
||||
def __init__(self, document):
|
||||
# type: (nodes.document) -> None
|
||||
self.auto_footnotes = [] # type: List[nodes.footnote]
|
||||
self.used_footnote_numbers = set() # type: Set[unicode]
|
||||
self.footnote_refs = {} # type: Dict[nodes.Node, List[nodes.footnote_reference]] # NOQA
|
||||
self.current_document = [] # type: List[nodes.Node]
|
||||
nodes.NodeVisitor.__init__(self, document)
|
||||
|
||||
def visit_document(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.current_document.append(node)
|
||||
|
||||
def depart_document(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.current_document.pop()
|
||||
|
||||
def visit_start_of_file(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.current_document.append(node)
|
||||
|
||||
def depart_start_of_file(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.current_document.pop()
|
||||
|
||||
def unknown_visit(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
pass
|
||||
|
||||
def visit_footnote(self, node):
|
||||
# type: (nodes.footnote) -> None
|
||||
document = self.current_document[-1]
|
||||
if node.get('auto'):
|
||||
self.auto_footnotes.append((document, node))
|
||||
else:
|
||||
for name in node['names']:
|
||||
self.used_footnote_numbers.add(name)
|
||||
|
||||
def visit_footnote_reference(self, node):
|
||||
# type: (nodes.footnote_reference) -> None
|
||||
document = self.current_document[-1]
|
||||
footnote_refs = self.footnote_refs.setdefault(document, [])
|
||||
footnote_refs.append(node)
|
||||
|
||||
def unknown_departure(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
pass
|
270
sphinx/config.py
270
sphinx/config.py
@ -11,13 +11,15 @@
|
||||
|
||||
import re
|
||||
import traceback
|
||||
import warnings
|
||||
from collections import OrderedDict
|
||||
from os import path, getenv
|
||||
from typing import Any, NamedTuple, Union
|
||||
|
||||
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.util import logging
|
||||
from sphinx.util.i18n import format_date
|
||||
@ -26,28 +28,15 @@ from sphinx.util.pycompat import execfile_, NoneType
|
||||
|
||||
if False:
|
||||
# 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.util.tags import Tags # NOQA
|
||||
|
||||
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})(?=[ ,])')
|
||||
|
||||
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:
|
||||
unicode = str # special alias for static typing...
|
||||
|
||||
@ -144,11 +133,7 @@ class Config(object):
|
||||
nitpick_ignore = ([], None),
|
||||
numfig = (False, 'env'),
|
||||
numfig_secnum_depth = (1, 'env'),
|
||||
numfig_format = ({'section': _('Section %s'),
|
||||
'figure': _('Fig. %s'),
|
||||
'table': _('Table %s'),
|
||||
'code-block': _('Listing %s')},
|
||||
'env'),
|
||||
numfig_format = ({}, 'env'), # will be initialized in init_numfig_format()
|
||||
|
||||
tls_verify = (True, 'env'),
|
||||
tls_cacerts = (None, 'env'),
|
||||
@ -159,30 +144,30 @@ class Config(object):
|
||||
'env'),
|
||||
) # type: Dict[unicode, Tuple]
|
||||
|
||||
def __init__(self, dirname, filename, overrides, tags):
|
||||
# type: (unicode, unicode, Dict, Tags) -> None
|
||||
def __init__(self, *args):
|
||||
# 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.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
|
||||
# these two must be preinitialized because extensions can add their
|
||||
# own config values
|
||||
self.setup = config.get('setup', None) # type: Callable
|
||||
|
||||
if 'extensions' in overrides:
|
||||
@ -192,69 +177,25 @@ class Config(object):
|
||||
config['extensions'] = overrides.pop('extensions')
|
||||
self.extensions = config.get('extensions', []) # type: List[unicode]
|
||||
|
||||
# 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:
|
||||
config[k] = copyright_year_re.sub(r'\g<1>%s' % format_date('%Y'),
|
||||
config[k])
|
||||
@classmethod
|
||||
def read(cls, confdir, overrides=None, tags=None):
|
||||
# type: (unicode, Dict, Tags) -> Config
|
||||
"""Create a Config object from configuration file."""
|
||||
filename = path.join(confdir, CONFIG_FILENAME)
|
||||
namespace = eval_config_file(filename, tags)
|
||||
return cls(namespace, overrides or {})
|
||||
|
||||
def check_types(self):
|
||||
# type: () -> None
|
||||
# check all values for deviation from the default value's type, since
|
||||
# that can result in TypeErrors all over the place
|
||||
# NB. since config values might use _() we have to wait with calling
|
||||
# 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)))
|
||||
warnings.warn('Config.check_types() is deprecated. Use check_confval_types() instead.',
|
||||
RemovedInSphinx30Warning)
|
||||
check_confval_types(None, self)
|
||||
|
||||
def check_unicode(self):
|
||||
# type: () -> None
|
||||
# check all string values for non-ASCII characters in bytestrings,
|
||||
# since that can result in UnicodeErrors all over the place
|
||||
for name, value in iteritems(self._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')
|
||||
warnings.warn('Config.check_unicode() is deprecated. Use check_unicode() instead.',
|
||||
RemovedInSphinx30Warning)
|
||||
check_unicode(self)
|
||||
|
||||
def convert_overrides(self, name, value):
|
||||
# type: (unicode, Any) -> Any
|
||||
@ -350,19 +291,49 @@ class Config(object):
|
||||
return name in self.values
|
||||
|
||||
def __iter__(self):
|
||||
# type: () -> Iterable[ConfigValue]
|
||||
# type: () -> Generator[ConfigValue, None, None]
|
||||
for name, value in iteritems(self.values):
|
||||
yield ConfigValue(name, getattr(self, name), value[1]) # type: ignore
|
||||
|
||||
def add(self, name, default, rebuild, types):
|
||||
# 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):
|
||||
# type: (Union[unicode, List[unicode]]) -> Iterator[ConfigValue]
|
||||
if isinstance(rebuild, string_types):
|
||||
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):
|
||||
@ -391,9 +362,104 @@ def convert_source_suffix(app, config):
|
||||
"But `%r' is given." % source_suffix))
|
||||
|
||||
|
||||
def init_numfig_format(app, config):
|
||||
# type: (Sphinx, Config) -> None
|
||||
"""Initialize :confval:`numfig_format`."""
|
||||
numfig_format = {'section': _('Section %s'),
|
||||
'figure': _('Fig. %s'),
|
||||
'table': _('Table %s'),
|
||||
'code-block': _('Listing %s')}
|
||||
|
||||
# override default labels by configuration
|
||||
numfig_format.update(config.numfig_format)
|
||||
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):
|
||||
# type: (Sphinx) -> Dict[unicode, Any]
|
||||
app.connect('config-inited', convert_source_suffix)
|
||||
app.connect('config-inited', init_numfig_format)
|
||||
app.connect('config-inited', correct_copyright_year)
|
||||
app.connect('config-inited', check_confval_types)
|
||||
|
||||
return {
|
||||
'version': 'builtin',
|
||||
|
@ -7,6 +7,9 @@
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import re
|
||||
from contextlib import contextmanager
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst import directives
|
||||
from docutils.parsers.rst.directives.admonitions import BaseAdmonition
|
||||
@ -42,6 +45,8 @@ locale.versionlabels = DeprecatedDict(
|
||||
RemovedInSphinx30Warning
|
||||
)
|
||||
|
||||
glob_re = re.compile('.*[*?\[].*')
|
||||
|
||||
|
||||
def int_or_nothing(argument):
|
||||
# type: (unicode) -> int
|
||||
@ -73,29 +78,50 @@ class TocTree(SphinxDirective):
|
||||
|
||||
def run(self):
|
||||
# type: () -> List[nodes.Node]
|
||||
suffixes = self.config.source_suffix
|
||||
glob = 'glob' in self.options
|
||||
subnode = addnodes.toctree()
|
||||
subnode['parent'] = self.env.docname
|
||||
|
||||
ret = []
|
||||
# (title, ref) pairs, where ref may be a document, or an external link,
|
||||
# and title may be None if the document's title is to be used
|
||||
entries = [] # type: List[Tuple[unicode, unicode]]
|
||||
includefiles = []
|
||||
subnode['entries'] = []
|
||||
subnode['includefiles'] = []
|
||||
subnode['maxdepth'] = self.options.get('maxdepth', -1)
|
||||
subnode['caption'] = self.options.get('caption')
|
||||
subnode['glob'] = 'glob' in self.options
|
||||
subnode['hidden'] = 'hidden' in self.options
|
||||
subnode['includehidden'] = 'includehidden' in self.options
|
||||
subnode['numbered'] = self.options.get('numbered', 0)
|
||||
subnode['titlesonly'] = 'titlesonly' in self.options
|
||||
set_source_info(self, subnode)
|
||||
wrappernode = nodes.compound(classes=['toctree-wrapper'])
|
||||
wrappernode.append(subnode)
|
||||
self.add_name(wrappernode)
|
||||
|
||||
ret = self.parse_content(subnode)
|
||||
ret.append(wrappernode)
|
||||
return ret
|
||||
|
||||
def parse_content(self, toctree):
|
||||
suffixes = self.config.source_suffix
|
||||
|
||||
# glob target documents
|
||||
all_docnames = self.env.found_docs.copy()
|
||||
# don't add the currently visited file in catch-all patterns
|
||||
all_docnames.remove(self.env.docname)
|
||||
all_docnames.remove(self.env.docname) # remove current document
|
||||
|
||||
ret = []
|
||||
for entry in self.content:
|
||||
if not entry:
|
||||
continue
|
||||
# look for explicit titles ("Some Title <document>")
|
||||
explicit = explicit_title_re.match(entry)
|
||||
if glob and ('*' in entry or '?' in entry or '[' in entry) and not explicit:
|
||||
if (toctree['glob'] and glob_re.match(entry) and
|
||||
not explicit and not url_re.match(entry)):
|
||||
patname = docname_join(self.env.docname, entry)
|
||||
docnames = sorted(patfilter(all_docnames, patname)) # type: ignore
|
||||
docnames = sorted(patfilter(all_docnames, patname))
|
||||
for docname in docnames:
|
||||
all_docnames.remove(docname) # don't include it again
|
||||
entries.append((None, docname))
|
||||
includefiles.append(docname)
|
||||
toctree['entries'].append((None, docname))
|
||||
toctree['includefiles'].append(docname)
|
||||
if not docnames:
|
||||
ret.append(self.state.document.reporter.warning(
|
||||
'toctree glob pattern %r didn\'t match any documents'
|
||||
@ -116,7 +142,7 @@ class TocTree(SphinxDirective):
|
||||
# absolutize filenames
|
||||
docname = docname_join(self.env.docname, docname)
|
||||
if url_re.match(ref) or ref == 'self':
|
||||
entries.append((title, ref))
|
||||
toctree['entries'].append((title, ref))
|
||||
elif docname not in self.env.found_docs:
|
||||
ret.append(self.state.document.reporter.warning(
|
||||
'toctree contains reference to nonexisting '
|
||||
@ -124,28 +150,13 @@ class TocTree(SphinxDirective):
|
||||
self.env.note_reread()
|
||||
else:
|
||||
all_docnames.discard(docname)
|
||||
entries.append((title, docname))
|
||||
includefiles.append(docname)
|
||||
subnode = addnodes.toctree()
|
||||
subnode['parent'] = self.env.docname
|
||||
toctree['entries'].append((title, docname))
|
||||
toctree['includefiles'].append(docname)
|
||||
|
||||
# entries contains all entries (self references, external links etc.)
|
||||
if 'reversed' in self.options:
|
||||
entries.reverse()
|
||||
subnode['entries'] = entries
|
||||
# includefiles only entries that are documents
|
||||
subnode['includefiles'] = includefiles
|
||||
subnode['maxdepth'] = self.options.get('maxdepth', -1)
|
||||
subnode['caption'] = self.options.get('caption')
|
||||
subnode['glob'] = glob
|
||||
subnode['hidden'] = 'hidden' in self.options
|
||||
subnode['includehidden'] = 'includehidden' in self.options
|
||||
subnode['numbered'] = self.options.get('numbered', 0)
|
||||
subnode['titlesonly'] = 'titlesonly' in self.options
|
||||
set_source_info(self, subnode)
|
||||
wrappernode = nodes.compound(classes=['toctree-wrapper'])
|
||||
wrappernode.append(subnode)
|
||||
self.add_name(wrappernode)
|
||||
ret.append(wrappernode)
|
||||
toctree['entries'] = list(reversed(toctree['entries']))
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
@ -426,6 +437,7 @@ class Include(BaseInclude, SphinxDirective):
|
||||
|
||||
def run(self):
|
||||
# type: () -> List[nodes.Node]
|
||||
current_filename = self.env.doc2path(self.env.docname)
|
||||
if self.arguments[0].startswith('<') and \
|
||||
self.arguments[0].endswith('>'):
|
||||
# docutils "standard" includes, do not do path processing
|
||||
@ -433,7 +445,27 @@ class Include(BaseInclude, SphinxDirective):
|
||||
rel_filename, filename = self.env.relfn2path(self.arguments[0])
|
||||
self.arguments[0] = filename
|
||||
self.env.note_included(filename)
|
||||
return BaseInclude.run(self)
|
||||
with patched_warnings(self, current_filename):
|
||||
return BaseInclude.run(self)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def patched_warnings(directive, parent_filename):
|
||||
# type: (BaseInclude, unicode) -> Generator[None, None, None]
|
||||
"""Add includee filename to the warnings during inclusion."""
|
||||
try:
|
||||
original = directive.state_machine.insert_input
|
||||
|
||||
def insert_input(input_lines, source):
|
||||
# type: (Any, unicode) -> None
|
||||
source += ' <included from %s>' % parent_filename
|
||||
original(input_lines, source)
|
||||
|
||||
# patch insert_input() temporarily
|
||||
directive.state_machine.insert_input = insert_input
|
||||
yield
|
||||
finally:
|
||||
directive.state_machine.insert_input = original
|
||||
|
||||
|
||||
def setup(app):
|
||||
|
@ -579,9 +579,10 @@ class PyModule(Directive):
|
||||
env.ref_context['py:module'] = modname
|
||||
ret = []
|
||||
if not noindex:
|
||||
env.domaindata['py']['modules'][modname] = \
|
||||
(env.docname, self.options.get('synopsis', ''),
|
||||
self.options.get('platform', ''), 'deprecated' in self.options)
|
||||
env.domaindata['py']['modules'][modname] = (env.docname,
|
||||
self.options.get('synopsis', ''),
|
||||
self.options.get('platform', ''),
|
||||
'deprecated' in self.options)
|
||||
# make a duplicate entry in 'objects' to facilitate searching for
|
||||
# the module in PythonDomain.find_obj()
|
||||
env.domaindata['py']['objects'][modname] = (env.docname, 'module')
|
||||
|
@ -21,10 +21,10 @@ from os import path
|
||||
|
||||
from docutils.frontend import OptionParser
|
||||
from docutils.utils import Reporter, get_source_line
|
||||
from six import BytesIO, itervalues, class_types, next
|
||||
from six import BytesIO, class_types, next
|
||||
from six.moves import cPickle as pickle
|
||||
|
||||
from sphinx import addnodes, versioning
|
||||
from sphinx import addnodes
|
||||
from sphinx.deprecation import RemovedInSphinx20Warning, RemovedInSphinx30Warning
|
||||
from sphinx.environment.adapters.indexentries import IndexEntries
|
||||
from sphinx.environment.adapters.toctree import TocTree
|
||||
@ -570,14 +570,6 @@ class BuildEnvironment(object):
|
||||
with sphinx_domains(self), rst.default_role(docname, self.config.default_role):
|
||||
doctree = read_doc(self.app, self, self.doc2path(docname))
|
||||
|
||||
# post-processing
|
||||
for domain in itervalues(self.domains):
|
||||
domain.process_doc(self, docname, doctree)
|
||||
|
||||
# allow extension-specific post-processing
|
||||
if app:
|
||||
app.emit('doctree-read', doctree)
|
||||
|
||||
# store time of reading, for outdated files detection
|
||||
# (Some filesystems have coarse timestamp resolution;
|
||||
# therefore time.time() can be older than filesystem's timestamp.
|
||||
@ -585,10 +577,6 @@ class BuildEnvironment(object):
|
||||
self.all_docs[docname] = max(
|
||||
time.time(), path.getmtime(self.doc2path(docname)))
|
||||
|
||||
if self.versioning_condition:
|
||||
# add uids for versioning
|
||||
versioning.prepare(doctree)
|
||||
|
||||
# cleanup
|
||||
self.temp_data.clear()
|
||||
self.ref_context.clear()
|
||||
|
@ -20,8 +20,8 @@ from docutils.statemachine import ViewList
|
||||
from six import iteritems, itervalues, text_type, class_types, string_types
|
||||
|
||||
import sphinx
|
||||
from sphinx.application import ExtensionError
|
||||
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 _MockImporter # to keep compatibility # NOQA
|
||||
from sphinx.ext.autodoc.inspector import format_annotation, formatargspec # to keep compatibility # NOQA
|
||||
|
@ -57,7 +57,7 @@ class IfConfig(Directive):
|
||||
|
||||
def process_ifconfig_nodes(app, doctree, docname):
|
||||
# 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['builder'] = app.builder.name
|
||||
for node in doctree.traverse(ifconfig):
|
||||
|
@ -13,7 +13,7 @@
|
||||
from docutils import nodes
|
||||
|
||||
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 setup_math as mathbase_setup
|
||||
from sphinx.locale import _
|
||||
@ -26,7 +26,7 @@ if False:
|
||||
|
||||
def html_visit_math(self, node):
|
||||
# type: (nodes.NodeVisitor, nodes.Node) -> None
|
||||
self.body.append(self.starttag(node, 'span', '', CLASS='math notranslate'))
|
||||
self.body.append(self.starttag(node, 'span', '', CLASS='math notranslate nohighlight'))
|
||||
self.body.append(self.encode(node['latex']) + '</span>')
|
||||
raise nodes.SkipNode
|
||||
|
||||
@ -34,7 +34,7 @@ def html_visit_math(self, node):
|
||||
def html_visit_displaymath(self, node):
|
||||
# type: (nodes.NodeVisitor, nodes.Node) -> None
|
||||
if node['nowrap']:
|
||||
self.body.append(self.starttag(node, 'div', CLASS='math notranslate'))
|
||||
self.body.append(self.starttag(node, 'div', CLASS='math notranslate nohighlight'))
|
||||
self.body.append(self.encode(node['latex']))
|
||||
self.body.append('</div>')
|
||||
raise nodes.SkipNode
|
||||
@ -47,7 +47,7 @@ def html_visit_displaymath(self, node):
|
||||
self.body.append('<span class="eqno">(%s)' % number)
|
||||
self.add_permalink_ref(node, _('Permalink to this equation'))
|
||||
self.body.append('</span>')
|
||||
self.body.append(self.starttag(node, 'div', CLASS='math notranslate'))
|
||||
self.body.append(self.starttag(node, 'div', CLASS='math notranslate nohighlight'))
|
||||
else:
|
||||
# but only once!
|
||||
self.body.append('<div class="math">')
|
||||
|
@ -27,7 +27,7 @@ if False:
|
||||
|
||||
def html_visit_math(self, node):
|
||||
# type: (nodes.NodeVisitor, nodes.Node) -> None
|
||||
self.body.append(self.starttag(node, 'span', '', CLASS='math notranslate'))
|
||||
self.body.append(self.starttag(node, 'span', '', CLASS='math notranslate nohighlight'))
|
||||
self.body.append(self.builder.config.mathjax_inline[0] +
|
||||
self.encode(node['latex']) +
|
||||
self.builder.config.mathjax_inline[1] + '</span>')
|
||||
@ -36,7 +36,7 @@ def html_visit_math(self, node):
|
||||
|
||||
def html_visit_displaymath(self, node):
|
||||
# type: (nodes.NodeVisitor, nodes.Node) -> None
|
||||
self.body.append(self.starttag(node, 'div', CLASS='math notranslate'))
|
||||
self.body.append(self.starttag(node, 'div', CLASS='math notranslate nohighlight'))
|
||||
if node['nowrap']:
|
||||
self.body.append(self.encode(node['latex']))
|
||||
self.body.append('</div>')
|
||||
|
11
sphinx/io.py
11
sphinx/io.py
@ -24,15 +24,17 @@ from sphinx.transforms import (
|
||||
ApplySourceWorkaround, ExtraTranslatableNodes, CitationReferences,
|
||||
DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks, SortIds,
|
||||
AutoNumbering, AutoIndexUpgrader, FilterSystemMessages,
|
||||
UnreferencedFootnotesDetector, SphinxSmartQuotes, ManpageLink
|
||||
UnreferencedFootnotesDetector, SphinxSmartQuotes, DoctreeReadEvent, ManpageLink
|
||||
)
|
||||
from sphinx.transforms import SphinxTransformer
|
||||
from sphinx.transforms.compact_bullet_list import RefOnlyBulletListTransform
|
||||
from sphinx.transforms.i18n import (
|
||||
PreserveTranslatableMessages, Locale, RemoveTranslatableInline,
|
||||
)
|
||||
from sphinx.transforms.references import SphinxDomains, SubstitutionDefinitionsRemover
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.docutils import LoggingReporter
|
||||
from sphinx.versioning import UIDTransform
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
@ -93,7 +95,9 @@ class SphinxStandaloneReader(SphinxBaseReader):
|
||||
Locale, CitationReferences, DefaultSubstitutions, MoveModuleTargets,
|
||||
HandleCodeBlocks, AutoNumbering, AutoIndexUpgrader, SortIds,
|
||||
RemoveTranslatableInline, FilterSystemMessages, RefOnlyBulletListTransform,
|
||||
UnreferencedFootnotesDetector, SphinxSmartQuotes, ManpageLink
|
||||
UnreferencedFootnotesDetector, SphinxSmartQuotes, ManpageLink,
|
||||
SphinxDomains, SubstitutionDefinitionsRemover, DoctreeReadEvent,
|
||||
UIDTransform,
|
||||
] # type: List[Transform]
|
||||
|
||||
def __init__(self, app, *args, **kwargs):
|
||||
@ -116,7 +120,8 @@ class SphinxI18nReader(SphinxBaseReader):
|
||||
DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks,
|
||||
AutoNumbering, SortIds, RemoveTranslatableInline,
|
||||
FilterSystemMessages, RefOnlyBulletListTransform,
|
||||
UnreferencedFootnotesDetector, SphinxSmartQuotes, ManpageLink]
|
||||
UnreferencedFootnotesDetector, SphinxSmartQuotes, ManpageLink,
|
||||
SubstitutionDefinitionsRemover]
|
||||
|
||||
def set_lineno_for_reporter(self, lineno):
|
||||
# type: (int) -> None
|
||||
|
@ -425,7 +425,7 @@ msgstr "行规范 %r:未能从包含文件 %r 中拉取行"
|
||||
|
||||
#: sphinx/directives/other.py:157
|
||||
msgid "Section author: "
|
||||
msgstr "节作者:"
|
||||
msgstr "章节作者:"
|
||||
|
||||
#: sphinx/directives/other.py:159
|
||||
msgid "Module author: "
|
||||
@ -878,7 +878,7 @@ msgstr "格式转换程序退出并报错:\n[stderr]\n%s\n[stdout]\n%s"
|
||||
|
||||
#: sphinx/ext/imgmath.py:338 sphinx/ext/jsmath.py:41 sphinx/ext/mathjax.py:42
|
||||
msgid "Permalink to this equation"
|
||||
msgstr "永久链接至公式"
|
||||
msgstr "公式的永久链接"
|
||||
|
||||
#: sphinx/ext/intersphinx.py:339
|
||||
#, python-format
|
||||
@ -1047,7 +1047,7 @@ msgstr "搜索"
|
||||
|
||||
#: sphinx/themes/agogo/layout.html:54 sphinx/themes/basic/searchbox.html:16
|
||||
msgid "Go"
|
||||
msgstr "转向"
|
||||
msgstr "搜"
|
||||
|
||||
#: sphinx/themes/agogo/layout.html:81 sphinx/themes/basic/sourcelink.html:15
|
||||
msgid "Show Source"
|
||||
@ -1250,13 +1250,13 @@ msgstr "其他更改"
|
||||
#: sphinx/writers/html.py:410 sphinx/writers/html5.py:351
|
||||
#: sphinx/writers/html5.py:356
|
||||
msgid "Permalink to this headline"
|
||||
msgstr "永久链接至标题"
|
||||
msgstr "标题的永久链接"
|
||||
|
||||
#: sphinx/themes/basic/static/doctools.js_t:199 sphinx/writers/html.py:126
|
||||
#: sphinx/writers/html.py:137 sphinx/writers/html5.py:95
|
||||
#: sphinx/writers/html5.py:106
|
||||
msgid "Permalink to this definition"
|
||||
msgstr "永久链接至目标"
|
||||
msgstr "定义的永久链接"
|
||||
|
||||
#: sphinx/themes/basic/static/doctools.js_t:232
|
||||
msgid "Hide Search Matches"
|
||||
@ -1313,19 +1313,19 @@ msgstr "当添加指令类时,不应该给定额外参数"
|
||||
|
||||
#: sphinx/writers/html.py:414 sphinx/writers/html5.py:360
|
||||
msgid "Permalink to this table"
|
||||
msgstr "永久链接至表格"
|
||||
msgstr "表格的永久链接"
|
||||
|
||||
#: sphinx/writers/html.py:466 sphinx/writers/html5.py:412
|
||||
msgid "Permalink to this code"
|
||||
msgstr "永久链接至代码"
|
||||
msgstr "代码的永久链接"
|
||||
|
||||
#: sphinx/writers/html.py:470 sphinx/writers/html5.py:416
|
||||
msgid "Permalink to this image"
|
||||
msgstr "永久链接至图片"
|
||||
msgstr "图片的永久链接"
|
||||
|
||||
#: sphinx/writers/html.py:472 sphinx/writers/html5.py:418
|
||||
msgid "Permalink to this toctree"
|
||||
msgstr "永久链接至目录树"
|
||||
msgstr "目录的永久链接"
|
||||
|
||||
#: sphinx/writers/latex.py:554
|
||||
msgid "Release"
|
||||
|
@ -1046,7 +1046,7 @@ msgstr "搜尋"
|
||||
|
||||
#: sphinx/themes/agogo/layout.html:54 sphinx/themes/basic/searchbox.html:16
|
||||
msgid "Go"
|
||||
msgstr "前往"
|
||||
msgstr "搜"
|
||||
|
||||
#: sphinx/themes/agogo/layout.html:81 sphinx/themes/basic/sourcelink.html:15
|
||||
msgid "Show Source"
|
||||
|
@ -471,6 +471,7 @@ class SphinxComponentRegistry(object):
|
||||
logger.warning(__('extension %r returned an unsupported object from '
|
||||
'its setup() function; it should return None or a '
|
||||
'metadata dictionary'), extname)
|
||||
metadata = {}
|
||||
|
||||
app.extensions[extname] = Extension(extname, mod, **metadata)
|
||||
app._setting_up_extension.pop()
|
||||
|
@ -318,4 +318,4 @@ class SearchGerman(SearchLanguage):
|
||||
|
||||
def stem(self, word):
|
||||
# type: (unicode) -> unicode
|
||||
return self.stemmer.stemWord(word)
|
||||
return self.stemmer.stemWord(word.lower())
|
||||
|
@ -20,6 +20,7 @@
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
from six import iteritems, PY3
|
||||
|
||||
@ -35,6 +36,7 @@ try:
|
||||
except ImportError:
|
||||
janome_module = False
|
||||
|
||||
from sphinx.deprecation import RemovedInSphinx30Warning
|
||||
from sphinx.errors import SphinxError, ExtensionError
|
||||
from sphinx.search import SearchLanguage
|
||||
from sphinx.util import import_object
|
||||
@ -556,9 +558,12 @@ class SearchJapanese(SearchLanguage):
|
||||
|
||||
def init(self, options):
|
||||
# type: (Dict) -> None
|
||||
type = options.get('type', 'default')
|
||||
type = options.get('type', 'sphinx.search.ja.DefaultSplitter')
|
||||
if type in self.splitters:
|
||||
dotted_path = self.splitters[type]
|
||||
warnings.warn('html_search_options["type"]: %s is deprecated. '
|
||||
'Please give "%s" instead.' % (type, dotted_path),
|
||||
RemovedInSphinx30Warning)
|
||||
else:
|
||||
dotted_path = type
|
||||
try:
|
||||
|
@ -74,7 +74,7 @@ language = {{ language | repr }}
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# 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 }}]
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
|
@ -1578,6 +1578,7 @@
|
||||
\protected\def\sphinxtablecontinued#1{\textsf{#1}}
|
||||
\protected\def\sphinxtitleref#1{\emph{#1}}
|
||||
\protected\def\sphinxmenuselection#1{\emph{#1}}
|
||||
\protected\def\sphinxguilabel#1{\emph{#1}}
|
||||
\protected\def\sphinxaccelerator#1{\underline{#1}}
|
||||
\protected\def\sphinxcrossref#1{\emph{#1}}
|
||||
\protected\def\sphinxtermref#1{\emph{#1}}
|
||||
|
@ -70,7 +70,9 @@ jQuery.fn.highlightText = function(text, className) {
|
||||
if (node.nodeType === 3) {
|
||||
var val = node.nodeValue;
|
||||
var pos = val.toLowerCase().indexOf(text);
|
||||
if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) {
|
||||
if (pos >= 0 &&
|
||||
!jQuery(node.parentNode).hasClass(className) &&
|
||||
!jQuery(node.parentNode).hasClass("nohighlight")) {
|
||||
var span;
|
||||
var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
|
||||
if (isInSVG) {
|
||||
|
@ -343,6 +343,8 @@ class SphinxSmartQuotes(SmartQuotes, SphinxTransform):
|
||||
|
||||
refs: sphinx.parsers.RSTParser
|
||||
"""
|
||||
default_priority = 750
|
||||
|
||||
def apply(self):
|
||||
# type: () -> None
|
||||
if not self.is_available():
|
||||
@ -397,6 +399,15 @@ class SphinxSmartQuotes(SmartQuotes, SphinxTransform):
|
||||
yield (texttype[notsmartquotable], txtnode.astext())
|
||||
|
||||
|
||||
class DoctreeReadEvent(SphinxTransform):
|
||||
"""Emit :event:`doctree-read` event."""
|
||||
default_priority = 880
|
||||
|
||||
def apply(self):
|
||||
# type: () -> None
|
||||
self.app.emit('doctree-read', self.document)
|
||||
|
||||
|
||||
class ManpageLink(SphinxTransform):
|
||||
"""Find manpage section numbers and names"""
|
||||
default_priority = 999
|
||||
|
@ -165,19 +165,37 @@ def get_filename_for(filename, mimetype):
|
||||
|
||||
|
||||
class ImageConverter(BaseImageConverter):
|
||||
"""A base class images converter.
|
||||
"""A base class for image converters.
|
||||
|
||||
The concrete image converters should derive this class and
|
||||
overrides the following methods and attributes:
|
||||
An image converter is kind of Docutils transform module. It is used to
|
||||
convert image files which does not supported by builder to appropriate
|
||||
format for that builder.
|
||||
|
||||
* default_priority (if needed)
|
||||
* conversion_rules
|
||||
* is_available()
|
||||
* convert()
|
||||
For example, :py:class:`LaTeX builder <.LaTeXBuilder>` supports PDF,
|
||||
PNG and JPEG as image formats. However it does not support SVG images.
|
||||
For such case, to use image converters allows to embed these
|
||||
unsupported images into the document. One of image converters;
|
||||
:ref:`sphinx.ext.imgconverter <sphinx.ext.imgconverter>` can convert
|
||||
a SVG image to PNG format using Imagemagick internally.
|
||||
|
||||
There are three steps to make your custom image converter:
|
||||
|
||||
1. Make a subclass of ``ImageConverter`` class
|
||||
2. Override ``conversion_rules``, ``is_available()`` and ``convert()``
|
||||
3. Register your image converter to Sphinx using
|
||||
:py:meth:`.Sphinx.add_post_transform`
|
||||
"""
|
||||
default_priority = 200
|
||||
|
||||
#: A conversion rules between two mimetypes which this converters supports
|
||||
#: A conversion rules the image converter supports.
|
||||
#: It is represented as a list of pair of source image format (mimetype) and
|
||||
#: destination one::
|
||||
#:
|
||||
#: conversion_rules = [
|
||||
#: ('image/svg+xml', 'image/png'),
|
||||
#: ('image/gif', 'image/png'),
|
||||
#: ('application/pdf', 'image/png'),
|
||||
#: ]
|
||||
conversion_rules = [] # type: List[Tuple[unicode, unicode]]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -216,7 +234,7 @@ class ImageConverter(BaseImageConverter):
|
||||
|
||||
def is_available(self):
|
||||
# type: () -> bool
|
||||
"""Confirms the converter is available or not."""
|
||||
"""Return the image converter is available or not."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def guess_mimetypes(self, node):
|
||||
@ -255,7 +273,11 @@ class ImageConverter(BaseImageConverter):
|
||||
|
||||
def convert(self, _from, _to):
|
||||
# type: (unicode, unicode) -> bool
|
||||
"""Converts the image to expected one."""
|
||||
"""Convert a image file to expected format.
|
||||
|
||||
*_from* is a path for source image file, and *_to* is a path for
|
||||
destination file.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
|
@ -11,15 +11,13 @@
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.transforms.references import Substitutions
|
||||
from six import itervalues
|
||||
|
||||
from sphinx.transforms import SphinxTransform
|
||||
|
||||
|
||||
class SubstitutionDefinitionsRemover(SphinxTransform):
|
||||
"""Remove ``substitution_definition node from doctrees.
|
||||
|
||||
.. note:: In Sphinx-1.7, this transform is only used in LaTeX builder.
|
||||
"""
|
||||
"""Remove ``substitution_definition node from doctrees."""
|
||||
|
||||
# should be invoked after Substitutions process
|
||||
default_priority = Substitutions.default_priority + 1
|
||||
@ -28,3 +26,13 @@ class SubstitutionDefinitionsRemover(SphinxTransform):
|
||||
# type: () -> None
|
||||
for node in self.document.traverse(nodes.substitution_definition):
|
||||
node.parent.remove(node)
|
||||
|
||||
|
||||
class SphinxDomains(SphinxTransform):
|
||||
"""Collect objects to Sphinx domains for cross references."""
|
||||
default_priority = 850
|
||||
|
||||
def apply(self):
|
||||
# type: () -> None
|
||||
for domain in itervalues(self.env.domains):
|
||||
domain.process_doc(self, self.env.docname, self.document)
|
||||
|
@ -62,13 +62,13 @@ def getLogger(name):
|
||||
# type: (str) -> SphinxLoggerAdapter
|
||||
"""Get logger wrapped by :class:`sphinx.util.logging.SphinxLoggerAdapter`.
|
||||
|
||||
Sphinx logger always uses ``sphinx.*`` namesapce to be independent from
|
||||
settings of root logger. It ensure logging is consistent even if a
|
||||
Sphinx logger always uses ``sphinx.*`` namespace to be independent from
|
||||
settings of root logger. It ensures logging is consistent even if a
|
||||
third-party extension or imported application resets logger settings.
|
||||
|
||||
Example usage::
|
||||
|
||||
>>> from sphinx.utils import logging
|
||||
>>> from sphinx.util import logging
|
||||
>>> logger = logging.getLogger(__name__)
|
||||
>>> logger.info('Hello, this is an extension!')
|
||||
Hello, this is an extension!
|
||||
|
@ -9,6 +9,7 @@
|
||||
:copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
import warnings
|
||||
from itertools import product
|
||||
from operator import itemgetter
|
||||
from uuid import uuid4
|
||||
@ -17,6 +18,7 @@ from six import iteritems
|
||||
from six.moves import cPickle as pickle
|
||||
from six.moves import range, zip_longest
|
||||
|
||||
from sphinx.deprecation import RemovedInSphinx30Warning
|
||||
from sphinx.transforms import SphinxTransform
|
||||
|
||||
if False:
|
||||
@ -155,12 +157,15 @@ def levenshtein_distance(a, b):
|
||||
|
||||
class UIDTransform(SphinxTransform):
|
||||
"""Add UIDs to doctree for versioning."""
|
||||
default_priority = 100
|
||||
default_priority = 880
|
||||
|
||||
def apply(self):
|
||||
# type: () -> None
|
||||
env = self.env
|
||||
old_doctree = None
|
||||
if not env.versioning_condition:
|
||||
return
|
||||
|
||||
if env.versioning_compare:
|
||||
# get old doctree
|
||||
try:
|
||||
@ -180,5 +185,7 @@ class UIDTransform(SphinxTransform):
|
||||
def prepare(document):
|
||||
# type: (nodes.Node) -> None
|
||||
"""Simple wrapper for UIDTransform."""
|
||||
warnings.warn('versioning.prepare() is deprecated. Use UIDTransform instead.',
|
||||
RemovedInSphinx30Warning)
|
||||
transform = UIDTransform(document)
|
||||
transform.apply()
|
||||
|
@ -25,10 +25,9 @@ from sphinx import addnodes
|
||||
from sphinx import highlighting
|
||||
from sphinx.errors import SphinxError
|
||||
from sphinx.locale import admonitionlabels, _, __
|
||||
from sphinx.transforms import SphinxTransform
|
||||
from sphinx.util import split_into, logging
|
||||
from sphinx.util.i18n import format_date
|
||||
from sphinx.util.nodes import clean_astext, traverse_parent
|
||||
from sphinx.util.nodes import clean_astext
|
||||
from sphinx.util.template import LaTeXRenderer
|
||||
from sphinx.util.texescape import tex_escape_map, tex_replace_map
|
||||
|
||||
@ -47,7 +46,6 @@ BEGIN_DOC = r'''
|
||||
'''
|
||||
|
||||
|
||||
URI_SCHEMES = ('mailto:', 'http:', 'https:', 'ftp:')
|
||||
LATEXSECTIONNAMES = ["part", "chapter", "section", "subsection",
|
||||
"subsubsection", "paragraph", "subparagraph"]
|
||||
|
||||
@ -190,10 +188,11 @@ class LaTeXWriter(writers.Writer):
|
||||
# Helper classes
|
||||
|
||||
class ExtBabel(Babel):
|
||||
def __init__(self, language_code):
|
||||
# type: (unicode) -> None
|
||||
def __init__(self, language_code, use_polyglossia=False):
|
||||
# type: (unicode, bool) -> None
|
||||
super(ExtBabel, self).__init__(language_code or '')
|
||||
self.language_code = language_code
|
||||
self.use_polyglossia = use_polyglossia
|
||||
|
||||
def get_shorthandoff(self):
|
||||
# type: () -> unicode
|
||||
@ -221,116 +220,27 @@ class ExtBabel(Babel):
|
||||
def get_language(self):
|
||||
# type: () -> unicode
|
||||
language = super(ExtBabel, self).get_language()
|
||||
if not language:
|
||||
if language == 'ngerman' and self.use_polyglossia:
|
||||
# polyglossia calls new orthography (Neue Rechtschreibung) as
|
||||
# german (with new spelling option).
|
||||
return 'german'
|
||||
elif not language:
|
||||
return 'english' # fallback to english
|
||||
else:
|
||||
return language
|
||||
|
||||
|
||||
class ShowUrlsTransform(SphinxTransform, object):
|
||||
def __init__(self, document, startnode=None):
|
||||
# type: (nodes.document, nodes.Node) -> None
|
||||
super(ShowUrlsTransform, self).__init__(document, startnode)
|
||||
self.expanded = False
|
||||
|
||||
def apply(self):
|
||||
# type: () -> None
|
||||
# replace id_prefix temporarily
|
||||
id_prefix = self.document.settings.id_prefix
|
||||
self.document.settings.id_prefix = 'show_urls'
|
||||
|
||||
self.expand_show_urls()
|
||||
if self.expanded:
|
||||
self.renumber_footnotes()
|
||||
|
||||
# restore id_prefix
|
||||
self.document.settings.id_prefix = id_prefix
|
||||
|
||||
def expand_show_urls(self):
|
||||
# type: () -> None
|
||||
show_urls = self.document.settings.env.config.latex_show_urls
|
||||
if show_urls is False or show_urls == 'no':
|
||||
return
|
||||
|
||||
for node in self.document.traverse(nodes.reference):
|
||||
uri = node.get('refuri', '')
|
||||
if uri.startswith(URI_SCHEMES):
|
||||
if uri.startswith('mailto:'):
|
||||
uri = uri[7:]
|
||||
if node.astext() != uri:
|
||||
index = node.parent.index(node)
|
||||
if show_urls == 'footnote':
|
||||
footnote_nodes = self.create_footnote(uri)
|
||||
for i, fn in enumerate(footnote_nodes):
|
||||
node.parent.insert(index + i + 1, fn)
|
||||
|
||||
self.expanded = True
|
||||
else: # all other true values (b/w compat)
|
||||
textnode = nodes.Text(" (%s)" % uri)
|
||||
node.parent.insert(index + 1, textnode)
|
||||
|
||||
def create_footnote(self, uri):
|
||||
# type: (unicode) -> List[Union[nodes.footnote, nodes.footnote_ref]]
|
||||
label = nodes.label('', '#')
|
||||
para = nodes.paragraph()
|
||||
para.append(nodes.reference('', nodes.Text(uri), refuri=uri, nolinkurl=True))
|
||||
footnote = nodes.footnote(uri, label, para, auto=1)
|
||||
footnote['names'].append('#')
|
||||
self.document.note_autofootnote(footnote)
|
||||
|
||||
label = nodes.Text('#')
|
||||
footnote_ref = nodes.footnote_reference('[#]_', label, auto=1,
|
||||
refid=footnote['ids'][0])
|
||||
self.document.note_autofootnote_ref(footnote_ref)
|
||||
footnote.add_backref(footnote_ref['ids'][0])
|
||||
|
||||
return [footnote, footnote_ref]
|
||||
|
||||
def renumber_footnotes(self):
|
||||
# type: () -> None
|
||||
def is_used_number(number):
|
||||
# type: (unicode) -> bool
|
||||
for node in self.document.traverse(nodes.footnote):
|
||||
if not node.get('auto') and number in node['names']:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def is_auto_footnote(node):
|
||||
# type: (nodes.Node) -> bool
|
||||
return isinstance(node, nodes.footnote) and node.get('auto')
|
||||
|
||||
def footnote_ref_by(node):
|
||||
# type: (nodes.Node) -> Callable[[nodes.Node], bool]
|
||||
ids = node['ids']
|
||||
parent = list(traverse_parent(node, (nodes.document, addnodes.start_of_file)))[0]
|
||||
|
||||
def is_footnote_ref(node):
|
||||
# type: (nodes.Node) -> bool
|
||||
return (isinstance(node, nodes.footnote_reference) and
|
||||
ids[0] == node['refid'] and
|
||||
parent in list(traverse_parent(node)))
|
||||
|
||||
return is_footnote_ref
|
||||
|
||||
startnum = 1
|
||||
for footnote in self.document.traverse(is_auto_footnote):
|
||||
while True:
|
||||
label = str(startnum)
|
||||
startnum += 1
|
||||
if not is_used_number(label):
|
||||
break
|
||||
|
||||
old_label = footnote[0].astext()
|
||||
footnote.remove(footnote[0])
|
||||
footnote.insert(0, nodes.label('', label))
|
||||
if old_label in footnote['names']:
|
||||
footnote['names'].remove(old_label)
|
||||
footnote['names'].append(label)
|
||||
|
||||
for footnote_ref in self.document.traverse(footnote_ref_by(footnote)):
|
||||
footnote_ref.remove(footnote_ref[0])
|
||||
footnote_ref += nodes.Text(label)
|
||||
def get_mainlanguage_options(self):
|
||||
# type: () -> unicode
|
||||
"""Return options for polyglossia's ``\setmainlanguage``."""
|
||||
language = super(ExtBabel, self).get_language()
|
||||
if self.use_polyglossia is False:
|
||||
return None
|
||||
elif language == 'ngerman':
|
||||
return 'spelling=new'
|
||||
elif language == 'german':
|
||||
return 'spelling=old'
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class Table(object):
|
||||
@ -618,9 +528,13 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
if builder.config.language \
|
||||
and 'fncychap' not in builder.config.latex_elements:
|
||||
# use Sonny style if any language specified
|
||||
self.elements['fncychap'] = '\\usepackage[Sonny]{fncychap}'
|
||||
self.elements['fncychap'] = ('\\usepackage[Sonny]{fncychap}\n'
|
||||
'\\ChNameVar{\\Large\\normalfont'
|
||||
'\\sffamily}\n\\ChTitleVar{\\Large'
|
||||
'\\normalfont\\sffamily}')
|
||||
|
||||
self.babel = ExtBabel(builder.config.language)
|
||||
self.babel = ExtBabel(builder.config.language,
|
||||
not self.elements['babel'])
|
||||
if builder.config.language and not self.babel.is_supported_language():
|
||||
# emit warning if specified language is invalid
|
||||
# (only emitting, nothing changed to processing)
|
||||
@ -654,8 +568,15 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
# disable fncychap in Japanese documents
|
||||
self.elements['fncychap'] = ''
|
||||
elif self.elements['polyglossia']:
|
||||
self.elements['multilingual'] = '%s\n\\setmainlanguage{%s}' % \
|
||||
(self.elements['polyglossia'], self.babel.get_language())
|
||||
options = self.babel.get_mainlanguage_options()
|
||||
if options:
|
||||
mainlanguage = r'\setmainlanguage[%s]{%s}' % (options,
|
||||
self.babel.get_language())
|
||||
else:
|
||||
mainlanguage = r'\setmainlanguage{%s}' % self.babel.get_language()
|
||||
|
||||
self.elements['multilingual'] = '%s\n%s' % (self.elements['polyglossia'],
|
||||
mainlanguage)
|
||||
|
||||
if getattr(builder, 'usepackages', None):
|
||||
def declare_package(packagename, options=None):
|
||||
@ -2502,20 +2423,15 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
# type: (nodes.Node) -> None
|
||||
self.body.append('}}$')
|
||||
|
||||
def visit_substitution_definition(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_substitution_reference(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_inline(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
classes = node.get('classes', [])
|
||||
if classes in [['menuselection'], ['guilabel']]:
|
||||
if classes in [['menuselection']]:
|
||||
self.body.append(r'\sphinxmenuselection{')
|
||||
self.context.append('}')
|
||||
elif classes in [['guilabel']]:
|
||||
self.body.append(r'\sphinxguilabel{')
|
||||
self.context.append('}')
|
||||
elif classes in [['accelerator']]:
|
||||
self.body.append(r'\sphinxaccelerator{')
|
||||
self.context.append('}')
|
||||
@ -2644,3 +2560,10 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
||||
def unknown_visit(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
raise NotImplementedError('Unknown node: ' + node.__class__.__name__)
|
||||
|
||||
|
||||
# Import old modules here for compatibility
|
||||
# They should be imported after `LaTeXTranslator` to avoid recursive import.
|
||||
#
|
||||
# refs: https://github.com/sphinx-doc/sphinx/issues/4889
|
||||
from sphinx.builders.latex.transforms import URI_SCHEMES, ShowUrlsTransform # NOQA
|
||||
|
@ -1385,18 +1385,6 @@ class TexinfoTranslator(nodes.NodeVisitor):
|
||||
# type: (nodes.Node) -> None
|
||||
pass
|
||||
|
||||
def visit_substitution_reference(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
pass
|
||||
|
||||
def depart_substitution_reference(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
pass
|
||||
|
||||
def visit_substitution_definition(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_system_message(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.body.append('\n@verbatim\n'
|
||||
|
@ -987,10 +987,6 @@ class TextTranslator(nodes.NodeVisitor):
|
||||
# type: (nodes.Node) -> None
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_substitution_definition(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
raise nodes.SkipNode
|
||||
|
||||
def visit_pending_xref(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
pass
|
||||
|
@ -1,54 +1,3 @@
|
||||
from sphinx.config import string_classes, ENUM
|
||||
|
||||
value1 = 123 # wrong type
|
||||
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'))
|
||||
project = 'Sphinx <Tests>'
|
||||
release = '0.6alpha1'
|
||||
templates_path = ['_templates']
|
||||
|
@ -4,7 +4,7 @@ References
|
||||
Translation Tips
|
||||
-----------------
|
||||
|
||||
.. _download Sphinx: https://pypi.python.org/pypi/sphinx
|
||||
.. _download Sphinx: https://pypi.org/project/Sphinx/
|
||||
.. _Docutils site: http://docutils.sourceforge.net/
|
||||
.. _Sphinx site: http://sphinx-doc.org/
|
||||
|
||||
|
@ -12,7 +12,7 @@ from sphinx import addnodes
|
||||
sys.path.append(os.path.abspath('.'))
|
||||
|
||||
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'
|
||||
|
||||
@ -65,8 +65,6 @@ man_pages = [
|
||||
'Georg Brandl and someone else', 1),
|
||||
]
|
||||
|
||||
value_from_conf_py = 84
|
||||
|
||||
coverage_c_path = ['special/*.h']
|
||||
coverage_c_regexes = {'function': r'^PyAPI_FUNC\(.*\)\s+([^_][\w_]+)'}
|
||||
|
||||
@ -104,7 +102,6 @@ class ClassDirective(Directive):
|
||||
def setup(app):
|
||||
import parsermod
|
||||
|
||||
app.add_config_value('value_from_conf_py', 42, False)
|
||||
app.add_directive('clsdir', ClassDirective)
|
||||
app.add_object_type('userdesc', 'userdescrole', '%s (userdesc)',
|
||||
userdesc_parse, objname='user desc')
|
||||
|
@ -1,5 +0,0 @@
|
||||
# Test extension module
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_config_value('value_from_ext', [], False)
|
@ -15,7 +15,7 @@ normal order
|
||||
hyperref <https://sphinx-doc.org/?q=sphinx>
|
||||
|
||||
reversed order
|
||||
-------------
|
||||
--------------
|
||||
|
||||
.. toctree::
|
||||
:glob:
|
||||
|
@ -11,7 +11,7 @@
|
||||
import pytest
|
||||
from docutils import nodes
|
||||
|
||||
from sphinx.application import ExtensionError
|
||||
from sphinx.errors import ExtensionError
|
||||
from sphinx.domains import Domain
|
||||
from sphinx.testing.util import strip_escseq
|
||||
from sphinx.util import logging
|
||||
|
@ -534,6 +534,50 @@ def test_babel_with_unknown_language(app, status, warning):
|
||||
assert "WARNING: no Babel option known for language 'unknown'" in warning.getvalue()
|
||||
|
||||
|
||||
@pytest.mark.sphinx(
|
||||
'latex', testroot='latex-babel',
|
||||
confoverrides={'language': 'de', 'latex_engine': 'lualatex'})
|
||||
def test_polyglossia_with_language_de(app, status, warning):
|
||||
app.builder.build_all()
|
||||
result = (app.outdir / 'Python.tex').text(encoding='utf8')
|
||||
print(result)
|
||||
print(status.getvalue())
|
||||
print(warning.getvalue())
|
||||
assert '\\documentclass[letterpaper,10pt,german]{sphinxmanual}' in result
|
||||
assert '\\usepackage{polyglossia}' in result
|
||||
assert '\\setmainlanguage[spelling=new]{german}' in result
|
||||
assert '\\usepackage{times}' not in result
|
||||
assert '\\usepackage[Sonny]{fncychap}' in result
|
||||
assert ('\\addto\\captionsgerman{\\renewcommand{\\contentsname}{Table of content}}\n'
|
||||
in result)
|
||||
assert '\\addto\\captionsgerman{\\renewcommand{\\figurename}{Fig.}}\n' in result
|
||||
assert '\\addto\\captionsgerman{\\renewcommand{\\tablename}{Table.}}\n' in result
|
||||
assert '\\def\\pageautorefname{Seite}\n' in result
|
||||
assert '\\shorthandoff' not in result
|
||||
|
||||
|
||||
@pytest.mark.sphinx(
|
||||
'latex', testroot='latex-babel',
|
||||
confoverrides={'language': 'de-1901', 'latex_engine': 'lualatex'})
|
||||
def test_polyglossia_with_language_de_1901(app, status, warning):
|
||||
app.builder.build_all()
|
||||
result = (app.outdir / 'Python.tex').text(encoding='utf8')
|
||||
print(result)
|
||||
print(status.getvalue())
|
||||
print(warning.getvalue())
|
||||
assert '\\documentclass[letterpaper,10pt,german]{sphinxmanual}' in result
|
||||
assert '\\usepackage{polyglossia}' in result
|
||||
assert '\\setmainlanguage[spelling=old]{german}' in result
|
||||
assert '\\usepackage{times}' not in result
|
||||
assert '\\usepackage[Sonny]{fncychap}' in result
|
||||
assert ('\\addto\\captionsgerman{\\renewcommand{\\contentsname}{Table of content}}\n'
|
||||
in result)
|
||||
assert '\\addto\\captionsgerman{\\renewcommand{\\figurename}{Fig.}}\n' in result
|
||||
assert '\\addto\\captionsgerman{\\renewcommand{\\tablename}{Table.}}\n' in result
|
||||
assert '\\def\\pageautorefname{page}\n' in result
|
||||
assert '\\shorthandoff' not in result
|
||||
|
||||
|
||||
@pytest.mark.sphinx('latex')
|
||||
def test_footnote(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
@ -11,15 +11,15 @@
|
||||
"""
|
||||
import mock
|
||||
import pytest
|
||||
from six import PY3, iteritems
|
||||
from six import PY3
|
||||
|
||||
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.testing.path import path
|
||||
|
||||
|
||||
@pytest.mark.sphinx(confoverrides={
|
||||
@pytest.mark.sphinx(testroot='config', confoverrides={
|
||||
'master_doc': 'master',
|
||||
'nonexisting_value': 'True',
|
||||
'latex_elements.docclass': 'scrartcl',
|
||||
@ -74,36 +74,64 @@ def test_core_config(app, status, warning):
|
||||
assert cfg['project'] == cfg.project == 'Sphinx Tests'
|
||||
|
||||
|
||||
def test_extension_values(app, status, warning):
|
||||
cfg = app.config
|
||||
def test_extension_values():
|
||||
config = Config()
|
||||
|
||||
# default value
|
||||
assert cfg.value_from_ext == []
|
||||
# non-default value
|
||||
assert cfg.value_from_conf_py == 84
|
||||
# check standard settings
|
||||
assert config.master_doc == 'contents'
|
||||
|
||||
# no duplicate values allowed
|
||||
# can't override it by add_config_value()
|
||||
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)
|
||||
|
||||
# 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:
|
||||
app.add_config_value('value_from_ext', 'x', True)
|
||||
config.add('value_from_ext', [], 'env', None)
|
||||
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")
|
||||
def test_errors_warnings(logger, tempdir):
|
||||
# test the error for syntax errors in the config file
|
||||
(tempdir / 'conf.py').write_text(u'project = \n', encoding='ascii')
|
||||
with pytest.raises(ConfigError) as excinfo:
|
||||
Config(tempdir, 'conf.py', {}, None)
|
||||
Config.read(tempdir, {}, None)
|
||||
assert 'conf.py' in str(excinfo.value)
|
||||
|
||||
# test the automatic conversion of 2.x only code in configs
|
||||
(tempdir / 'conf.py').write_text(
|
||||
u'# -*- coding: utf-8\n\nproject = u"Jägermeister"\n',
|
||||
encoding='utf-8')
|
||||
cfg = Config(tempdir, 'conf.py', {}, None)
|
||||
cfg = Config.read(tempdir, {}, None)
|
||||
cfg.init_values()
|
||||
assert cfg.project == u'Jägermeister'
|
||||
assert logger.called is False
|
||||
@ -115,7 +143,7 @@ def test_errors_warnings(logger, tempdir):
|
||||
return
|
||||
(tempdir / 'conf.py').write_text(
|
||||
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
|
||||
cfg.check_unicode()
|
||||
@ -174,7 +202,7 @@ def test_config_eol(logger, tempdir):
|
||||
configfile = tempdir / 'conf.py'
|
||||
for eol in (b'\n', b'\r\n'):
|
||||
configfile.write_bytes(b'project = "spam"' + eol)
|
||||
cfg = Config(tempdir, 'conf.py', {}, None)
|
||||
cfg = Config.read(tempdir, {}, None)
|
||||
cfg.init_values()
|
||||
assert cfg.project == u'spam'
|
||||
assert logger.called is False
|
||||
@ -195,60 +223,81 @@ def test_builtin_conf(app, status, warning):
|
||||
'warning')
|
||||
|
||||
|
||||
# See roots/test-config/conf.py.
|
||||
TYPECHECK_WARNINGS = {
|
||||
'value1': True,
|
||||
'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,
|
||||
}
|
||||
# example classes for type checking
|
||||
class A(object):
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.parametrize("key,should", iteritems(TYPECHECK_WARNINGS))
|
||||
@pytest.mark.sphinx(testroot='config')
|
||||
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
|
||||
)
|
||||
class B(A):
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='config')
|
||||
def test_check_enum(app, status, warning):
|
||||
assert "The config value `value17` has to be a one of ('default', 'one', 'two'), " \
|
||||
not in warning.getvalue()
|
||||
class C(A):
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='config', confoverrides={'value17': 'invalid'})
|
||||
def test_check_enum_failed(app, status, warning):
|
||||
assert "The config value `value17` has to be a one of ('default', 'one', 'two'), " \
|
||||
"but `invalid` is given." in warning.getvalue()
|
||||
# name, default, annotation, actual, warned
|
||||
TYPECHECK_WARNINGS = [
|
||||
('value1', 'string', None, 123, True), # wrong type
|
||||
('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']})
|
||||
def test_check_enum_for_list(app, status, warning):
|
||||
assert "The config value `value17` has to be a one of ('default', 'one', 'two'), " \
|
||||
not in warning.getvalue()
|
||||
@mock.patch("sphinx.config.logger")
|
||||
@pytest.mark.parametrize("name,default,annotation,actual,warned", TYPECHECK_WARNINGS)
|
||||
def test_check_types(logger, name, default, annotation, actual, warned):
|
||||
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']})
|
||||
def test_check_enum_for_list_failed(app, status, warning):
|
||||
assert "The config value `value17` has to be a one of ('default', 'one', 'two'), " \
|
||||
"but `['one', 'two', 'invalid']` is given." in warning.getvalue()
|
||||
@mock.patch("sphinx.config.logger")
|
||||
def test_check_enum(logger):
|
||||
config = Config()
|
||||
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()
|
||||
|
@ -17,7 +17,7 @@ from sphinx.config import Config
|
||||
from sphinx.directives.code import LiteralIncludeReader
|
||||
from sphinx.testing.util import etree_parse
|
||||
|
||||
DUMMY_CONFIG = Config(None, None, {}, '')
|
||||
DUMMY_CONFIG = Config({}, {})
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
|
153
tests/test_directive_other.py
Normal file
153
tests/test_directive_other.py
Normal file
@ -0,0 +1,153 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
test_directive_other
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Test the other directives.
|
||||
|
||||
:copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from docutils import nodes
|
||||
from docutils.core import publish_doctree
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.io import SphinxStandaloneReader
|
||||
from sphinx.parsers import RSTParser
|
||||
from sphinx.testing.util import assert_node
|
||||
|
||||
|
||||
def parse(app, docname, text):
|
||||
app.env.temp_data['docname'] = docname
|
||||
return publish_doctree(text, app.srcdir / docname + '.rst',
|
||||
reader=SphinxStandaloneReader(app),
|
||||
parser=RSTParser(),
|
||||
settings_overrides={'env': app.env,
|
||||
'gettext_compact': True})
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='toctree-glob')
|
||||
def test_toctree(app):
|
||||
text = (".. toctree::\n"
|
||||
"\n"
|
||||
" foo\n"
|
||||
" bar/index\n"
|
||||
" baz\n")
|
||||
|
||||
app.env.find_files(app.config, app.builder)
|
||||
doctree = parse(app, 'index', text)
|
||||
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
|
||||
assert_node(doctree[0][0],
|
||||
entries=[(None, 'foo'), (None, 'bar/index'), (None, 'baz')],
|
||||
includefiles=['foo', 'bar/index', 'baz'])
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='toctree-glob')
|
||||
def test_relative_toctree(app):
|
||||
text = (".. toctree::\n"
|
||||
"\n"
|
||||
" bar_1\n"
|
||||
" bar_2\n"
|
||||
" bar_3\n"
|
||||
" ../quux\n")
|
||||
|
||||
app.env.find_files(app.config, app.builder)
|
||||
doctree = parse(app, 'bar/index', text)
|
||||
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
|
||||
assert_node(doctree[0][0],
|
||||
entries=[(None, 'bar/bar_1'), (None, 'bar/bar_2'), (None, 'bar/bar_3'),
|
||||
(None, 'quux')],
|
||||
includefiles=['bar/bar_1', 'bar/bar_2', 'bar/bar_3', 'quux'])
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='toctree-glob')
|
||||
def test_toctree_urls_and_titles(app):
|
||||
text = (".. toctree::\n"
|
||||
"\n"
|
||||
" Sphinx <https://www.sphinx-doc.org/>\n"
|
||||
" https://readthedocs.org/\n"
|
||||
" The BAR <bar/index>\n")
|
||||
|
||||
app.env.find_files(app.config, app.builder)
|
||||
doctree = parse(app, 'index', text)
|
||||
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
|
||||
assert_node(doctree[0][0],
|
||||
entries=[('Sphinx', 'https://www.sphinx-doc.org/'),
|
||||
(None, 'https://readthedocs.org/'),
|
||||
('The BAR', 'bar/index')],
|
||||
includefiles=['bar/index'])
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='toctree-glob')
|
||||
def test_toctree_glob(app):
|
||||
text = (".. toctree::\n"
|
||||
" :glob:\n"
|
||||
"\n"
|
||||
" *\n")
|
||||
|
||||
app.env.find_files(app.config, app.builder)
|
||||
doctree = parse(app, 'index', text)
|
||||
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
|
||||
assert_node(doctree[0][0],
|
||||
entries=[(None, 'baz'), (None, 'foo'), (None, 'quux')],
|
||||
includefiles=['baz', 'foo', 'quux'])
|
||||
|
||||
# give both docname and glob (case1)
|
||||
text = (".. toctree::\n"
|
||||
" :glob:\n"
|
||||
"\n"
|
||||
" foo\n"
|
||||
" *\n")
|
||||
|
||||
app.env.find_files(app.config, app.builder)
|
||||
doctree = parse(app, 'index', text)
|
||||
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
|
||||
assert_node(doctree[0][0],
|
||||
entries=[(None, 'foo'), (None, 'baz'), (None, 'quux')],
|
||||
includefiles=['foo', 'baz', 'quux'])
|
||||
|
||||
# give both docname and glob (case2)
|
||||
text = (".. toctree::\n"
|
||||
" :glob:\n"
|
||||
"\n"
|
||||
" *\n"
|
||||
" foo\n")
|
||||
|
||||
app.env.find_files(app.config, app.builder)
|
||||
doctree = parse(app, 'index', text)
|
||||
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
|
||||
assert_node(doctree[0][0],
|
||||
entries=[(None, 'baz'), (None, 'foo'), (None, 'quux'), (None, 'foo')],
|
||||
includefiles=['baz', 'foo', 'quux', 'foo'])
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='toctree-glob')
|
||||
def test_toctree_glob_and_url(app):
|
||||
text = (".. toctree::\n"
|
||||
" :glob:\n"
|
||||
"\n"
|
||||
" https://example.com/?q=sphinx\n")
|
||||
|
||||
app.env.find_files(app.config, app.builder)
|
||||
doctree = parse(app, 'index', text)
|
||||
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
|
||||
assert_node(doctree[0][0],
|
||||
entries=[(None, 'https://example.com/?q=sphinx')],
|
||||
includefiles=[])
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='toctree-glob')
|
||||
def test_toctree_twice(app):
|
||||
text = (".. toctree::\n"
|
||||
"\n"
|
||||
" foo\n"
|
||||
" foo\n")
|
||||
|
||||
app.env.find_files(app.config, app.builder)
|
||||
doctree = parse(app, 'index', text)
|
||||
assert_node(doctree, [nodes.document, nodes.compound, addnodes.toctree])
|
||||
assert_node(doctree[0][0],
|
||||
entries=[(None, 'foo'), (None, 'foo')],
|
||||
includefiles=['foo', 'foo'])
|
@ -35,19 +35,19 @@ def test_jsmath(app, status, warning):
|
||||
app.builder.build_all()
|
||||
content = (app.outdir / 'math.html').text()
|
||||
|
||||
assert '<div class="math notranslate">\na^2 + b^2 = c^2</div>' in content
|
||||
assert ('<div class="math notranslate">\n\\begin{split}a + 1 < b\\end{split}</div>'
|
||||
in content)
|
||||
assert '<div class="math notranslate nohighlight">\na^2 + b^2 = c^2</div>' in content
|
||||
assert ('<div class="math notranslate nohighlight">\n\\begin{split}a + 1 < '
|
||||
'b\\end{split}</div>' in content)
|
||||
assert (u'<span class="eqno">(1)<a class="headerlink" href="#equation-foo" '
|
||||
u'title="Permalink to this equation">\xb6</a></span>'
|
||||
u'<div class="math notranslate" id="equation-foo">\ne^{i\\pi} = 1</div>'
|
||||
in content)
|
||||
u'<div class="math notranslate nohighlight" id="equation-foo">'
|
||||
'\ne^{i\\pi} = 1</div>' in content)
|
||||
assert (u'<span class="eqno">(2)<a class="headerlink" href="#equation-math-0" '
|
||||
u'title="Permalink to this equation">\xb6</a></span>'
|
||||
u'<div class="math notranslate" id="equation-math-0">\n'
|
||||
u'<div class="math notranslate nohighlight" id="equation-math-0">\n'
|
||||
u'e^{ix} = \\cos x + i\\sin x</div>' in content)
|
||||
assert '<div class="math notranslate">\nn \\in \\mathbb N</div>' in content
|
||||
assert '<div class="math notranslate">\na + 1 < b</div>' in content
|
||||
assert '<div class="math notranslate nohighlight">\nn \\in \\mathbb N</div>' in content
|
||||
assert '<div class="math notranslate nohighlight">\na + 1 < b</div>' in content
|
||||
|
||||
|
||||
@pytest.mark.skipif(not has_binary('dvipng'),
|
||||
@ -91,7 +91,7 @@ def test_mathjax_align(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
content = (app.outdir / 'index.html').text()
|
||||
html = (r'<div class="math notranslate">\s*'
|
||||
html = (r'<div class="math notranslate nohighlight">\s*'
|
||||
r'\\\[ \\begin\{align\}\\begin\{aligned\}S \&= \\pi r\^2\\\\'
|
||||
r'V \&= \\frac\{4\}\{3\} \\pi r\^3\\end\{aligned\}\\end\{align\} \\\]</div>')
|
||||
assert re.search(html, content, re.S)
|
||||
@ -104,7 +104,7 @@ def test_math_number_all_mathjax(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
content = (app.outdir / 'index.html').text()
|
||||
html = (r'<div class="math notranslate" id="equation-index-0">\s*'
|
||||
html = (r'<div class="math notranslate nohighlight" id="equation-index-0">\s*'
|
||||
r'<span class="eqno">\(1\)<a .*>\xb6</a></span>\\\[a\^2\+b\^2=c\^2\\\]</div>')
|
||||
assert re.search(html, content, re.S)
|
||||
|
||||
@ -169,7 +169,7 @@ def test_mathjax_numfig_html(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
content = (app.outdir / 'math.html').text()
|
||||
html = ('<div class="math notranslate" id="equation-math-0">\n'
|
||||
html = ('<div class="math notranslate nohighlight" id="equation-math-0">\n'
|
||||
'<span class="eqno">(1.2)')
|
||||
assert html in content
|
||||
html = ('<p>Referencing equation <a class="reference internal" '
|
||||
|
@ -162,12 +162,20 @@ def get_verifier(verify, verify_re):
|
||||
'\\sphinxmenuselection{a \\(\\rightarrow\\) b}',
|
||||
),
|
||||
(
|
||||
# interpolation of ampersands in guilabel/menuselection
|
||||
# interpolation of ampersands in menuselection
|
||||
'verify',
|
||||
':menuselection:`&Foo -&&- &Bar`',
|
||||
(u'<p><span class="menuselection"><span class="accelerator">F</span>oo '
|
||||
'-&- <span class="accelerator">B</span>ar</span></p>'),
|
||||
r'\sphinxmenuselection{\sphinxaccelerator{F}oo -\&- \sphinxaccelerator{B}ar}',
|
||||
),
|
||||
(
|
||||
# interpolation of ampersands in guilabel
|
||||
'verify',
|
||||
':guilabel:`&Foo -&&- &Bar`',
|
||||
(u'<p><span class="guilabel"><span class="accelerator">F</span>oo '
|
||||
'-&- <span class="accelerator">B</span>ar</span></p>'),
|
||||
r'\sphinxmenuselection{\sphinxaccelerator{F}oo -\&- \sphinxaccelerator{B}ar}',
|
||||
r'\sphinxguilabel{\sphinxaccelerator{F}oo -\&- \sphinxaccelerator{B}ar}',
|
||||
),
|
||||
(
|
||||
# non-interpolation of dashes in option role
|
||||
|
@ -12,7 +12,7 @@ for stable releases
|
||||
* ``git commit -am 'Bump to X.Y.Z final'``
|
||||
* ``make clean``
|
||||
* ``python setup.py release bdist_wheel sdist upload --identity=[your key]``
|
||||
* open https://pypi.python.org/pypi/Sphinx and check there are no obvious errors
|
||||
* open https://pypi.org/project/Sphinx/ and check there are no obvious errors
|
||||
* ``git tag vX.Y.Z``
|
||||
* ``python utils/bump_version.py --in-develop X.Y.Zb0`` (ex. 1.5.3b0)
|
||||
* Check diff by ``git diff``
|
||||
@ -38,7 +38,7 @@ for first beta releases
|
||||
* ``git commit -am 'Bump to X.Y.0 beta1'``
|
||||
* ``make clean``
|
||||
* ``python setup.py release bdist_wheel sdist upload --identity=[your key]``
|
||||
* open https://pypi.python.org/pypi/Sphinx and check there are no obvious errors
|
||||
* open https://pypi.org/project/Sphinx/ and check there are no obvious errors
|
||||
* ``git tag vX.Y.0b1``
|
||||
* ``python utils/bump_version.py --in-develop X.Y.0b2`` (ex. 1.6.0b2)
|
||||
* Check diff by ``git diff``
|
||||
@ -67,7 +67,7 @@ for other beta releases
|
||||
* ``git commit -am 'Bump to X.Y.0 betaN'``
|
||||
* ``make clean``
|
||||
* ``python setup.py release bdist_wheel sdist upload --identity=[your key]``
|
||||
* open https://pypi.python.org/pypi/Sphinx and check there are no obvious errors
|
||||
* open https://pypi.org/project/Sphinx/ and check there are no obvious errors
|
||||
* ``git tag vX.Y.0bN``
|
||||
* ``python utils/bump_version.py --in-develop X.Y.0bM`` (ex. 1.6.0b3)
|
||||
* Check diff by `git diff``
|
||||
@ -95,7 +95,7 @@ for major releases
|
||||
* ``git commit -am 'Bump to X.Y.0 final'``
|
||||
* ``make clean``
|
||||
* ``python setup.py release bdist_wheel sdist upload --identity=[your key]``
|
||||
* open https://pypi.python.org/pypi/Sphinx and check there are no obvious errors
|
||||
* open https://pypi.org/project/Sphinx/ and check there are no obvious errors
|
||||
* ``git tag vX.Y.0``
|
||||
* ``python utils/bump_version.py --in-develop X.Y.1b0`` (ex. 1.6.1b0)
|
||||
* Check diff by ``git diff``
|
||||
|
Loading…
Reference in New Issue
Block a user