mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch 'master' into simplify_conf.py
This commit is contained in:
commit
1ece29597e
@ -13,13 +13,14 @@ matrix:
|
||||
include:
|
||||
- python: '3.5'
|
||||
env:
|
||||
- TOXENV=du13
|
||||
- TOXENV=du12
|
||||
- python: '3.6'
|
||||
env:
|
||||
- TOXENV=py36
|
||||
- PYTEST_ADDOPTS="--cov ./ --cov-append --cov-config setup.cfg"
|
||||
- TOXENV=du13
|
||||
- python: '3.7'
|
||||
env: TOXENV=py37
|
||||
env:
|
||||
- TOXENV=py37
|
||||
- PYTEST_ADDOPTS="--cov ./ --cov-append --cov-config setup.cfg"
|
||||
- python: 'nightly'
|
||||
env: TOXENV=py38
|
||||
- python: '3.6'
|
||||
|
29
CHANGES
29
CHANGES
@ -23,6 +23,7 @@ Dependencies
|
||||
|
||||
- sphinxcontrib.applehelp
|
||||
- sphinxcontrib.devhelp
|
||||
- sphinxcontrib.htmlhelp
|
||||
- sphinxcontrib.jsmath
|
||||
- sphinxcontrib.qthelp
|
||||
|
||||
@ -66,6 +67,8 @@ Incompatible changes
|
||||
the text width and height, even if width and/or height option were used.
|
||||
(refs: #5956)
|
||||
* epub: ``epub_title`` defaults to the :confval:`project` option
|
||||
* #4550: All tables and figures without ``align`` option are displayed to center
|
||||
* #4587: html: Output HTML5 by default
|
||||
|
||||
Deprecated
|
||||
----------
|
||||
@ -76,6 +79,10 @@ Deprecated
|
||||
``EpubBuilder.build_container()``, ``EpubBuilder.bulid_content()``,
|
||||
``EpubBuilder.build_toc()`` and ``EpubBuilder.build_epub()``
|
||||
* The arguments of ``Epub3Builder.build_navigation_doc()``
|
||||
* The config variables
|
||||
|
||||
- :confval:`html_experimental_html5_writer`
|
||||
|
||||
* The ``encoding`` argument of ``autodoc.Documenter.get_doc()``,
|
||||
``autodoc.DocstringSignatureMixin.get_doc()``,
|
||||
``autodoc.DocstringSignatureMixin._find_signature()``, and
|
||||
@ -109,6 +116,11 @@ Deprecated
|
||||
* ``sphinx.io.SphinxFileInput.supported``
|
||||
* ``sphinx.io.SphinxRSTFileInput``
|
||||
* ``sphinx.registry.SphinxComponentRegistry.add_source_input()``
|
||||
* ``sphinx.roles.abbr_role()``
|
||||
* ``sphinx.roles.emph_literal_role()``
|
||||
* ``sphinx.roles.menusel_role()``
|
||||
* ``sphinx.roles.index_role()``
|
||||
* ``sphinx.roles.indexmarkup_role()``
|
||||
* ``sphinx.testing.util.remove_unicode_literal()``
|
||||
* ``sphinx.util.attrdict``
|
||||
* ``sphinx.util.force_decode()``
|
||||
@ -120,10 +132,13 @@ Deprecated
|
||||
* ``sphinx.util.osutil.EPIPE``
|
||||
* ``sphinx.util.osutil.walk()``
|
||||
* ``sphinx.util.PeekableIterator``
|
||||
* ``sphinx.util.pycompat.NoneType``
|
||||
* ``sphinx.util.pycompat.TextIOWrapper``
|
||||
* ``sphinx.util.pycompat.UnicodeMixin``
|
||||
* ``sphinx.util.pycompat.htmlescape``
|
||||
* ``sphinx.util.pycompat.indent``
|
||||
* ``sphinx.util.pycompat.sys_encoding``
|
||||
* ``sphinx.util.pycompat.terminal_safe()``
|
||||
* ``sphinx.util.pycompat.u``
|
||||
* ``sphinx.writers.latex.ExtBabel``
|
||||
* ``sphinx.writers.latex.LaTeXTranslator._make_visit_admonition()``
|
||||
@ -155,6 +170,10 @@ Features added
|
||||
|
||||
* #4182: autodoc: Support :confval:`suppress_warnings`
|
||||
* #5533: autodoc: :confval:`autodoc_default_options` supports ``member-order``
|
||||
* #5394: autodoc: Display readable names in type annotations for mocked objects
|
||||
* #5459: autodoc: :confval:`autodoc_default_options` accepts ``True`` as a value
|
||||
* #5635: autosummary: Add :confval:`autosummary_mock_imports` to mock external
|
||||
libraries on importing targets
|
||||
* #4018: htmlhelp: Add :confval:`htmlhelp_file_suffix` and
|
||||
:confval:`htmlhelp_link_suffix`
|
||||
* #5559: text: Support complex tables (colspan and rowspan)
|
||||
@ -174,10 +193,17 @@ Features added
|
||||
* #4611: epub: Show warning for duplicated ToC entries
|
||||
* #1851: Allow to omit an argument for :rst:dir:`code-block` directive. If
|
||||
omitted, it follows :rst:dir:`highlight` or :confval:`highlight_language`
|
||||
* #4587: html: Add :confval:`html4_writer` to use old HTML4 writer
|
||||
* #6016: HTML search: A placeholder for the search summary prevents search
|
||||
result links from changing their position when the search terminates. This
|
||||
makes navigating search results easier.
|
||||
|
||||
* #5196: linkcheck also checks remote images exist
|
||||
* #5924: githubpages: create CNAME file for custom domains when
|
||||
:confval:`html_baseurl` set
|
||||
* #4261: autosectionlabel: restrict the labeled sections by new config value;
|
||||
:confval:`autosectionlabel_maxdepth`
|
||||
|
||||
|
||||
Bugs fixed
|
||||
----------
|
||||
|
||||
@ -226,6 +252,7 @@ Bugs fixed
|
||||
----------
|
||||
|
||||
* LaTeX: Remove extraneous space after author names on PDF title page (refs: #6004)
|
||||
* #6046: LaTeX: ``TypeError`` is raised when invalid latex_elements given
|
||||
|
||||
Testing
|
||||
--------
|
||||
|
23
doc/_themes/sphinx13/static/sphinx13.css
vendored
23
doc/_themes/sphinx13/static/sphinx13.css
vendored
@ -337,7 +337,7 @@ a tt:hover {
|
||||
}
|
||||
|
||||
pre {
|
||||
font-family: 'Consolas', 'DejaVu Sans Mono',
|
||||
font-family: 'Consolas', 'Courier New', 'DejaVu Sans Mono',
|
||||
'Bitstream Vera Sans Mono', monospace;
|
||||
font-size: 13px;
|
||||
letter-spacing: 0.015em;
|
||||
@ -388,32 +388,29 @@ div.admonition, div.warning {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.admonition p, div.warning p {
|
||||
div.admonition > p, div.warning > p {
|
||||
margin: 0.5em 1em 0.5em 1em;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.admonition pre, div.warning pre {
|
||||
div.admonition > pre, div.warning > pre {
|
||||
margin: 0.4em 1em 0.4em 1em;
|
||||
}
|
||||
|
||||
div.admonition p.admonition-title,
|
||||
div.warning p.admonition-title {
|
||||
margin-top: 1em;
|
||||
padding-top: 0.5em;
|
||||
div.admonition > p.admonition-title,
|
||||
div.warning > p.admonition-title {
|
||||
margin-top: 0.5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.warning {
|
||||
border: 1px solid #940000;
|
||||
/* background-color: #FFCCCF;*/
|
||||
}
|
||||
|
||||
div.warning p.admonition-title {
|
||||
}
|
||||
|
||||
div.admonition ul, div.admonition ol,
|
||||
div.warning ul, div.warning ol {
|
||||
div.admonition > ul,
|
||||
div.admonition > ol,
|
||||
div.warning > ul,
|
||||
div.warning > ol {
|
||||
margin: 0.1em 0.5em 0.5em 3em;
|
||||
padding: 0;
|
||||
}
|
||||
|
@ -295,6 +295,11 @@ The following is a list of deprecated interfaces.
|
||||
- 4.0
|
||||
- ``sphinx.builders.singlehtml.SingleFileHTMLBuilder``
|
||||
|
||||
* - ``sphinx.builders.htmlhelp``
|
||||
- 2.0
|
||||
- 4.0
|
||||
- ``sphinxcontrib.htmlhelp``
|
||||
|
||||
* - ``sphinx.builders.htmlhelp.HTMLHelpBuilder.open_file()``
|
||||
- 2.0
|
||||
- 4.0
|
||||
@ -370,6 +375,31 @@ The following is a list of deprecated interfaces.
|
||||
- 4.0
|
||||
- ``sphinxcontrib.jsmath``
|
||||
|
||||
* - ``sphinx.roles.abbr_role()``
|
||||
- 2.0
|
||||
- 4.0
|
||||
- ``sphinx.roles.Abbreviation``
|
||||
|
||||
* - ``sphinx.roles.emph_literal_role()``
|
||||
- 2.0
|
||||
- 4.0
|
||||
- ``sphinx.roles.EmphasizedLiteral``
|
||||
|
||||
* - ``sphinx.roles.menusel_role()``
|
||||
- 2.0
|
||||
- 4.0
|
||||
- ``sphinx.roles.GUILabel`` or ``sphinx.roles.MenuSelection``
|
||||
|
||||
* - ``sphinx.roles.index_role()``
|
||||
- 2.0
|
||||
- 4.0
|
||||
- ``sphinx.roles.Index``
|
||||
|
||||
* - ``sphinx.roles.indexmarkup_role()``
|
||||
- 2.0
|
||||
- 4.0
|
||||
- ``sphinx.roles.PEP`` or ``sphinx.roles.RFC``
|
||||
|
||||
* - ``sphinx.testing.util.remove_unicode_literal()``
|
||||
- 2.0
|
||||
- 4.0
|
||||
@ -420,6 +450,11 @@ The following is a list of deprecated interfaces.
|
||||
- 4.0
|
||||
- ``os.walk()``
|
||||
|
||||
* - ``sphinx.util.pycompat.NoneType``
|
||||
- 2.0
|
||||
- 4.0
|
||||
- ``sphinx.util.typing.NoneType``
|
||||
|
||||
* - ``sphinx.util.pycompat.TextIOWrapper``
|
||||
- 2.0
|
||||
- 4.0
|
||||
@ -440,6 +475,16 @@ The following is a list of deprecated interfaces.
|
||||
- 4.0
|
||||
- ``textwrap.indent()``
|
||||
|
||||
* - ``sphinx.util.pycompat.sys_encoding``
|
||||
- 2.0
|
||||
- 4.0
|
||||
- ``sys.getdefaultencoding()``
|
||||
|
||||
* - ``sphinx.util.pycompat.terminal_safe()``
|
||||
- 2.0
|
||||
- 4.0
|
||||
- ``sphinx.util.console.terminal_safe()``
|
||||
|
||||
* - ``sphinx.util.pycompat.u``
|
||||
- 2.0
|
||||
- 4.0
|
||||
|
@ -18,5 +18,11 @@ components (e.g. :class:`.Config`, :class:`.BuildEnvironment` and so on) easily.
|
||||
.. autoclass:: sphinx.util.docutils.SphinxDirective
|
||||
:members:
|
||||
|
||||
.. autoclass:: sphinx.util.docutils.SphinxRole
|
||||
:members:
|
||||
|
||||
.. autoclass:: sphinx.util.docutils.ReferenceRole
|
||||
:members:
|
||||
|
||||
.. autoclass:: sphinx.transforms.post_transforms.images.ImageConverter
|
||||
:members:
|
||||
|
@ -28,6 +28,7 @@ The builder's "name" must be given to the **-b** command-line option of
|
||||
|
||||
.. autoattribute:: supported_image_types
|
||||
|
||||
.. module:: sphinx.builders.dirhtml
|
||||
.. class:: DirectoryHTMLBuilder
|
||||
|
||||
This is a subclass of the standard HTML builder. Its output is a directory
|
||||
@ -45,6 +46,7 @@ The builder's "name" must be given to the **-b** command-line option of
|
||||
|
||||
.. versionadded:: 0.6
|
||||
|
||||
.. module:: sphinx.builders.singlehtml
|
||||
.. class:: SingleFileHTMLBuilder
|
||||
|
||||
This is an HTML builder that combines the whole project in one output file.
|
||||
|
@ -1331,6 +1331,12 @@ that use Sphinx's HTMLWriter class.
|
||||
|
||||
.. versionadded:: 1.6
|
||||
|
||||
.. deprecated:: 2.0
|
||||
|
||||
.. confval:: html4_writer
|
||||
|
||||
Output is processed with HTML4 writer. Default is ``False``.
|
||||
|
||||
Options for Single HTML output
|
||||
-------------------------------
|
||||
|
||||
|
@ -376,12 +376,12 @@ There are also new config values that you can set:
|
||||
'members': 'var1, var2',
|
||||
'member-order': 'bysource',
|
||||
'special-members': '__init__',
|
||||
'undoc-members': None,
|
||||
'undoc-members': True,
|
||||
'exclude-members': '__weakref__'
|
||||
}
|
||||
|
||||
Setting ``None`` is equivalent to giving the option name in the list format
|
||||
(i.e. it means "yes/true/on").
|
||||
Setting ``None`` or ``True`` to the value is equivalent to giving only the
|
||||
option name to the directives.
|
||||
|
||||
The supported options are ``'members'``, ``'member-order'``,
|
||||
``'undoc-members'``, ``'private-members'``, ``'special-members'``,
|
||||
@ -390,6 +390,9 @@ There are also new config values that you can set:
|
||||
|
||||
.. versionadded:: 1.8
|
||||
|
||||
.. versionchanged:: 2.0
|
||||
Accepts ``True`` as a value.
|
||||
|
||||
.. confval:: autodoc_docstring_signature
|
||||
|
||||
Functions imported from C modules cannot be introspected, and therefore the
|
||||
|
@ -38,3 +38,10 @@ Configuration
|
||||
called ``Introduction`` that appears in document ``index.rst``. Useful for
|
||||
avoiding ambiguity when the same section heading appears in different
|
||||
documents.
|
||||
|
||||
.. confval:: autosectionlabel_maxdepth
|
||||
|
||||
If set, autosectionlabel chooses the sections for labeling by its depth. For
|
||||
example, when set 1 to ``autosectionlabel_maxdepth``, labels are generated
|
||||
only for top level sections, and deeper sections are not labeled. It
|
||||
defaults to ``None`` (disabled).
|
||||
|
@ -143,6 +143,11 @@ also use this new config value:
|
||||
The new files will be placed in the directories specified in the
|
||||
``:toctree:`` options of the directives.
|
||||
|
||||
.. confval:: autosummary_mock_imports
|
||||
|
||||
This value contains a list of modules to be mocked up. See
|
||||
:confval:`autodoc_mock_imports` for more details. It defaults to
|
||||
:confval:`autodoc_mock_imports`.
|
||||
|
||||
Customizing templates
|
||||
---------------------
|
||||
|
@ -6,5 +6,11 @@
|
||||
|
||||
.. versionadded:: 1.4
|
||||
|
||||
.. versionchanged:: 2.0
|
||||
Support ``CNAME`` file
|
||||
|
||||
This extension creates ``.nojekyll`` file on generated HTML directory to publish
|
||||
the document on GitHub Pages.
|
||||
|
||||
It also creates a ``CNAME`` file for custom domains when :confval:`html_baseurl`
|
||||
set.
|
||||
|
1
setup.py
1
setup.py
@ -18,6 +18,7 @@ install_requires = [
|
||||
'sphinxcontrib-applehelp',
|
||||
'sphinxcontrib-devhelp',
|
||||
'sphinxcontrib-jsmath',
|
||||
'sphinxcontrib-htmlhelp',
|
||||
'sphinxcontrib-qthelp',
|
||||
'Jinja2>=2.3',
|
||||
'Pygments>=2.0',
|
||||
|
@ -37,7 +37,6 @@ from sphinx.registry import SphinxComponentRegistry
|
||||
from sphinx.util import docutils
|
||||
from sphinx.util import import_object, progress_message
|
||||
from sphinx.util import logging
|
||||
from sphinx.util import pycompat # noqa: F401
|
||||
from sphinx.util.build_phase import BuildPhase
|
||||
from sphinx.util.console import bold # type: ignore
|
||||
from sphinx.util.docutils import directive_helper
|
||||
@ -68,7 +67,6 @@ builtin_extensions = (
|
||||
'sphinx.builders.dummy',
|
||||
'sphinx.builders.gettext',
|
||||
'sphinx.builders.html',
|
||||
'sphinx.builders.htmlhelp',
|
||||
'sphinx.builders.latex',
|
||||
'sphinx.builders.linkcheck',
|
||||
'sphinx.builders.manpage',
|
||||
@ -108,6 +106,7 @@ builtin_extensions = (
|
||||
# 1st party extensions
|
||||
'sphinxcontrib.applehelp',
|
||||
'sphinxcontrib.devhelp',
|
||||
'sphinxcontrib.htmlhelp',
|
||||
'sphinxcontrib.qthelp',
|
||||
# Strictly, alabaster theme is not a builtin extension,
|
||||
# but it is loaded automatically to use it as default theme.
|
||||
|
@ -19,7 +19,7 @@ from sphinx.environment.adapters.asset import ImageAdapter
|
||||
from sphinx.errors import SphinxError
|
||||
from sphinx.io import read_doc
|
||||
from sphinx.locale import __
|
||||
from sphinx.util import i18n, import_object, logging, rst, status_iterator
|
||||
from sphinx.util import i18n, import_object, logging, rst, progress_message, status_iterator
|
||||
from sphinx.util.build_phase import BuildPhase
|
||||
from sphinx.util.console import bold # type: ignore
|
||||
from sphinx.util.docutils import sphinx_domains
|
||||
@ -351,16 +351,14 @@ class Builder:
|
||||
if updated_docnames:
|
||||
# save the environment
|
||||
from sphinx.application import ENV_PICKLE_FILENAME
|
||||
logger.info(bold(__('pickling environment... ')), nonl=True)
|
||||
with open(path.join(self.doctreedir, ENV_PICKLE_FILENAME), 'wb') as f:
|
||||
pickle.dump(self.env, f, pickle.HIGHEST_PROTOCOL)
|
||||
logger.info(__('done'))
|
||||
with progress_message(__('pickling environment')):
|
||||
with open(path.join(self.doctreedir, ENV_PICKLE_FILENAME), 'wb') as f:
|
||||
pickle.dump(self.env, f, pickle.HIGHEST_PROTOCOL)
|
||||
|
||||
# global actions
|
||||
self.app.phase = BuildPhase.CONSISTENCY_CHECK
|
||||
logger.info(bold(__('checking consistency... ')), nonl=True)
|
||||
self.env.check_consistency()
|
||||
logger.info(__('done'))
|
||||
with progress_message(__('checking consistency')):
|
||||
self.env.check_consistency()
|
||||
else:
|
||||
if method == 'update' and not docnames:
|
||||
logger.info(bold(__('no targets are out of date.')))
|
||||
@ -559,9 +557,8 @@ class Builder:
|
||||
docnames.add(tocdocname)
|
||||
docnames.add(self.config.master_doc)
|
||||
|
||||
logger.info(bold(__('preparing documents... ')), nonl=True)
|
||||
self.prepare_writing(docnames)
|
||||
logger.info(__('done'))
|
||||
with progress_message(__('preparing documents')):
|
||||
self.prepare_writing(docnames)
|
||||
|
||||
if self.parallel_ok:
|
||||
# number of subprocesses is parallel-1 because the main process
|
||||
|
@ -134,8 +134,6 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
||||
html_scaled_image_link = False
|
||||
# don't generate search index or include search page
|
||||
search = False
|
||||
# use html5 translator by default
|
||||
default_html5_translator = True
|
||||
|
||||
coverpage_name = COVERPAGE_NAME
|
||||
toctree_template = TOCTREE_TEMPLATE
|
||||
@ -655,7 +653,7 @@ class EpubBuilder(StandaloneHTMLBuilder):
|
||||
if incr:
|
||||
self.playorder += 1
|
||||
self.tocid += 1
|
||||
return NavPoint(self.esc('navPoint%d' % self.tocid), self.playorder,
|
||||
return NavPoint('navPoint%d' % self.tocid, self.playorder,
|
||||
node['text'], node['refuri'], [])
|
||||
|
||||
def build_navpoints(self, nodes):
|
||||
|
@ -18,7 +18,6 @@ import warnings
|
||||
from hashlib import md5
|
||||
from os import path
|
||||
|
||||
import docutils
|
||||
from docutils import nodes
|
||||
from docutils.core import publish_parts
|
||||
from docutils.frontend import OptionParser
|
||||
@ -58,7 +57,7 @@ if False:
|
||||
from sphinx.domains import Domain, Index, IndexEntry # NOQA
|
||||
from sphinx.util.tags import Tags # NOQA
|
||||
|
||||
# Experimental HTML5 Writer
|
||||
# HTML5 Writer is avialable or not
|
||||
if is_html5_writer_available():
|
||||
from sphinx.writers.html5 import HTML5Translator
|
||||
html5_ready = True
|
||||
@ -242,8 +241,6 @@ class StandaloneHTMLBuilder(Builder):
|
||||
search = True # for things like HTML help and Apple help: suppress search
|
||||
use_index = False
|
||||
download_support = True # enable download role
|
||||
# use html5 translator by default
|
||||
default_html5_translator = False
|
||||
|
||||
imgpath = None # type: str
|
||||
domain_indices = [] # type: List[Tuple[str, Type[Index], List[Tuple[str, List[IndexEntry]]], bool]] # NOQA
|
||||
@ -285,11 +282,6 @@ class StandaloneHTMLBuilder(Builder):
|
||||
|
||||
self.use_index = self.get_builder_config('use_index', 'html')
|
||||
|
||||
if self.config.html_experimental_html5_writer and not html5_ready:
|
||||
logger.warning(__('html_experimental_html5_writer is set, but current version '
|
||||
'is old. Docutils\' version should be 0.13 or newer, but %s.'),
|
||||
docutils.__version__)
|
||||
|
||||
def create_build_info(self):
|
||||
# type: () -> BuildInfo
|
||||
return BuildInfo(self.config, self.tags, ['html'])
|
||||
@ -374,14 +366,10 @@ class StandaloneHTMLBuilder(Builder):
|
||||
@property
|
||||
def default_translator_class(self): # type: ignore
|
||||
# type: () -> Type[nodes.NodeVisitor]
|
||||
use_html5_writer = self.config.html_experimental_html5_writer
|
||||
if use_html5_writer is None:
|
||||
use_html5_writer = self.default_html5_translator
|
||||
|
||||
if use_html5_writer and html5_ready:
|
||||
return HTML5Translator
|
||||
else:
|
||||
if not html5_ready or self.config.html4_writer:
|
||||
return HTMLTranslator
|
||||
else:
|
||||
return HTML5Translator
|
||||
|
||||
@property
|
||||
def math_renderer_name(self):
|
||||
@ -562,7 +550,7 @@ class StandaloneHTMLBuilder(Builder):
|
||||
'parents': [],
|
||||
'logo': logo,
|
||||
'favicon': favicon,
|
||||
'html5_doctype': self.config.html_experimental_html5_writer and html5_ready,
|
||||
'html5_doctype': html5_ready and not self.config.html4_writer
|
||||
}
|
||||
if self.theme:
|
||||
self.globalcontext.update(
|
||||
@ -1440,9 +1428,9 @@ def setup(app):
|
||||
app.add_config_value('html_search_options', {}, 'html')
|
||||
app.add_config_value('html_search_scorer', '', None)
|
||||
app.add_config_value('html_scaled_image_link', True, 'html')
|
||||
app.add_config_value('html_experimental_html5_writer', None, 'html')
|
||||
app.add_config_value('html_baseurl', '', 'html')
|
||||
app.add_config_value('html_math_renderer', None, 'env')
|
||||
app.add_config_value('html4_writer', False, 'html')
|
||||
|
||||
# event handlers
|
||||
app.connect('config-inited', convert_html_css_files)
|
||||
|
@ -9,364 +9,36 @@
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import html
|
||||
import os
|
||||
import warnings
|
||||
from os import path
|
||||
|
||||
from docutils import nodes
|
||||
from sphinxcontrib.htmlhelp import (
|
||||
chm_locales, chm_htmlescape, HTMLHelpBuilder, default_htmlhelp_basename
|
||||
)
|
||||
|
||||
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.builders.html import StandaloneHTMLBuilder
|
||||
from sphinx.deprecation import RemovedInSphinx40Warning
|
||||
from sphinx.environment.adapters.indexentries import IndexEntries
|
||||
from sphinx.locale import __
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.nodes import NodeMatcher
|
||||
from sphinx.util.osutil import make_filename_from_project
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Any, Dict, IO, List, Match, Tuple # NOQA
|
||||
from typing import Any, Dict # NOQA
|
||||
from sphinx.application import Sphinx # NOQA
|
||||
from sphinx.config import Config # NOQA
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Project file (*.hhp) template. 'outname' is the file basename (like
|
||||
# the pythlp in pythlp.hhp); 'version' is the doc version number (like
|
||||
# the 2.2 in Python 2.2).
|
||||
# The magical numbers in the long line under [WINDOWS] set most of the
|
||||
# user-visible features (visible buttons, tabs, etc).
|
||||
# About 0x10384e: This defines the buttons in the help viewer. The
|
||||
# following defns are taken from htmlhelp.h. Not all possibilities
|
||||
# actually work, and not all those that work are available from the Help
|
||||
# Workshop GUI. In particular, the Zoom/Font button works and is not
|
||||
# available from the GUI. The ones we're using are marked with 'x':
|
||||
#
|
||||
# 0x000002 Hide/Show x
|
||||
# 0x000004 Back x
|
||||
# 0x000008 Forward x
|
||||
# 0x000010 Stop
|
||||
# 0x000020 Refresh
|
||||
# 0x000040 Home x
|
||||
# 0x000080 Forward
|
||||
# 0x000100 Back
|
||||
# 0x000200 Notes
|
||||
# 0x000400 Contents
|
||||
# 0x000800 Locate x
|
||||
# 0x001000 Options x
|
||||
# 0x002000 Print x
|
||||
# 0x004000 Index
|
||||
# 0x008000 Search
|
||||
# 0x010000 History
|
||||
# 0x020000 Favorites
|
||||
# 0x040000 Jump 1
|
||||
# 0x080000 Jump 2
|
||||
# 0x100000 Zoom/Font x
|
||||
# 0x200000 TOC Next
|
||||
# 0x400000 TOC Prev
|
||||
|
||||
project_template = '''\
|
||||
[OPTIONS]
|
||||
Binary TOC=No
|
||||
Binary Index=No
|
||||
Compiled file=%(outname)s.chm
|
||||
Contents file=%(outname)s.hhc
|
||||
Default Window=%(outname)s
|
||||
Default topic=%(master_doc)s
|
||||
Display compile progress=No
|
||||
Full text search stop list file=%(outname)s.stp
|
||||
Full-text search=Yes
|
||||
Index file=%(outname)s.hhk
|
||||
Language=%(lcid)#x
|
||||
Title=%(title)s
|
||||
|
||||
[WINDOWS]
|
||||
%(outname)s="%(title)s","%(outname)s.hhc","%(outname)s.hhk",\
|
||||
"%(master_doc)s","%(master_doc)s",,,,,0x63520,220,0x10384e,[0,0,1024,768],,,,,,,0
|
||||
|
||||
[FILES]
|
||||
'''
|
||||
|
||||
contents_header = '''\
|
||||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<meta name="GENERATOR" content="Microsoft® HTML Help Workshop 4.1">
|
||||
<!-- Sitemap 1.0 -->
|
||||
</HEAD><BODY>
|
||||
<OBJECT type="text/site properties">
|
||||
<param name="Window Styles" value="0x801227">
|
||||
<param name="ImageType" value="Folder">
|
||||
</OBJECT>
|
||||
<UL>
|
||||
'''
|
||||
|
||||
contents_footer = '''\
|
||||
</UL></BODY></HTML>
|
||||
'''
|
||||
|
||||
object_sitemap = '''\
|
||||
<OBJECT type="text/sitemap">
|
||||
<param name="Name" value="%s">
|
||||
<param name="Local" value="%s">
|
||||
</OBJECT>
|
||||
'''
|
||||
|
||||
# List of words the full text search facility shouldn't index. This
|
||||
# becomes file outname.stp. Note that this list must be pretty small!
|
||||
# Different versions of the MS docs claim the file has a maximum size of
|
||||
# 256 or 512 bytes (including \r\n at the end of each line).
|
||||
# Note that "and", "or", "not" and "near" are operators in the search
|
||||
# language, so no point indexing them even if we wanted to.
|
||||
stopwords = """
|
||||
a and are as at
|
||||
be but by
|
||||
for
|
||||
if in into is it
|
||||
near no not
|
||||
of on or
|
||||
such
|
||||
that the their then there these they this to
|
||||
was will with
|
||||
""".split()
|
||||
|
||||
# The following list includes only languages supported by Sphinx. See
|
||||
# https://docs.microsoft.com/en-us/previous-versions/windows/embedded/ms930130(v=msdn.10)
|
||||
# for more.
|
||||
chm_locales = {
|
||||
# lang: LCID, encoding
|
||||
'ca': (0x403, 'cp1252'),
|
||||
'cs': (0x405, 'cp1250'),
|
||||
'da': (0x406, 'cp1252'),
|
||||
'de': (0x407, 'cp1252'),
|
||||
'en': (0x409, 'cp1252'),
|
||||
'es': (0x40a, 'cp1252'),
|
||||
'et': (0x425, 'cp1257'),
|
||||
'fa': (0x429, 'cp1256'),
|
||||
'fi': (0x40b, 'cp1252'),
|
||||
'fr': (0x40c, 'cp1252'),
|
||||
'hr': (0x41a, 'cp1250'),
|
||||
'hu': (0x40e, 'cp1250'),
|
||||
'it': (0x410, 'cp1252'),
|
||||
'ja': (0x411, 'cp932'),
|
||||
'ko': (0x412, 'cp949'),
|
||||
'lt': (0x427, 'cp1257'),
|
||||
'lv': (0x426, 'cp1257'),
|
||||
'nl': (0x413, 'cp1252'),
|
||||
'no_NB': (0x414, 'cp1252'),
|
||||
'pl': (0x415, 'cp1250'),
|
||||
'pt_BR': (0x416, 'cp1252'),
|
||||
'ru': (0x419, 'cp1251'),
|
||||
'sk': (0x41b, 'cp1250'),
|
||||
'sl': (0x424, 'cp1250'),
|
||||
'sv': (0x41d, 'cp1252'),
|
||||
'tr': (0x41f, 'cp1254'),
|
||||
'uk_UA': (0x422, 'cp1251'),
|
||||
'zh_CN': (0x804, 'cp936'),
|
||||
'zh_TW': (0x404, 'cp950'),
|
||||
}
|
||||
|
||||
|
||||
def chm_htmlescape(s, quote=True):
|
||||
# type: (str, bool) -> str
|
||||
"""
|
||||
chm_htmlescape() is a wrapper of html.escape().
|
||||
.hhc/.hhk files don't recognize hex escaping, we need convert
|
||||
hex escaping to decimal escaping. for example: ``'`` -> ``'``
|
||||
html.escape() may generates a hex escaping ``'`` for single
|
||||
quote ``'``, this wrapper fixes this.
|
||||
"""
|
||||
s = html.escape(s, quote)
|
||||
s = s.replace(''', ''') # re-escape as decimal
|
||||
return s
|
||||
|
||||
|
||||
class HTMLHelpBuilder(StandaloneHTMLBuilder):
|
||||
"""
|
||||
Builder that also outputs Windows HTML help project, contents and
|
||||
index files. Adapted from the original Doc/tools/prechm.py.
|
||||
"""
|
||||
name = 'htmlhelp'
|
||||
epilog = __('You can now run HTML Help Workshop with the .htp file in '
|
||||
'%(outdir)s.')
|
||||
|
||||
# don't copy the reST source
|
||||
copysource = False
|
||||
supported_image_types = ['image/png', 'image/gif', 'image/jpeg']
|
||||
|
||||
# don't add links
|
||||
add_permalinks = False
|
||||
# don't add sidebar etc.
|
||||
embedded = True
|
||||
|
||||
# don't generate search index or include search page
|
||||
search = False
|
||||
|
||||
lcid = 0x409
|
||||
encoding = 'cp1252'
|
||||
|
||||
def init(self):
|
||||
# type: () -> None
|
||||
# the output files for HTML help is .html by default
|
||||
self.out_suffix = '.html'
|
||||
self.link_suffix = '.html'
|
||||
super().init()
|
||||
# determine the correct locale setting
|
||||
locale = chm_locales.get(self.config.language)
|
||||
if locale is not None:
|
||||
self.lcid, self.encoding = locale
|
||||
|
||||
def open_file(self, outdir, basename, mode='w'):
|
||||
# type: (str, str, str) -> IO
|
||||
# open a file with the correct encoding for the selected language
|
||||
warnings.warn('HTMLHelpBuilder.open_file() is deprecated.',
|
||||
RemovedInSphinx40Warning)
|
||||
return open(path.join(outdir, basename), mode, encoding=self.encoding,
|
||||
errors='xmlcharrefreplace')
|
||||
|
||||
def update_page_context(self, pagename, templatename, ctx, event_arg):
|
||||
# type: (str, str, Dict, str) -> None
|
||||
ctx['encoding'] = self.encoding
|
||||
|
||||
def handle_finish(self):
|
||||
# type: () -> None
|
||||
self.build_hhx(self.outdir, self.config.htmlhelp_basename)
|
||||
|
||||
def write_doc(self, docname, doctree):
|
||||
# type: (str, nodes.document) -> None
|
||||
for node in doctree.traverse(nodes.reference):
|
||||
# add ``target=_blank`` attributes to external links
|
||||
if node.get('internal') is None and 'refuri' in node:
|
||||
node['target'] = '_blank'
|
||||
|
||||
super().write_doc(docname, doctree)
|
||||
|
||||
def build_hhx(self, outdir, outname):
|
||||
# type: (str, str) -> None
|
||||
logger.info(__('dumping stopword list...'))
|
||||
filename = path.join(outdir, outname + '.stp')
|
||||
with open(filename, 'w', encoding=self.encoding, errors='xmlcharrefreplace') as f:
|
||||
for word in sorted(stopwords):
|
||||
print(word, file=f)
|
||||
|
||||
logger.info(__('writing project file...'))
|
||||
filename = path.join(outdir, outname + '.hhp')
|
||||
with open(filename, 'w', encoding=self.encoding, errors='xmlcharrefreplace') as f:
|
||||
f.write(project_template % {
|
||||
'outname': outname,
|
||||
'title': self.config.html_title,
|
||||
'version': self.config.version,
|
||||
'project': self.config.project,
|
||||
'lcid': self.lcid,
|
||||
'master_doc': self.config.master_doc + self.out_suffix
|
||||
})
|
||||
if not outdir.endswith(os.sep):
|
||||
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 \
|
||||
fn.endswith('.html'):
|
||||
print(path.join(root, fn)[olen:].replace(os.sep, '\\'),
|
||||
file=f)
|
||||
|
||||
logger.info(__('writing TOC file...'))
|
||||
filename = path.join(outdir, outname + '.hhc')
|
||||
with open(filename, 'w', encoding=self.encoding, errors='xmlcharrefreplace') as f:
|
||||
f.write(contents_header)
|
||||
# special books
|
||||
f.write('<LI> ' + object_sitemap % (self.config.html_short_title,
|
||||
self.config.master_doc + self.out_suffix))
|
||||
for indexname, indexcls, content, collapse in self.domain_indices:
|
||||
f.write('<LI> ' + object_sitemap % (indexcls.localname,
|
||||
'%s.html' % indexname))
|
||||
# the TOC
|
||||
tocdoc = self.env.get_and_resolve_doctree(
|
||||
self.config.master_doc, self, prune_toctrees=False)
|
||||
|
||||
def write_toc(node, ullevel=0):
|
||||
# type: (nodes.Node, int) -> None
|
||||
if isinstance(node, nodes.list_item):
|
||||
f.write('<LI> ')
|
||||
for subnode in node:
|
||||
write_toc(subnode, ullevel)
|
||||
elif isinstance(node, nodes.reference):
|
||||
link = node['refuri']
|
||||
title = chm_htmlescape(node.astext(), True)
|
||||
f.write(object_sitemap % (title, link))
|
||||
elif isinstance(node, nodes.bullet_list):
|
||||
if ullevel != 0:
|
||||
f.write('<UL>\n')
|
||||
for subnode in node:
|
||||
write_toc(subnode, ullevel + 1)
|
||||
if ullevel != 0:
|
||||
f.write('</UL>\n')
|
||||
elif isinstance(node, addnodes.compact_paragraph):
|
||||
for subnode in node:
|
||||
write_toc(subnode, ullevel)
|
||||
|
||||
matcher = NodeMatcher(addnodes.compact_paragraph, toctree=True)
|
||||
for node in tocdoc.traverse(matcher): # type: addnodes.compact_paragraph
|
||||
write_toc(node)
|
||||
f.write(contents_footer)
|
||||
|
||||
logger.info(__('writing index file...'))
|
||||
index = IndexEntries(self.env).create_index(self)
|
||||
filename = path.join(outdir, outname + '.hhk')
|
||||
with open(filename, 'w', encoding=self.encoding, errors='xmlcharrefreplace') as f:
|
||||
f.write('<UL>\n')
|
||||
|
||||
def write_index(title, refs, subitems):
|
||||
# type: (str, List[Tuple[str, str]], List[Tuple[str, List[Tuple[str, str]]]]) -> None # NOQA
|
||||
def write_param(name, value):
|
||||
# type: (str, str) -> None
|
||||
item = ' <param name="%s" value="%s">\n' % (name, value)
|
||||
f.write(item)
|
||||
title = chm_htmlescape(title, True)
|
||||
f.write('<LI> <OBJECT type="text/sitemap">\n')
|
||||
write_param('Keyword', title)
|
||||
if len(refs) == 0:
|
||||
write_param('See Also', title)
|
||||
elif len(refs) == 1:
|
||||
write_param('Local', refs[0][1])
|
||||
else:
|
||||
for i, ref in enumerate(refs):
|
||||
# XXX: better title?
|
||||
write_param('Name', '[%d] %s' % (i, ref[1]))
|
||||
write_param('Local', ref[1])
|
||||
f.write('</OBJECT>\n')
|
||||
if subitems:
|
||||
f.write('<UL> ')
|
||||
for subitem in subitems:
|
||||
write_index(subitem[0], subitem[1], [])
|
||||
f.write('</UL>')
|
||||
for (key, group) in index:
|
||||
for title, (refs, subitems, key_) in group:
|
||||
write_index(title, refs, subitems)
|
||||
f.write('</UL>\n')
|
||||
|
||||
|
||||
def default_htmlhelp_basename(config):
|
||||
# type: (Config) -> str
|
||||
"""Better default htmlhelp_basename setting."""
|
||||
return make_filename_from_project(config.project) + 'doc'
|
||||
deprecated_alias('sphinx.builders.devhelp',
|
||||
{
|
||||
'chm_locales': chm_locales,
|
||||
'chm_htmlescape': chm_htmlescape,
|
||||
'HTMLHelpBuilder': HTMLHelpBuilder,
|
||||
'default_htmlhelp_basename': default_htmlhelp_basename,
|
||||
},
|
||||
RemovedInSphinx40Warning)
|
||||
|
||||
|
||||
def setup(app):
|
||||
# type: (Sphinx) -> Dict[str, Any]
|
||||
app.setup_extension('sphinx.builders.html')
|
||||
app.add_builder(HTMLHelpBuilder)
|
||||
|
||||
app.add_config_value('htmlhelp_basename', default_htmlhelp_basename, None)
|
||||
app.add_config_value('htmlhelp_file_suffix', None, 'html', [str])
|
||||
app.add_config_value('htmlhelp_link_suffix', None, 'html', [str])
|
||||
warnings.warn('sphinx.builders.htmlhelp has been moved to sphinxcontrib-htmlhelp.',
|
||||
RemovedInSphinx40Warning)
|
||||
app.setup_extension('sphinxcontrib.htmlhelp')
|
||||
|
||||
return {
|
||||
'version': 'builtin',
|
||||
|
@ -28,7 +28,7 @@ from sphinx.environment.adapters.asset import ImageAdapter
|
||||
from sphinx.errors import SphinxError
|
||||
from sphinx.locale import _, __
|
||||
from sphinx.transforms import SphinxTransformer
|
||||
from sphinx.util import texescape, logging, status_iterator
|
||||
from sphinx.util import texescape, logging, progress_message, status_iterator
|
||||
from sphinx.util.console import bold, darkgreen # type: ignore
|
||||
from sphinx.util.docutils import SphinxFileOutput, new_document
|
||||
from sphinx.util.fileutil import copy_asset_file
|
||||
@ -250,33 +250,32 @@ class LaTeXBuilder(Builder):
|
||||
toctree_only = entry[5]
|
||||
destination = SphinxFileOutput(destination_path=path.join(self.outdir, targetname),
|
||||
encoding='utf-8', overwrite_if_changed=True)
|
||||
logger.info(__("processing %s..."), targetname, nonl=True)
|
||||
toctrees = self.env.get_doctree(docname).traverse(addnodes.toctree)
|
||||
if toctrees:
|
||||
if toctrees[0].get('maxdepth') > 0:
|
||||
tocdepth = toctrees[0].get('maxdepth')
|
||||
with progress_message(__("processing %s") % targetname):
|
||||
toctrees = self.env.get_doctree(docname).traverse(addnodes.toctree)
|
||||
if toctrees:
|
||||
if toctrees[0].get('maxdepth') > 0:
|
||||
tocdepth = toctrees[0].get('maxdepth')
|
||||
else:
|
||||
tocdepth = None
|
||||
else:
|
||||
tocdepth = None
|
||||
else:
|
||||
tocdepth = None
|
||||
doctree = self.assemble_doctree(
|
||||
docname, toctree_only,
|
||||
appendices=((docclass != 'howto') and self.config.latex_appendices or []))
|
||||
doctree['tocdepth'] = tocdepth
|
||||
self.apply_transforms(doctree)
|
||||
self.post_process_images(doctree)
|
||||
self.update_doc_context(title, author)
|
||||
doctree = self.assemble_doctree(
|
||||
docname, toctree_only,
|
||||
appendices=((docclass != 'howto') and self.config.latex_appendices or []))
|
||||
doctree['tocdepth'] = tocdepth
|
||||
self.apply_transforms(doctree)
|
||||
self.post_process_images(doctree)
|
||||
self.update_doc_context(title, author)
|
||||
|
||||
logger.info(__("writing... "), nonl=True)
|
||||
docsettings.author = author
|
||||
docsettings.title = title
|
||||
docsettings.contentsname = self.get_contentsname(docname)
|
||||
docsettings.docname = docname
|
||||
docsettings.docclass = docclass
|
||||
with progress_message(__("writing")):
|
||||
docsettings.author = author
|
||||
docsettings.title = title
|
||||
docsettings.contentsname = self.get_contentsname(docname)
|
||||
docsettings.docname = docname
|
||||
docsettings.docclass = docclass
|
||||
|
||||
doctree.settings = docsettings
|
||||
docwriter.write(doctree, destination)
|
||||
logger.info(__("done"))
|
||||
doctree.settings = docsettings
|
||||
docwriter.write(doctree, destination)
|
||||
|
||||
def get_contentsname(self, indexfile):
|
||||
# type: (str) -> str
|
||||
@ -354,8 +353,15 @@ class LaTeXBuilder(Builder):
|
||||
# type: () -> None
|
||||
self.copy_image_files()
|
||||
self.write_message_catalog()
|
||||
self.copy_support_files()
|
||||
|
||||
# copy TeX support files from texinputs
|
||||
if self.config.latex_additional_files:
|
||||
self.copy_latex_additional_files()
|
||||
|
||||
@progress_message(__('copying TeX support files'))
|
||||
def copy_support_files(self):
|
||||
# type: () -> None
|
||||
"""copy TeX support files from texinputs."""
|
||||
# configure usage of xindy (impacts Makefile and latexmkrc)
|
||||
# FIXME: convert this rather to a confval with suitable default
|
||||
# according to language ? but would require extra documentation
|
||||
@ -386,21 +392,19 @@ class LaTeXBuilder(Builder):
|
||||
copy_asset_file(path.join(staticdirname, 'Makefile_t'),
|
||||
self.outdir, context=context)
|
||||
|
||||
# copy additional files
|
||||
if self.config.latex_additional_files:
|
||||
logger.info(bold(__('copying additional files...')), nonl=True)
|
||||
for filename in self.config.latex_additional_files:
|
||||
logger.info(' ' + filename, nonl=True)
|
||||
copy_asset_file(path.join(self.confdir, filename), self.outdir)
|
||||
logger.info('')
|
||||
|
||||
# the logo is handled differently
|
||||
if self.config.latex_logo:
|
||||
if not path.isfile(path.join(self.confdir, self.config.latex_logo)):
|
||||
raise SphinxError(__('logo file %r does not exist') % self.config.latex_logo)
|
||||
else:
|
||||
copy_asset_file(path.join(self.confdir, self.config.latex_logo), self.outdir)
|
||||
logger.info(__('done'))
|
||||
|
||||
@progress_message(__('copying additional files'))
|
||||
def copy_latex_additional_files(self):
|
||||
# type: () -> None
|
||||
for filename in self.config.latex_additional_files:
|
||||
logger.info(' ' + filename, nonl=True)
|
||||
copy_asset_file(path.join(self.confdir, filename), self.outdir)
|
||||
|
||||
def copy_image_files(self):
|
||||
# type: () -> None
|
||||
@ -439,7 +443,7 @@ def validate_config_values(app, config):
|
||||
for key in list(config.latex_elements):
|
||||
if key not in DEFAULT_SETTINGS:
|
||||
msg = __("Unknown configure key: latex_elements[%r]. ignored.")
|
||||
logger.warning(msg % key)
|
||||
logger.warning(msg % (key,))
|
||||
config.latex_elements.pop(key)
|
||||
|
||||
|
||||
|
@ -25,7 +25,7 @@ from sphinx.util import encode_uri, requests, logging
|
||||
from sphinx.util.console import ( # type: ignore
|
||||
purple, red, darkgreen, darkgray, darkred, turquoise
|
||||
)
|
||||
from sphinx.util.nodes import traverse_parent
|
||||
from sphinx.util.nodes import get_node_line
|
||||
from sphinx.util.requests import is_ssl_error
|
||||
|
||||
if False:
|
||||
@ -271,17 +271,24 @@ class CheckExternalLinksBuilder(Builder):
|
||||
# type: (str, nodes.Node) -> None
|
||||
logger.info('')
|
||||
n = 0
|
||||
for node in doctree.traverse(nodes.reference):
|
||||
if 'refuri' not in node:
|
||||
|
||||
# reference nodes
|
||||
for refnode in doctree.traverse(nodes.reference):
|
||||
if 'refuri' not in refnode:
|
||||
continue
|
||||
uri = node['refuri']
|
||||
lineno = None
|
||||
for parent in traverse_parent(node):
|
||||
if parent.line:
|
||||
lineno = parent.line
|
||||
break
|
||||
uri = refnode['refuri']
|
||||
lineno = get_node_line(refnode)
|
||||
self.wqueue.put((uri, docname, lineno), False)
|
||||
n += 1
|
||||
|
||||
# image nodes
|
||||
for imgnode in doctree.traverse(nodes.image):
|
||||
uri = imgnode['candidates'].get('?')
|
||||
if uri and '://' in uri:
|
||||
lineno = get_node_line(imgnode)
|
||||
self.wqueue.put((uri, docname, lineno), False)
|
||||
n += 1
|
||||
|
||||
done = 0
|
||||
while done < n:
|
||||
self.process_result(self.rqueue.get())
|
||||
|
@ -18,7 +18,8 @@ from sphinx.builders import Builder
|
||||
from sphinx.environment import NoUri
|
||||
from sphinx.locale import __
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.console import bold, darkgreen # type: ignore
|
||||
from sphinx.util import progress_message
|
||||
from sphinx.util.console import darkgreen # type: ignore
|
||||
from sphinx.util.nodes import inline_all_toctrees
|
||||
from sphinx.util.osutil import make_filename_from_project
|
||||
from sphinx.writers.manpage import ManualPageWriter, ManualPageTranslator
|
||||
@ -60,6 +61,7 @@ class ManualPageBuilder(Builder):
|
||||
return ''
|
||||
raise NoUri
|
||||
|
||||
@progress_message(__('writing'))
|
||||
def write(self, *ignored):
|
||||
# type: (Any) -> None
|
||||
docwriter = ManualPageWriter(self)
|
||||
@ -68,8 +70,6 @@ class ManualPageBuilder(Builder):
|
||||
components=(docwriter,),
|
||||
read_config_files=True).get_default_values() # type: Any
|
||||
|
||||
logger.info(bold(__('writing... ')), nonl=True)
|
||||
|
||||
for info in self.config.man_pages:
|
||||
docname, name, description, authors, section = info
|
||||
if docname not in self.env.all_docs:
|
||||
@ -105,7 +105,6 @@ class ManualPageBuilder(Builder):
|
||||
pendingnode.replace_self(pendingnode.children)
|
||||
|
||||
docwriter.write(largetree, destination)
|
||||
logger.info('')
|
||||
|
||||
def finish(self):
|
||||
# type: () -> None
|
||||
|
@ -16,7 +16,8 @@ from sphinx.builders.html import StandaloneHTMLBuilder
|
||||
from sphinx.environment.adapters.toctree import TocTree
|
||||
from sphinx.locale import __
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.console import bold, darkgreen # type: ignore
|
||||
from sphinx.util import progress_message
|
||||
from sphinx.util.console import darkgreen # type: ignore
|
||||
from sphinx.util.nodes import inline_all_toctrees
|
||||
|
||||
if False:
|
||||
@ -162,24 +163,32 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
|
||||
# type: (Any) -> None
|
||||
docnames = self.env.all_docs
|
||||
|
||||
logger.info(bold(__('preparing documents... ')), nonl=True)
|
||||
self.prepare_writing(docnames) # type: ignore
|
||||
logger.info(__('done'))
|
||||
with progress_message(__('preparing documents')):
|
||||
self.prepare_writing(docnames) # type: ignore
|
||||
|
||||
logger.info(bold(__('assembling single document... ')), nonl=True)
|
||||
doctree = self.assemble_doctree()
|
||||
self.env.toc_secnumbers = self.assemble_toc_secnumbers()
|
||||
self.env.toc_fignumbers = self.assemble_toc_fignumbers()
|
||||
logger.info('')
|
||||
logger.info(bold(__('writing... ')), nonl=True)
|
||||
self.write_doc_serialized(self.config.master_doc, doctree)
|
||||
self.write_doc(self.config.master_doc, doctree)
|
||||
logger.info(__('done'))
|
||||
with progress_message(__('assembling single document')):
|
||||
doctree = self.assemble_doctree()
|
||||
self.env.toc_secnumbers = self.assemble_toc_secnumbers()
|
||||
self.env.toc_fignumbers = self.assemble_toc_fignumbers()
|
||||
|
||||
with progress_message(__('writing')):
|
||||
self.write_doc_serialized(self.config.master_doc, doctree)
|
||||
self.write_doc(self.config.master_doc, doctree)
|
||||
|
||||
def finish(self):
|
||||
# type: () -> None
|
||||
self.write_additional_files()
|
||||
self.copy_image_files()
|
||||
self.copy_download_files()
|
||||
self.copy_static_files()
|
||||
self.copy_extra_files()
|
||||
self.write_buildinfo()
|
||||
self.dump_inventory()
|
||||
|
||||
@progress_message(__('writing additional files'))
|
||||
def write_additional_files(self):
|
||||
# type: () -> None
|
||||
# no indices or search pages are supported
|
||||
logger.info(bold(__('writing additional files...')), nonl=True)
|
||||
|
||||
# additional pages from conf.py
|
||||
for pagename, template in self.config.html_additional_pages.items():
|
||||
@ -191,15 +200,6 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
|
||||
fn = path.join(self.outdir, '_static', 'opensearch.xml')
|
||||
self.handle_page('opensearch', {}, 'opensearch.xml', outfilename=fn)
|
||||
|
||||
logger.info('')
|
||||
|
||||
self.copy_image_files()
|
||||
self.copy_download_files()
|
||||
self.copy_static_files()
|
||||
self.copy_extra_files()
|
||||
self.write_buildinfo()
|
||||
self.dump_inventory()
|
||||
|
||||
|
||||
def setup(app):
|
||||
# type: (Sphinx) -> Dict[str, Any]
|
||||
|
@ -22,8 +22,8 @@ from sphinx.environment import NoUri
|
||||
from sphinx.environment.adapters.asset import ImageAdapter
|
||||
from sphinx.locale import _, __
|
||||
from sphinx.util import logging
|
||||
from sphinx.util import status_iterator
|
||||
from sphinx.util.console import bold, darkgreen # type: ignore
|
||||
from sphinx.util import progress_message, status_iterator
|
||||
from sphinx.util.console import darkgreen # type: ignore
|
||||
from sphinx.util.docutils import new_document
|
||||
from sphinx.util.fileutil import copy_asset_file
|
||||
from sphinx.util.nodes import inline_all_toctrees
|
||||
@ -113,28 +113,27 @@ class TexinfoBuilder(Builder):
|
||||
destination = FileOutput(
|
||||
destination_path=path.join(self.outdir, targetname),
|
||||
encoding='utf-8')
|
||||
logger.info(__("processing %s..."), targetname, nonl=True)
|
||||
doctree = self.assemble_doctree(
|
||||
docname, toctree_only,
|
||||
appendices=(self.config.texinfo_appendices or []))
|
||||
logger.info(__("writing... "), nonl=True)
|
||||
self.post_process_images(doctree)
|
||||
docwriter = TexinfoWriter(self)
|
||||
settings = OptionParser(
|
||||
defaults=self.env.settings,
|
||||
components=(docwriter,),
|
||||
read_config_files=True).get_default_values() # type: Any
|
||||
settings.author = author
|
||||
settings.title = title
|
||||
settings.texinfo_filename = targetname[:-5] + '.info'
|
||||
settings.texinfo_elements = self.config.texinfo_elements
|
||||
settings.texinfo_dir_entry = direntry or ''
|
||||
settings.texinfo_dir_category = category or ''
|
||||
settings.texinfo_dir_description = description or ''
|
||||
settings.docname = docname
|
||||
doctree.settings = settings
|
||||
docwriter.write(doctree, destination)
|
||||
logger.info(__("done"))
|
||||
with progress_message(__("processing %s") % targetname):
|
||||
appendices = self.config.texinfo_appendices or []
|
||||
doctree = self.assemble_doctree(docname, toctree_only, appendices=appendices)
|
||||
|
||||
with progress_message(__("writing")):
|
||||
self.post_process_images(doctree)
|
||||
docwriter = TexinfoWriter(self)
|
||||
settings = OptionParser(
|
||||
defaults=self.env.settings,
|
||||
components=(docwriter,),
|
||||
read_config_files=True).get_default_values() # type: Any
|
||||
settings.author = author
|
||||
settings.title = title
|
||||
settings.texinfo_filename = targetname[:-5] + '.info'
|
||||
settings.texinfo_elements = self.config.texinfo_elements
|
||||
settings.texinfo_dir_entry = direntry or ''
|
||||
settings.texinfo_dir_category = category or ''
|
||||
settings.texinfo_dir_description = description or ''
|
||||
settings.docname = docname
|
||||
doctree.settings = settings
|
||||
docwriter.write(doctree, destination)
|
||||
|
||||
def assemble_doctree(self, indexfile, toctree_only, appendices):
|
||||
# type: (str, bool, List[str]) -> nodes.document
|
||||
@ -182,16 +181,7 @@ class TexinfoBuilder(Builder):
|
||||
def finish(self):
|
||||
# type: () -> None
|
||||
self.copy_image_files()
|
||||
|
||||
logger.info(bold(__('copying Texinfo support files... ')), nonl=True)
|
||||
# copy Makefile
|
||||
fn = path.join(self.outdir, 'Makefile')
|
||||
logger.info(fn, nonl=True)
|
||||
try:
|
||||
copy_asset_file(os.path.join(template_dir, 'Makefile'), fn)
|
||||
except OSError as err:
|
||||
logger.warning(__("error writing file %s: %s"), fn, err)
|
||||
logger.info(__(' done'))
|
||||
self.copy_support_files()
|
||||
|
||||
def copy_image_files(self):
|
||||
# type: () -> None
|
||||
@ -208,6 +198,15 @@ class TexinfoBuilder(Builder):
|
||||
logger.warning(__('cannot copy image file %r: %s'),
|
||||
path.join(self.srcdir, src), err)
|
||||
|
||||
def copy_support_files(self):
|
||||
# type: () -> None
|
||||
try:
|
||||
with progress_message(__('copying Texinfo support files')):
|
||||
logger.info('Makefile ', nonl=True)
|
||||
copy_asset_file(os.path.join(template_dir, 'Makefile'), self.outdir)
|
||||
except OSError as err:
|
||||
logger.warning(__("error writing file Makefile: %s"), err)
|
||||
|
||||
|
||||
def default_texinfo_documents(config):
|
||||
# type: (Config) -> List[Tuple[str, str, str, str, str, str, str]]
|
||||
|
@ -23,9 +23,8 @@ from sphinx.application import Sphinx
|
||||
from sphinx.errors import SphinxError
|
||||
from sphinx.locale import __
|
||||
from sphinx.util import Tee, format_exception_cut_frames, save_traceback
|
||||
from sphinx.util.console import red, nocolor, color_terminal # type: ignore
|
||||
from sphinx.util.console import red, nocolor, color_terminal, terminal_safe # type: ignore
|
||||
from sphinx.util.docutils import docutils_namespace, patch_docutils
|
||||
from sphinx.util.pycompat import terminal_safe
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
|
@ -17,7 +17,6 @@ import time
|
||||
import warnings
|
||||
from collections import OrderedDict
|
||||
from os import path
|
||||
from urllib.parse import quote
|
||||
|
||||
# try to import readline, unix specific enhancement
|
||||
try:
|
||||
@ -37,11 +36,10 @@ import sphinx.locale
|
||||
from sphinx import __display_version__, package_dir
|
||||
from sphinx.deprecation import RemovedInSphinx40Warning
|
||||
from sphinx.locale import __
|
||||
from sphinx.util import texescape
|
||||
from sphinx.util.console import ( # type: ignore
|
||||
colorize, bold, red, turquoise, nocolor, color_terminal
|
||||
)
|
||||
from sphinx.util.osutil import ensuredir, make_filename
|
||||
from sphinx.util.osutil import ensuredir
|
||||
from sphinx.util.template import SphinxRenderer
|
||||
|
||||
if False:
|
||||
@ -375,25 +373,15 @@ def generate(d, overwrite=True, silent=False, templatedir=None):
|
||||
"""Generate project based on values in *d*."""
|
||||
template = QuickstartRenderer(templatedir=templatedir)
|
||||
|
||||
texescape.init()
|
||||
|
||||
if 'mastertoctree' not in d:
|
||||
d['mastertoctree'] = ''
|
||||
if 'mastertocmaxdepth' not in d:
|
||||
d['mastertocmaxdepth'] = 2
|
||||
|
||||
d['PY3'] = True
|
||||
d['project_fn'] = make_filename(d['project'])
|
||||
d['project_url'] = quote(d['project'].encode('idna'))
|
||||
d['project_manpage'] = d['project_fn'].lower()
|
||||
d['now'] = time.asctime()
|
||||
d['project_underline'] = column_width(d['project']) * '='
|
||||
d.setdefault('extensions', [])
|
||||
d['copyright'] = time.strftime('%Y') + ', ' + d['author']
|
||||
d['author_texescaped'] = d['author'].translate(texescape.tex_escape_map)
|
||||
d['project_doc'] = d['project'] + ' Documentation'
|
||||
d['project_doc_texescaped'] = (d['project'] + ' Documentation').\
|
||||
translate(texescape.tex_escape_map)
|
||||
|
||||
ensuredir(d['path'])
|
||||
|
||||
|
@ -22,7 +22,8 @@ from sphinx.locale import _, __
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.i18n import format_date
|
||||
from sphinx.util.osutil import cd
|
||||
from sphinx.util.pycompat import execfile_, NoneType
|
||||
from sphinx.util.pycompat import execfile_
|
||||
from sphinx.util.typing import NoneType
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
|
@ -48,7 +48,7 @@ class _ModuleWrapper(object):
|
||||
def __getattr__(self, name):
|
||||
# type: (str) -> Any
|
||||
if name in self._objects:
|
||||
warnings.warn("%s.%s is now deprecated. Please refer CHANGES to grasp"
|
||||
warnings.warn("%s.%s is now deprecated. Please refer CHANGES to grasp "
|
||||
"the changes of Sphinx API." % (self._modname, name),
|
||||
self._warning, stacklevel=3)
|
||||
return self._objects[name]
|
||||
|
@ -7158,7 +7158,7 @@ class CPPDomain(Domain):
|
||||
# the non-identifier refs are cross-references, which should be processed:
|
||||
# - fix parenthesis due to operator() and add_function_parentheses
|
||||
if typ != "identifier":
|
||||
title = contnode.pop(0).astext() # type: ignore
|
||||
title = contnode.pop(0).astext()
|
||||
# If it's operator(), we need to add '()' if explicit function parens
|
||||
# are requested. Then the Sphinx machinery will add another pair.
|
||||
# Also, if it's an 'any' ref that resolves to a function, we need to add
|
||||
|
@ -151,7 +151,7 @@ class PyXrefMixin:
|
||||
delims_re = re.compile(delims)
|
||||
sub_targets = re.split(delims, target)
|
||||
|
||||
split_contnode = bool(contnode and contnode.astext() == target) # type: ignore
|
||||
split_contnode = bool(contnode and contnode.astext() == target)
|
||||
|
||||
results = []
|
||||
for sub_target in filter(None, sub_targets):
|
||||
|
@ -72,7 +72,7 @@ INSTANCEATTR = object()
|
||||
def members_option(arg):
|
||||
# type: (Any) -> Union[object, List[str]]
|
||||
"""Used to convert the :members: option to auto directives."""
|
||||
if arg is None:
|
||||
if arg is None or arg is True:
|
||||
return ALL
|
||||
return [x.strip() for x in arg.split(',')]
|
||||
|
||||
|
@ -32,13 +32,18 @@ logger = logging.getLogger(__name__)
|
||||
class _MockObject:
|
||||
"""Used by autodoc_mock_imports."""
|
||||
|
||||
__display_name__ = '_MockObject'
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
# type: (Any, Any) -> Any
|
||||
if len(args) == 3 and isinstance(args[1], tuple) and args[1][-1].__class__ is cls:
|
||||
# subclassing MockObject
|
||||
return type(args[0], (_MockObject,), args[2], **kwargs) # type: ignore
|
||||
else:
|
||||
return super(_MockObject, cls).__new__(cls)
|
||||
if len(args) == 3 and isinstance(args[1], tuple):
|
||||
superclass = args[1][-1].__class__
|
||||
if superclass is cls:
|
||||
# subclassing MockObject
|
||||
return _make_subclass(args[0], superclass.__display_name__,
|
||||
superclass=superclass, attributes=args[2])
|
||||
|
||||
return super(_MockObject, cls).__new__(cls)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# type: (Any, Any) -> None
|
||||
@ -62,11 +67,11 @@ class _MockObject:
|
||||
|
||||
def __getitem__(self, key):
|
||||
# type: (str) -> _MockObject
|
||||
return self
|
||||
return _make_subclass(key, self.__display_name__, self.__class__)()
|
||||
|
||||
def __getattr__(self, key):
|
||||
# type: (str) -> _MockObject
|
||||
return self
|
||||
return _make_subclass(key, self.__display_name__, self.__class__)()
|
||||
|
||||
def __call__(self, *args, **kw):
|
||||
# type: (Any, Any) -> Any
|
||||
@ -75,6 +80,18 @@ class _MockObject:
|
||||
return args[0]
|
||||
return self
|
||||
|
||||
def __repr__(self):
|
||||
# type: () -> str
|
||||
return self.__display_name__
|
||||
|
||||
|
||||
def _make_subclass(name, module, superclass=_MockObject, attributes=None):
|
||||
# type: (str, str, Any, dict) -> Any
|
||||
attrs = {'__module__': module, '__display_name__': module + '.' + name}
|
||||
attrs.update(attributes or {})
|
||||
|
||||
return type(name, (superclass,), attrs)
|
||||
|
||||
|
||||
class _MockModule(ModuleType):
|
||||
"""Used by autodoc_mock_imports."""
|
||||
@ -92,9 +109,11 @@ class _MockModule(ModuleType):
|
||||
|
||||
def __getattr__(self, name):
|
||||
# type: (str) -> _MockObject
|
||||
o = _MockObject()
|
||||
o.__module__ = self.__name__
|
||||
return o
|
||||
return _make_subclass(name, self.__name__)()
|
||||
|
||||
def __repr__(self):
|
||||
# type: () -> str
|
||||
return self.__name__
|
||||
|
||||
|
||||
class _MockImporter(MetaPathFinder):
|
||||
|
@ -30,11 +30,23 @@ if False:
|
||||
from sphinx.application import Sphinx # NOQA
|
||||
|
||||
|
||||
def get_node_depth(node):
|
||||
i = 0
|
||||
cur_node = node
|
||||
while cur_node.parent != node.document:
|
||||
cur_node = cur_node.parent
|
||||
i += 1
|
||||
return i
|
||||
|
||||
|
||||
def register_sections_as_label(app, document):
|
||||
# type: (Sphinx, nodes.Node) -> None
|
||||
labels = app.env.domaindata['std']['labels']
|
||||
anonlabels = app.env.domaindata['std']['anonlabels']
|
||||
for node in document.traverse(nodes.section):
|
||||
if (app.config.autosectionlabel_maxdepth and
|
||||
get_node_depth(node) >= app.config.autosectionlabel_maxdepth):
|
||||
continue
|
||||
labelid = node['ids'][0]
|
||||
docname = app.env.docname
|
||||
title = cast(nodes.title, node[0])
|
||||
@ -57,6 +69,7 @@ def register_sections_as_label(app, document):
|
||||
def setup(app):
|
||||
# type: (Sphinx) -> Dict[str, Any]
|
||||
app.add_config_value('autosectionlabel_prefix_document', False, 'env')
|
||||
app.add_config_value('autosectionlabel_maxdepth', None, 'env')
|
||||
app.connect('doctree-read', register_sections_as_label)
|
||||
|
||||
return {
|
||||
|
@ -72,7 +72,7 @@ from sphinx.deprecation import RemovedInSphinx40Warning
|
||||
from sphinx.environment.adapters.toctree import TocTree
|
||||
from sphinx.ext.autodoc import get_documenters
|
||||
from sphinx.ext.autodoc.directive import DocumenterBridge, Options
|
||||
from sphinx.ext.autodoc.importer import import_module
|
||||
from sphinx.ext.autodoc.importer import import_module, mock
|
||||
from sphinx.locale import __
|
||||
from sphinx.pycode import ModuleAnalyzer, PycodeError
|
||||
from sphinx.util import import_object, rst, logging
|
||||
@ -286,7 +286,8 @@ class Autosummary(SphinxDirective):
|
||||
display_name = name.split('.')[-1]
|
||||
|
||||
try:
|
||||
real_name, obj, parent, modname = import_by_name(name, prefixes=prefixes)
|
||||
with mock(self.config.autosummary_mock_imports):
|
||||
real_name, obj, parent, modname = import_by_name(name, prefixes=prefixes)
|
||||
except ImportError:
|
||||
logger.warning(__('failed to import %s'), name)
|
||||
items.append((name, '', '', name))
|
||||
@ -702,10 +703,11 @@ def process_generate_options(app):
|
||||
'But your source_suffix does not contain .rst. Skipped.'))
|
||||
return
|
||||
|
||||
generate_autosummary_docs(genfiles, builder=app.builder,
|
||||
warn=logger.warning, info=logger.info,
|
||||
suffix=suffix, base_path=app.srcdir,
|
||||
app=app)
|
||||
with mock(app.config.autosummary_mock_imports):
|
||||
generate_autosummary_docs(genfiles, builder=app.builder,
|
||||
warn=logger.warning, info=logger.info,
|
||||
suffix=suffix, base_path=app.srcdir,
|
||||
app=app)
|
||||
|
||||
|
||||
def setup(app):
|
||||
@ -729,4 +731,7 @@ def setup(app):
|
||||
app.connect('doctree-read', process_autosummary_toc)
|
||||
app.connect('builder-inited', process_generate_options)
|
||||
app.add_config_value('autosummary_generate', [], True, [bool])
|
||||
app.add_config_value('autosummary_mock_imports',
|
||||
lambda config: config.autodoc_mock_imports, 'env')
|
||||
|
||||
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
|
||||
|
@ -9,6 +9,7 @@
|
||||
"""
|
||||
|
||||
import os
|
||||
import urllib
|
||||
|
||||
import sphinx
|
||||
|
||||
@ -19,14 +20,22 @@ if False:
|
||||
from sphinx.environment import BuildEnvironment # NOQA
|
||||
|
||||
|
||||
def create_nojekyll(app, env):
|
||||
def create_nojekyll_and_cname(app, env):
|
||||
# type: (Sphinx, BuildEnvironment) -> None
|
||||
if app.builder.format == 'html':
|
||||
path = os.path.join(app.builder.outdir, '.nojekyll')
|
||||
open(path, 'wt').close()
|
||||
open(os.path.join(app.builder.outdir, '.nojekyll'), 'wt').close()
|
||||
|
||||
html_baseurl = app.config.html_baseurl
|
||||
if html_baseurl:
|
||||
domain = urllib.parse.urlparse(html_baseurl).hostname
|
||||
if domain and not domain.endswith(".github.io"):
|
||||
with open(os.path.join(app.builder.outdir, 'CNAME'), 'wt') as f:
|
||||
# NOTE: don't write a trailing newline. The `CNAME` file that's
|
||||
# auto-generated by the Github UI doesn't have one.
|
||||
f.write(domain)
|
||||
|
||||
|
||||
def setup(app):
|
||||
# type: (Sphinx) -> Dict[str, Any]
|
||||
app.connect('env-updated', create_nojekyll)
|
||||
app.connect('env-updated', create_nojekyll_and_cname)
|
||||
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
|
||||
|
@ -12,6 +12,7 @@ import posixpath
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from hashlib import sha1
|
||||
from os import path
|
||||
@ -26,7 +27,6 @@ from sphinx.util import logging
|
||||
from sphinx.util.math import get_node_equation_number, wrap_displaymath
|
||||
from sphinx.util.osutil import ensuredir
|
||||
from sphinx.util.png import read_png_depth, write_png_depth
|
||||
from sphinx.util.pycompat import sys_encoding
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
@ -46,9 +46,9 @@ class MathExtError(SphinxError):
|
||||
def __init__(self, msg, stderr=None, stdout=None):
|
||||
# type: (str, bytes, bytes) -> None
|
||||
if stderr:
|
||||
msg += '\n[stderr]\n' + stderr.decode(sys_encoding, 'replace')
|
||||
msg += '\n[stderr]\n' + stderr.decode(sys.getdefaultencoding(), 'replace')
|
||||
if stdout:
|
||||
msg += '\n[stdout]\n' + stdout.decode(sys_encoding, 'replace')
|
||||
msg += '\n[stdout]\n' + stdout.decode(sys.getdefaultencoding(), 'replace')
|
||||
super().__init__(msg)
|
||||
|
||||
|
||||
|
@ -21,7 +21,7 @@ from typing import Any, Union # NOQA
|
||||
from sphinx.deprecation import RemovedInSphinx30Warning
|
||||
from sphinx.transforms import (
|
||||
ApplySourceWorkaround, ExtraTranslatableNodes, CitationReferences,
|
||||
DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks, SortIds,
|
||||
DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks, SortIds, FigureAligner,
|
||||
AutoNumbering, AutoIndexUpgrader, FilterSystemMessages,
|
||||
UnreferencedFootnotesDetector, SphinxSmartQuotes, DoctreeReadEvent, ManpageLink
|
||||
)
|
||||
@ -96,7 +96,7 @@ class SphinxStandaloneReader(SphinxBaseReader):
|
||||
"""
|
||||
transforms = [ApplySourceWorkaround, ExtraTranslatableNodes, PreserveTranslatableMessages,
|
||||
Locale, CitationReferences, DefaultSubstitutions, MoveModuleTargets,
|
||||
HandleCodeBlocks, AutoNumbering, AutoIndexUpgrader, SortIds,
|
||||
HandleCodeBlocks, AutoNumbering, AutoIndexUpgrader, SortIds, FigureAligner,
|
||||
RemoveTranslatableInline, FilterSystemMessages, RefOnlyBulletListTransform,
|
||||
UnreferencedFootnotesDetector, SphinxSmartQuotes, ManpageLink,
|
||||
SphinxDomains, SubstitutionDefinitionsRemover, DoctreeReadEvent,
|
||||
|
221
sphinx/roles.py
221
sphinx/roles.py
@ -9,13 +9,16 @@
|
||||
"""
|
||||
|
||||
import re
|
||||
import warnings
|
||||
|
||||
from docutils import nodes, utils
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.deprecation import RemovedInSphinx40Warning
|
||||
from sphinx.errors import SphinxError
|
||||
from sphinx.locale import _
|
||||
from sphinx.util import ws_re
|
||||
from sphinx.util.docutils import ReferenceRole, SphinxRole
|
||||
from sphinx.util.nodes import split_explicit_title, process_index_entry, \
|
||||
set_role_source_info
|
||||
|
||||
@ -179,6 +182,8 @@ class AnyXRefRole(XRefRole):
|
||||
def indexmarkup_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
# type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA
|
||||
"""Role for PEP/RFC references that generate an index entry."""
|
||||
warnings.warn('indexmarkup_role() is deprecated. Please use PEP or RFC class instead.',
|
||||
RemovedInSphinx40Warning, stacklevel=2)
|
||||
env = inliner.document.settings.env
|
||||
if not typ:
|
||||
assert env.temp_data['default_role']
|
||||
@ -242,11 +247,87 @@ def indexmarkup_role(typ, rawtext, text, lineno, inliner, options={}, content=[]
|
||||
raise ValueError('unknown role type: %s' % typ)
|
||||
|
||||
|
||||
class PEP(ReferenceRole):
|
||||
def run(self):
|
||||
# type: () -> Tuple[List[nodes.Node], List[nodes.system_message]]
|
||||
target_id = 'index-%s' % self.env.new_serialno('index')
|
||||
entries = [('single', _('Python Enhancement Proposals; PEP %s') % self.target,
|
||||
target_id, '', None)]
|
||||
|
||||
index = addnodes.index(entries=entries)
|
||||
target = nodes.target('', '', ids=[target_id])
|
||||
self.inliner.document.note_explicit_target(target)
|
||||
|
||||
try:
|
||||
refuri = self.build_uri()
|
||||
reference = nodes.reference('', '', internal=False, refuri=refuri, classes=['pep'])
|
||||
if self.has_explicit_title:
|
||||
reference += nodes.strong(self.title, self.title)
|
||||
else:
|
||||
title = "PEP " + self.title
|
||||
reference += nodes.strong(title, title)
|
||||
except ValueError:
|
||||
msg = self.inliner.reporter.error('invalid PEP number %s' % self.target,
|
||||
line=self.lineno)
|
||||
prb = self.inliner.problematic(self.rawtext, self.rawtext, msg)
|
||||
return [prb], [msg]
|
||||
|
||||
return [index, target, reference], []
|
||||
|
||||
def build_uri(self):
|
||||
# type: () -> str
|
||||
base_url = self.inliner.document.settings.pep_base_url
|
||||
ret = self.target.split('#', 1)
|
||||
if len(ret) == 2:
|
||||
return base_url + 'pep-%04d#%s' % (int(ret[0]), ret[1])
|
||||
else:
|
||||
return base_url + 'pep-%04d' % int(ret[0])
|
||||
|
||||
|
||||
class RFC(ReferenceRole):
|
||||
def run(self):
|
||||
# type: () -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA
|
||||
target_id = 'index-%s' % self.env.new_serialno('index')
|
||||
entries = [('single', 'RFC; RFC %s' % self.target, target_id, '', None)]
|
||||
|
||||
index = addnodes.index(entries=entries)
|
||||
target = nodes.target('', '', ids=[target_id])
|
||||
self.inliner.document.note_explicit_target(target)
|
||||
|
||||
try:
|
||||
refuri = self.build_uri()
|
||||
reference = nodes.reference('', '', internal=False, refuri=refuri, classes=['rfc'])
|
||||
if self.has_explicit_title:
|
||||
reference += nodes.strong(self.title, self.title)
|
||||
else:
|
||||
title = "RFC " + self.title
|
||||
reference += nodes.strong(title, title)
|
||||
except ValueError:
|
||||
msg = self.inliner.reporter.error('invalid RFC number %s' % self.target,
|
||||
line=self.lineno)
|
||||
prb = self.inliner.problematic(self.rawtext, self.rawtext, msg)
|
||||
return [prb], [msg]
|
||||
|
||||
return [index, target, reference], []
|
||||
|
||||
def build_uri(self):
|
||||
# type: () -> str
|
||||
base_url = self.inliner.document.settings.rfc_base_url
|
||||
ret = self.target.split('#', 1)
|
||||
if len(ret) == 2:
|
||||
return base_url + self.inliner.rfc_url % int(ret[0]) + '#' + ret[1]
|
||||
else:
|
||||
return base_url + self.inliner.rfc_url % int(ret[0])
|
||||
|
||||
|
||||
_amp_re = re.compile(r'(?<!&)&(?![&\s])')
|
||||
|
||||
|
||||
def menusel_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
# type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA
|
||||
warnings.warn('menusel_role() is deprecated. '
|
||||
'Please use MenuSelection or GUILabel class instead.',
|
||||
RemovedInSphinx40Warning, stacklevel=2)
|
||||
env = inliner.document.settings.env
|
||||
if not typ:
|
||||
assert env.temp_data['default_role']
|
||||
@ -279,6 +360,32 @@ def menusel_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
return [node], []
|
||||
|
||||
|
||||
class GUILabel(SphinxRole):
|
||||
amp_re = re.compile(r'(?<!&)&(?![&\s])')
|
||||
|
||||
def run(self):
|
||||
# type: () -> Tuple[List[nodes.Node], List[nodes.system_message]]
|
||||
node = nodes.inline(rawtext=self.rawtext, classes=[self.name])
|
||||
spans = self.amp_re.split(self.text)
|
||||
node += nodes.Text(spans.pop(0))
|
||||
for span in spans:
|
||||
span = span.replace('&&', '&')
|
||||
|
||||
letter = nodes.Text(span[0])
|
||||
accelerator = nodes.inline('', '', letter, classes=['accelerator'])
|
||||
node += accelerator
|
||||
node += nodes.Text(span[1:])
|
||||
|
||||
return [node], []
|
||||
|
||||
|
||||
class MenuSelection(GUILabel):
|
||||
def run(self):
|
||||
# type: () -> Tuple[List[nodes.Node], List[nodes.system_message]]
|
||||
self.text = self.text.replace('-->', '\N{TRIANGULAR BULLET}') # type: ignore
|
||||
return super().run()
|
||||
|
||||
|
||||
_litvar_re = re.compile('{([^}]+)}')
|
||||
parens_re = re.compile(r'(\\*{|\\*})')
|
||||
|
||||
@ -286,6 +393,9 @@ parens_re = re.compile(r'(\\*{|\\*})')
|
||||
def emph_literal_role(typ, rawtext, text, lineno, inliner,
|
||||
options={}, content=[]):
|
||||
# type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA
|
||||
warnings.warn('emph_literal_role() is deprecated. '
|
||||
'Please use EmphasizedLiteral class instead.',
|
||||
RemovedInSphinx40Warning, stacklevel=2)
|
||||
env = inliner.document.settings.env
|
||||
if not typ:
|
||||
assert env.temp_data['default_role']
|
||||
@ -333,11 +443,65 @@ def emph_literal_role(typ, rawtext, text, lineno, inliner,
|
||||
return [retnode], []
|
||||
|
||||
|
||||
class EmphasizedLiteral(SphinxRole):
|
||||
parens_re = re.compile(r'(\\\\|\\{|\\}|{|})')
|
||||
|
||||
def run(self):
|
||||
# type: () -> Tuple[List[nodes.Node], List[nodes.system_message]]
|
||||
children = self.parse(self.text)
|
||||
node = nodes.literal(self.rawtext, '', *children,
|
||||
role=self.name.lower(), classes=[self.name])
|
||||
|
||||
return [node], []
|
||||
|
||||
def parse(self, text):
|
||||
# type: (str) -> List[nodes.Node]
|
||||
result = [] # type: List[nodes.Node]
|
||||
|
||||
stack = ['']
|
||||
for part in self.parens_re.split(text):
|
||||
if part == '\\\\': # escaped backslash
|
||||
stack[-1] += '\\'
|
||||
elif part == '{':
|
||||
if len(stack) >= 2 and stack[-2] == "{": # nested
|
||||
stack[-1] += "{"
|
||||
else:
|
||||
# start emphasis
|
||||
stack.append('{')
|
||||
stack.append('')
|
||||
elif part == '}':
|
||||
if len(stack) == 3 and stack[1] == "{" and len(stack[2]) > 0:
|
||||
# emphasized word found
|
||||
if stack[0]:
|
||||
result.append(nodes.Text(stack[0], stack[0]))
|
||||
result.append(nodes.emphasis(stack[2], stack[2]))
|
||||
stack = ['']
|
||||
else:
|
||||
# emphasized word not found; the rparen is not a special symbol
|
||||
stack.append('}')
|
||||
stack = [''.join(stack)]
|
||||
elif part == '\\{': # escaped left-brace
|
||||
stack[-1] += '{'
|
||||
elif part == '\\}': # escaped right-brace
|
||||
stack[-1] += '}'
|
||||
else: # others (containing escaped braces)
|
||||
stack[-1] += part
|
||||
|
||||
if ''.join(stack):
|
||||
# remaining is treated as Text
|
||||
text = ''.join(stack)
|
||||
result.append(nodes.Text(text, text))
|
||||
|
||||
return result
|
||||
|
||||
|
||||
_abbr_re = re.compile(r'\((.*)\)$', re.S)
|
||||
|
||||
|
||||
def abbr_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
# type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA
|
||||
warnings.warn('abbr_role() is deprecated. Please use Abbrevation class instead.',
|
||||
RemovedInSphinx40Warning, stacklevel=2)
|
||||
text = utils.unescape(text)
|
||||
m = _abbr_re.search(text)
|
||||
if m is None:
|
||||
@ -349,8 +513,25 @@ def abbr_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
return [nodes.abbreviation(abbr, abbr, **options)], []
|
||||
|
||||
|
||||
class Abbreviation(SphinxRole):
|
||||
abbr_re = re.compile(r'\((.*)\)$', re.S)
|
||||
|
||||
def run(self):
|
||||
# type: () -> Tuple[List[nodes.Node], List[nodes.system_message]]
|
||||
matched = self.abbr_re.search(self.text)
|
||||
if matched:
|
||||
text = self.text[:matched.start()].strip()
|
||||
self.options['explanation'] = matched.group(1)
|
||||
else:
|
||||
text = self.text
|
||||
|
||||
return [nodes.abbreviation(self.rawtext, text, **self.options)], []
|
||||
|
||||
|
||||
def index_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
# type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA
|
||||
warnings.warn('index_role() is deprecated. Please use Index class instead.',
|
||||
RemovedInSphinx40Warning, stacklevel=2)
|
||||
# create new reference target
|
||||
env = inliner.document.settings.env
|
||||
targetid = 'index-%s' % env.new_serialno('index')
|
||||
@ -378,20 +559,44 @@ def index_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
return [indexnode, targetnode, textnode], []
|
||||
|
||||
|
||||
class Index(ReferenceRole):
|
||||
def run(self):
|
||||
# type: () -> Tuple[List[nodes.Node], List[nodes.system_message]]
|
||||
target_id = 'index-%s' % self.env.new_serialno('index')
|
||||
if self.has_explicit_title:
|
||||
# if an explicit target is given, process it as a full entry
|
||||
title = self.title
|
||||
entries = process_index_entry(self.target, target_id)
|
||||
else:
|
||||
# otherwise we just create a single entry
|
||||
if self.target.startswith('!'):
|
||||
title = self.title[1:]
|
||||
entries = [('single', self.target[1:], target_id, 'main', None)]
|
||||
else:
|
||||
title = self.title
|
||||
entries = [('single', self.target, target_id, '', None)]
|
||||
|
||||
index = addnodes.index(entries=entries)
|
||||
target = nodes.target('', '', ids=[target_id])
|
||||
text = nodes.Text(title, title)
|
||||
self.set_source_info(index)
|
||||
return [index, target, text], []
|
||||
|
||||
|
||||
specific_docroles = {
|
||||
# links to download references
|
||||
'download': XRefRole(nodeclass=addnodes.download_reference),
|
||||
# links to anything
|
||||
'any': AnyXRefRole(warn_dangling=True),
|
||||
|
||||
'pep': indexmarkup_role,
|
||||
'rfc': indexmarkup_role,
|
||||
'guilabel': menusel_role,
|
||||
'menuselection': menusel_role,
|
||||
'file': emph_literal_role,
|
||||
'samp': emph_literal_role,
|
||||
'abbr': abbr_role,
|
||||
'index': index_role,
|
||||
'pep': PEP(),
|
||||
'rfc': RFC(),
|
||||
'guilabel': GUILabel(),
|
||||
'menuselection': MenuSelection(),
|
||||
'file': EmphasizedLiteral(),
|
||||
'samp': EmphasizedLiteral(),
|
||||
'abbr': Abbreviation(),
|
||||
'index': Index(),
|
||||
} # type: Dict[str, RoleFunction]
|
||||
|
||||
|
||||
|
@ -18,8 +18,8 @@ from docutils import nodes
|
||||
from sphinx import addnodes
|
||||
from sphinx import package_dir
|
||||
from sphinx.deprecation import RemovedInSphinx40Warning
|
||||
from sphinx.util import jsdump, rpartition
|
||||
from sphinx.search.jssplitter import splitter_code
|
||||
from sphinx.util import jsdump, rpartition
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
|
31
sphinx/templates/htmlhelp/project.hhc
Normal file
31
sphinx/templates/htmlhelp/project.hhc
Normal file
@ -0,0 +1,31 @@
|
||||
{%- macro sitemap(name, docname) -%}
|
||||
<OBJECT type="text/sitemap">
|
||||
<PARAM name="Name" value="{{ name|e }}" />
|
||||
<PARAM name="Local" value="{{ docname|e }}{{ suffix }}" />
|
||||
</OBJECT>
|
||||
{%- endmacro -%}
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<META name="GENERATOR" content="Microsoft® HTML Help Workshop 4.1" />
|
||||
<!-- Sitemap 1.0 -->
|
||||
</HEAD>
|
||||
<BODY>
|
||||
<OBJECT type="text/site properties">
|
||||
<PARAM name="Window Styles" value="0x801227" />
|
||||
<PARAM name="ImageType" value="Folder" />
|
||||
</OBJECT>
|
||||
<UL>
|
||||
<LI>
|
||||
{{ sitemap(short_title, master_doc)|indent(8) }}
|
||||
</LI>
|
||||
{%- for indexname, indexcls, content, collapse in domain_indices %}
|
||||
<LI>
|
||||
{{ sitemap(indexcls.localname, indexname)|indent(8) }}
|
||||
</LI>
|
||||
{%- endfor %}
|
||||
{{ body|indent(6) }}
|
||||
</UL>
|
||||
</BODY>
|
||||
</HTML>
|
21
sphinx/templates/htmlhelp/project.hhp
Normal file
21
sphinx/templates/htmlhelp/project.hhp
Normal file
@ -0,0 +1,21 @@
|
||||
[OPTIONS]
|
||||
Binary TOC=No
|
||||
Binary Index=No
|
||||
Compiled file={{ outname }}.chm
|
||||
Contents file={{ outname }}.hhc
|
||||
Default Window={{ outname }}
|
||||
Default topic={{ master_doc }}
|
||||
Display compile progress=No
|
||||
Full text search stop list file={{ outname }}.stp
|
||||
Full-text search=Yes
|
||||
Index file={{ outname }}.hhk
|
||||
Language={{ "%#x"|format(lcid) }}
|
||||
Title={{ title }}
|
||||
|
||||
[WINDOWS]
|
||||
{{ outname }}="{{ title }}","{{ outname }}.hhc","{{ outname }}.hhk","{{ master_doc }}","{{ master_doc }}",,,,,0x63520,220,0x10384e,[0,0,1024,768],,,,,,,0
|
||||
|
||||
[FILES]
|
||||
{%- for filename in files %}
|
||||
{{ filename }}
|
||||
{%- endfor %}
|
33
sphinx/templates/htmlhelp/project.stp
Normal file
33
sphinx/templates/htmlhelp/project.stp
Normal file
@ -0,0 +1,33 @@
|
||||
a
|
||||
and
|
||||
are
|
||||
as
|
||||
at
|
||||
be
|
||||
but
|
||||
by
|
||||
for
|
||||
if
|
||||
in
|
||||
into
|
||||
is
|
||||
it
|
||||
near
|
||||
no
|
||||
not
|
||||
of
|
||||
on
|
||||
or
|
||||
such
|
||||
that
|
||||
the
|
||||
their
|
||||
then
|
||||
there
|
||||
these
|
||||
they
|
||||
this
|
||||
to
|
||||
was
|
||||
will
|
||||
with
|
@ -231,6 +231,16 @@ a.headerlink {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
a.brackets:before,
|
||||
span.brackets > a:before{
|
||||
content: "[";
|
||||
}
|
||||
|
||||
a.brackets:after,
|
||||
span.brackets > a:after {
|
||||
content: "]";
|
||||
}
|
||||
|
||||
h1:hover > a.headerlink,
|
||||
h2:hover > a.headerlink,
|
||||
h3:hover > a.headerlink,
|
||||
@ -391,6 +401,14 @@ table.citation td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
td > p:first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
td > p:only-child {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
/* -- figures --------------------------------------------------------------- */
|
||||
|
||||
div.figure {
|
||||
@ -460,11 +478,57 @@ ol.upperroman {
|
||||
list-style: upper-roman;
|
||||
}
|
||||
|
||||
li > p:first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
li > p:only-child {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
dl.footnote > dt,
|
||||
dl.citation > dt {
|
||||
float: left;
|
||||
}
|
||||
|
||||
dl.footnote > dd,
|
||||
dl.citation > dd {
|
||||
margin-bottom: 0em;
|
||||
}
|
||||
|
||||
dl.footnote > dd:after,
|
||||
dl.citation > dd:after {
|
||||
content: "";
|
||||
clear: both;
|
||||
}
|
||||
|
||||
dl.field-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
dl.field-list > dt {
|
||||
flex-basis: 20%;
|
||||
font-weight: bold;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
dl.field-list > dt:after {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
dl.field-list > dd {
|
||||
flex-basis: 70%;
|
||||
padding-left: 1em;
|
||||
margin-left: 0em;
|
||||
margin-bottom: 0em;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
dd p {
|
||||
dd > p:first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ from sphinx.locale import _, __
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.docutils import new_document
|
||||
from sphinx.util.i18n import format_date
|
||||
from sphinx.util.nodes import apply_source_workaround, is_smartquotable
|
||||
from sphinx.util.nodes import NodeMatcher, apply_source_workaround, is_smartquotable
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
@ -309,6 +309,19 @@ class UnreferencedFootnotesDetector(SphinxTransform):
|
||||
location=node)
|
||||
|
||||
|
||||
class FigureAligner(SphinxTransform):
|
||||
"""
|
||||
Align figures to center by default.
|
||||
"""
|
||||
default_priority = 700
|
||||
|
||||
def apply(self, **kwargs):
|
||||
# type: (Any) -> None
|
||||
matcher = NodeMatcher(nodes.table, nodes.figure)
|
||||
for node in self.document.traverse(matcher): # type: nodes.Element
|
||||
node.setdefault('align', 'center')
|
||||
|
||||
|
||||
class FilterSystemMessages(SphinxTransform):
|
||||
"""Filter system messages from a doctree."""
|
||||
default_priority = 999
|
||||
|
@ -27,6 +27,12 @@ _ansi_re = re.compile('\x1b\\[(\\d\\d;){0,2}\\d\\dm')
|
||||
codes = {} # type: Dict[str, str]
|
||||
|
||||
|
||||
def terminal_safe(s):
|
||||
# type: (str) -> str
|
||||
"""safely encode a string for printing to the terminal."""
|
||||
return s.encode('ascii', 'backslashreplace').decode('ascii')
|
||||
|
||||
|
||||
def get_terminal_width():
|
||||
# type: () -> int
|
||||
"""Borrowed from the py lib."""
|
||||
|
@ -23,10 +23,10 @@ from docutils import nodes
|
||||
from docutils.io import FileOutput
|
||||
from docutils.parsers.rst import Directive, directives, roles, convert_directive_function
|
||||
from docutils.statemachine import StateMachine
|
||||
from docutils.utils import Reporter
|
||||
from docutils.utils import Reporter, unescape
|
||||
|
||||
from sphinx.deprecation import RemovedInSphinx30Warning
|
||||
from sphinx.errors import ExtensionError
|
||||
from sphinx.errors import ExtensionError, SphinxError
|
||||
from sphinx.locale import __
|
||||
from sphinx.util import logging
|
||||
|
||||
@ -36,7 +36,8 @@ report_re = re.compile('^(.+?:(?:\\d+)?): \\((DEBUG|INFO|WARNING|ERROR|SEVERE)/(
|
||||
if False:
|
||||
# For type annotation
|
||||
from types import ModuleType # NOQA
|
||||
from typing import Any, Callable, Generator, List, Set, Tuple, Type # NOQA
|
||||
from typing import Any, Callable, Dict, Generator, List, Set, Tuple, Type # NOQA
|
||||
from docutils.parsers.rst.states import Inliner # NOQA
|
||||
from docutils.statemachine import State, StringList # NOQA
|
||||
from sphinx.builders import Builder # NOQA
|
||||
from sphinx.config import Config # NOQA
|
||||
@ -383,6 +384,99 @@ class SphinxDirective(Directive):
|
||||
return self.env.config
|
||||
|
||||
|
||||
class SphinxRole:
|
||||
"""A base class for Sphinx roles.
|
||||
|
||||
This class provides helper methods for Sphinx roles.
|
||||
|
||||
.. note:: The subclasses of this class might not work with docutils.
|
||||
This class is strongly coupled with Sphinx.
|
||||
"""
|
||||
name = None #: The role name actually used in the document.
|
||||
rawtext = None #: A string containing the entire interpreted text input.
|
||||
text = None #: The interpreted text content.
|
||||
lineno = None #: The line number where the interpreted text begins.
|
||||
inliner = None #: The ``docutils.parsers.rst.states.Inliner`` object.
|
||||
options = None #: A dictionary of directive options for customization
|
||||
#: (from the "role" directive).
|
||||
content = None #: A list of strings, the directive content for customization
|
||||
#: (from the "role" directive).
|
||||
|
||||
def __call__(self, name, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
# type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA
|
||||
self.rawtext = rawtext
|
||||
self.text = unescape(text)
|
||||
self.lineno = lineno
|
||||
self.inliner = inliner
|
||||
self.options = options
|
||||
self.content = content
|
||||
|
||||
# guess role type
|
||||
if name:
|
||||
self.name = name.lower()
|
||||
else:
|
||||
self.name = self.env.temp_data.get('default_role')
|
||||
if not self.name:
|
||||
self.name = self.env.config.default_role
|
||||
if not self.name:
|
||||
raise SphinxError('cannot determine default role!')
|
||||
|
||||
return self.run()
|
||||
|
||||
def run(self):
|
||||
# type: () -> Tuple[List[nodes.Node], List[nodes.system_message]]
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def env(self):
|
||||
# type: () -> BuildEnvironment
|
||||
"""Reference to the :class:`.BuildEnvironment` object."""
|
||||
return self.inliner.document.settings.env
|
||||
|
||||
@property
|
||||
def config(self):
|
||||
# type: () -> Config
|
||||
"""Reference to the :class:`.Config` object."""
|
||||
return self.env.config
|
||||
|
||||
def set_source_info(self, node, lineno=None):
|
||||
# type: (nodes.Node, int) -> None
|
||||
if lineno is None:
|
||||
lineno = self.lineno
|
||||
|
||||
source_info = self.inliner.reporter.get_source_and_line(lineno) # type: ignore
|
||||
node.source, node.line = source_info
|
||||
|
||||
|
||||
class ReferenceRole(SphinxRole):
|
||||
"""A base class for reference roles.
|
||||
|
||||
The reference roles can accpet ``link title <target>`` style as a text for
|
||||
the role. The parsed result; link title and target will be stored to
|
||||
``self.title`` and ``self.target``.
|
||||
"""
|
||||
has_explicit_title = None #: A boolean indicates the role has explicit title or not.
|
||||
title = None #: The link title for the interpreted text.
|
||||
target = None #: The link target for the interpreted text.
|
||||
|
||||
# \x00 means the "<" was backslash-escaped
|
||||
explicit_title_re = re.compile(r'^(.+?)\s*(?<!\x00)<(.*?)>$', re.DOTALL)
|
||||
|
||||
def __call__(self, name, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
# type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA
|
||||
matched = self.explicit_title_re.match(text)
|
||||
if matched:
|
||||
self.has_explicit_title = True
|
||||
self.title = unescape(matched.group(1))
|
||||
self.target = unescape(matched.group(2))
|
||||
else:
|
||||
self.has_explicit_title = False
|
||||
self.title = unescape(text)
|
||||
self.target = unescape(text)
|
||||
|
||||
return super().__call__(name, rawtext, text, lineno, inliner, options, content)
|
||||
|
||||
|
||||
class SphinxTranslator(nodes.NodeVisitor):
|
||||
"""A base class for Sphinx translators.
|
||||
|
||||
|
@ -20,7 +20,7 @@ from io import StringIO
|
||||
|
||||
from sphinx.deprecation import RemovedInSphinx30Warning
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.pycompat import NoneType
|
||||
from sphinx.util.typing import NoneType
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
|
@ -285,6 +285,14 @@ def find_source_node(node):
|
||||
return None
|
||||
|
||||
|
||||
def get_node_line(node):
|
||||
# type: (nodes.Element) -> int
|
||||
for pnode in traverse_parent(node):
|
||||
if pnode.line:
|
||||
return pnode.line
|
||||
return None
|
||||
|
||||
|
||||
def traverse_parent(node, cls=None):
|
||||
# type: (nodes.Element, Any) -> Iterable[nodes.Element]
|
||||
while node:
|
||||
|
@ -17,6 +17,8 @@ import warnings
|
||||
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
|
||||
from sphinx.locale import __
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.console import terminal_safe
|
||||
from sphinx.util.typing import NoneType
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
@ -26,22 +28,9 @@ if False:
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
NoneType = type(None)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Python 2/3 compatibility
|
||||
|
||||
# sys_encoding: some kind of default system encoding; should be used with
|
||||
# a lenient error handler
|
||||
sys_encoding = sys.getdefaultencoding()
|
||||
|
||||
|
||||
# terminal_safe(): safely encode a string for printing to the terminal
|
||||
def terminal_safe(s):
|
||||
# type: (str) -> str
|
||||
return s.encode('ascii', 'backslashreplace').decode('ascii')
|
||||
|
||||
|
||||
# convert_with_2to3():
|
||||
# support for running 2to3 over config files
|
||||
def convert_with_2to3(filepath):
|
||||
@ -99,9 +88,12 @@ def execfile_(filepath, _globals, open=open):
|
||||
|
||||
deprecated_alias('sphinx.util.pycompat',
|
||||
{
|
||||
'NoneType': NoneType, # type: ignore
|
||||
'TextIOWrapper': io.TextIOWrapper,
|
||||
'htmlescape': html.escape,
|
||||
'indent': textwrap.indent,
|
||||
'terminal_safe': terminal_safe,
|
||||
'sys_encoding': sys.getdefaultencoding(),
|
||||
'u': '',
|
||||
},
|
||||
RemovedInSphinx40Warning)
|
||||
|
@ -20,6 +20,9 @@ DirectiveOption = Callable[[str], Any]
|
||||
# Text like nodes which are initialized with text and rawsource
|
||||
TextlikeNode = Union[nodes.Text, nodes.TextElement]
|
||||
|
||||
# type of None
|
||||
NoneType = type(None)
|
||||
|
||||
# common role functions
|
||||
RoleFunction = Callable[[str, str, str, int, Inliner, Dict, List[str]],
|
||||
Tuple[List[nodes.Node], List[nodes.system_message]]]
|
||||
|
@ -247,7 +247,7 @@ class TexinfoTranslator(SphinxTranslator):
|
||||
title = self.settings.title # type: str
|
||||
if not title:
|
||||
title_node = self.document.next_node(nodes.title)
|
||||
title = (title and title_node.astext()) or '<untitled>'
|
||||
title = (title_node and title_node.astext()) or '<untitled>'
|
||||
elements['title'] = self.escape_id(title) or '<untitled>'
|
||||
# filename
|
||||
if not elements['filename']:
|
||||
|
@ -1 +0,0 @@
|
||||
project = 'test'
|
@ -1,19 +0,0 @@
|
||||
Index markup
|
||||
------------
|
||||
|
||||
.. index::
|
||||
single: entry
|
||||
pair: entry; pair
|
||||
double: entry; double
|
||||
triple: index; entry; triple
|
||||
keyword: with
|
||||
see: from; to
|
||||
seealso: fromalso; toalso
|
||||
|
||||
.. index::
|
||||
!Main, !Other
|
||||
!single: entry; pair
|
||||
|
||||
.. index:: triple-quoted string, Unicode Consortium, raw string
|
||||
single: """; string literal
|
||||
single: '''; string literal
|
@ -1,64 +0,0 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
set this=%~n0
|
||||
|
||||
if not defined PYTHON set PYTHON=py
|
||||
|
||||
if not defined SPHINXBUILD (
|
||||
%PYTHON% -c "import sphinx" > nul 2> nul
|
||||
if errorlevel 1 (
|
||||
echo Installing sphinx with %PYTHON%
|
||||
%PYTHON% -m pip install sphinx
|
||||
if errorlevel 1 exit /B
|
||||
)
|
||||
set SPHINXBUILD=%PYTHON% -c "import sphinx.cmd.build, sys; sys.exit(sphinx.cmd.build.main())"
|
||||
)
|
||||
|
||||
rem Search for HHC in likely places
|
||||
set HTMLHELP=
|
||||
where hhc /q && set HTMLHELP=hhc && goto :skiphhcsearch
|
||||
where /R ..\externals hhc > "%TEMP%\hhc.loc" 2> nul && set /P HTMLHELP= < "%TEMP%\hhc.loc" & del "%TEMP%\hhc.loc"
|
||||
if not exist "%HTMLHELP%" where /R "%ProgramFiles(x86)%" hhc > "%TEMP%\hhc.loc" 2> nul && set /P HTMLHELP= < "%TEMP%\hhc.loc" & del "%TEMP%\hhc.loc"
|
||||
if not exist "%HTMLHELP%" where /R "%ProgramFiles%" hhc > "%TEMP%\hhc.loc" 2> nul && set /P HTMLHELP= < "%TEMP%\hhc.loc" & del "%TEMP%\hhc.loc"
|
||||
if not exist "%HTMLHELP%" (
|
||||
echo.
|
||||
echo.The HTML Help Workshop was not found. Set the HTMLHELP variable
|
||||
echo.to the path to hhc.exe or download and install it from
|
||||
echo.http://msdn.microsoft.com/en-us/library/ms669985
|
||||
exit /B 1
|
||||
)
|
||||
echo hhc.exe path: %HTMLHELP%
|
||||
|
||||
if "%BUILDDIR%" EQU "" set BUILDDIR=build
|
||||
|
||||
%SPHINXBUILD% >nul 2> nul
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
popd
|
||||
exit /B 1
|
||||
)
|
||||
|
||||
set SPHINXOPTS=-D html_theme_options.body_max_width=none %SPHINXOPTS%
|
||||
|
||||
cmd /S /C "%SPHINXBUILD% %SPHINXOPTS% -bhtmlhelp -dbuild\doctrees . "%BUILDDIR%\htmlhelp"
|
||||
|
||||
"%HTMLHELP%" "%BUILDDIR%\htmlhelp\test.hhp"
|
||||
rem hhc.exe seems to always exit with code 1, reset to 0 for less than 2
|
||||
if not errorlevel 2 cmd /C exit /b 0
|
||||
|
||||
echo.
|
||||
if errorlevel 1 (
|
||||
echo.Build failed (exit code %ERRORLEVEL%^), check for error messages
|
||||
echo.above. Any output will be found in %BUILDDIR%\%1
|
||||
) else (
|
||||
echo.Build succeeded. All output should be in %BUILDDIR%\%1
|
||||
)
|
||||
|
||||
popd
|
@ -1,2 +1 @@
|
||||
html_theme = 'classic'
|
||||
exclude_patterns = ['_build']
|
||||
|
@ -1,2 +1 @@
|
||||
html_theme = 'classic'
|
||||
exclude_patterns = ['_build']
|
||||
|
@ -1,2 +1 @@
|
||||
html_theme = 'classic'
|
||||
exclude_patterns = ['_build']
|
||||
|
@ -15,6 +15,11 @@ def decoratedFunction():
|
||||
return None
|
||||
|
||||
|
||||
def func(arg: missing_module.Class):
|
||||
"""a function takes mocked object as an argument"""
|
||||
pass
|
||||
|
||||
|
||||
class TestAutodoc(object):
|
||||
"""TestAutodoc docstring."""
|
||||
@missing_name
|
||||
|
@ -15,6 +15,12 @@ For Windows users
|
||||
For UNIX users
|
||||
--------------
|
||||
|
||||
Linux
|
||||
^^^^^
|
||||
|
||||
FreeBSD
|
||||
^^^^^^^
|
||||
|
||||
This one's got an apostrophe
|
||||
----------------------------
|
||||
|
||||
@ -26,4 +32,6 @@ References
|
||||
* :ref:`index:Installation`
|
||||
* :ref:`index:For Windows users`
|
||||
* :ref:`index:For UNIX users`
|
||||
* :ref:`index:Linux`
|
||||
* :ref:`index:FreeBSD`
|
||||
* :ref:`index:This one's got an apostrophe`
|
||||
|
@ -15,15 +15,23 @@ For Windows users
|
||||
For UNIX users
|
||||
--------------
|
||||
|
||||
Linux
|
||||
^^^^^
|
||||
|
||||
FreeBSD
|
||||
^^^^^^^
|
||||
|
||||
This one's got an apostrophe
|
||||
----------------------------
|
||||
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
* :ref:`test-ext-autosectionlabel`
|
||||
* :ref:`Introduce of Sphinx`
|
||||
* :ref:`Installation`
|
||||
* :ref:`For Windows users`
|
||||
* :ref:`For UNIX users`
|
||||
* :ref:`Linux`
|
||||
* :ref:`FreeBSD`
|
||||
* :ref:`This one's got an apostrophe`
|
||||
|
7
tests/roots/test-ext-autosummary-mock_imports/conf.py
Normal file
7
tests/roots/test-ext-autosummary-mock_imports/conf.py
Normal file
@ -0,0 +1,7 @@
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
extensions = ['sphinx.ext.autosummary']
|
||||
autosummary_generate = True
|
||||
autosummary_mock_imports = ['unknown']
|
6
tests/roots/test-ext-autosummary-mock_imports/foo.py
Normal file
6
tests/roots/test-ext-autosummary-mock_imports/foo.py
Normal file
@ -0,0 +1,6 @@
|
||||
import unknown
|
||||
|
||||
|
||||
class Foo(unknown.Class):
|
||||
"""Foo class"""
|
||||
pass
|
7
tests/roots/test-ext-autosummary-mock_imports/index.rst
Normal file
7
tests/roots/test-ext-autosummary-mock_imports/index.rst
Normal file
@ -0,0 +1,7 @@
|
||||
test-ext-autosummary-mock_imports
|
||||
=================================
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
|
||||
foo
|
@ -1,2 +1 @@
|
||||
html_theme = 'classic'
|
||||
exclude_patterns = ['_build']
|
||||
|
@ -7,7 +7,7 @@ Empty cell
|
||||
----------
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
|
||||
- * un
|
||||
*
|
||||
* trois
|
||||
|
@ -1,6 +1,6 @@
|
||||
\label{\detokenize{longtable:longtable}}
|
||||
|
||||
\begin{savenotes}\sphinxatlongtablestart\begin{longtable}{|l|l|}
|
||||
\begin{savenotes}\sphinxatlongtablestart\begin{longtable}[c]{|l|l|}
|
||||
\hline
|
||||
\sphinxstyletheadfamily
|
||||
header1
|
||||
|
@ -1,6 +1,6 @@
|
||||
\label{\detokenize{longtable:longtable-having-caption}}
|
||||
|
||||
\begin{savenotes}\sphinxatlongtablestart\begin{longtable}{|l|l|}
|
||||
\begin{savenotes}\sphinxatlongtablestart\begin{longtable}[c]{|l|l|}
|
||||
\sphinxthelongtablecaptionisattop
|
||||
\caption{caption for longtable\strut}\label{\detokenize{longtable:id1}}\\*[\sphinxlongtablecapskipadjust]
|
||||
\hline
|
||||
|
@ -1,6 +1,6 @@
|
||||
\label{\detokenize{longtable:longtable-having-problematic-cell}}
|
||||
|
||||
\begin{savenotes}\sphinxatlongtablestart\begin{longtable}{|*{2}{\X{1}{2}|}}
|
||||
\begin{savenotes}\sphinxatlongtablestart\begin{longtable}[c]{|*{2}{\X{1}{2}|}}
|
||||
\hline
|
||||
\sphinxstyletheadfamily
|
||||
header1
|
||||
|
@ -1,6 +1,6 @@
|
||||
\label{\detokenize{longtable:longtable-having-both-stub-columns-and-problematic-cell}}
|
||||
|
||||
\begin{savenotes}\sphinxatlongtablestart\begin{longtable}{|*{3}{\X{1}{3}|}}
|
||||
\begin{savenotes}\sphinxatlongtablestart\begin{longtable}[c]{|*{3}{\X{1}{3}|}}
|
||||
\hline
|
||||
\sphinxstyletheadfamily
|
||||
header1
|
||||
|
@ -1,6 +1,6 @@
|
||||
\label{\detokenize{longtable:longtable-having-verbatim}}
|
||||
|
||||
\begin{savenotes}\sphinxatlongtablestart\begin{longtable}{|*{2}{\X{1}{2}|}}
|
||||
\begin{savenotes}\sphinxatlongtablestart\begin{longtable}[c]{|*{2}{\X{1}{2}|}}
|
||||
\hline
|
||||
\sphinxstyletheadfamily
|
||||
header1
|
||||
|
@ -1,6 +1,6 @@
|
||||
\label{\detokenize{longtable:longtable-having-widths-option}}
|
||||
|
||||
\begin{savenotes}\sphinxatlongtablestart\begin{longtable}{|\X{30}{100}|\X{70}{100}|}
|
||||
\begin{savenotes}\sphinxatlongtablestart\begin{longtable}[c]{|\X{30}{100}|\X{70}{100}|}
|
||||
\hline\noalign{\phantomsection\label{\detokenize{longtable:namedlongtable}}\label{\detokenize{longtable:mylongtable}}}%
|
||||
\sphinxstyletheadfamily
|
||||
header1
|
||||
|
@ -1,6 +1,6 @@
|
||||
\label{\detokenize{longtable:longtable-having-both-widths-and-problematic-cell}}
|
||||
|
||||
\begin{savenotes}\sphinxatlongtablestart\begin{longtable}{|\X{30}{100}|\X{70}{100}|}
|
||||
\begin{savenotes}\sphinxatlongtablestart\begin{longtable}[c]{|\X{30}{100}|\X{70}{100}|}
|
||||
\hline
|
||||
\sphinxstyletheadfamily
|
||||
header1
|
||||
|
@ -1,6 +1,6 @@
|
||||
\label{\detokenize{longtable:longtable-with-tabularcolumn}}
|
||||
|
||||
\begin{savenotes}\sphinxatlongtablestart\begin{longtable}{|c|c|}
|
||||
\begin{savenotes}\sphinxatlongtablestart\begin{longtable}[c]{|c|c|}
|
||||
\hline
|
||||
\sphinxstyletheadfamily
|
||||
header1
|
||||
|
@ -11,3 +11,6 @@ Some additional anchors to exercise ignore code
|
||||
* `Example Bar invalid <http://example.com/#top>`_
|
||||
* `Example anchor invalid <http://www.sphinx-doc.org/en/1.7/intro.html#does-not-exist>`_
|
||||
* `Complete nonsense <https://localhost:7777/doesnotexist>`_
|
||||
|
||||
.. image:: http://example.com/image.png
|
||||
.. figure:: http://example.com/image2.png
|
||||
|
@ -1,2 +1 @@
|
||||
html_theme = 'classic'
|
||||
exclude_patterns = ['_build']
|
||||
|
@ -1,4 +1,3 @@
|
||||
html_theme = 'classic'
|
||||
exclude_patterns = ['_build']
|
||||
|
||||
latex_elements = {
|
||||
|
@ -1,2 +1 @@
|
||||
html_theme = 'classic'
|
||||
exclude_patterns = ['_build']
|
||||
|
@ -3,8 +3,6 @@ Autodoc tests
|
||||
|
||||
Just testing a few autodoc possibilities...
|
||||
|
||||
.. automodule:: util
|
||||
|
||||
.. automodule:: autodoc_target
|
||||
:members:
|
||||
|
||||
@ -28,12 +26,6 @@ Just testing a few autodoc possibilities...
|
||||
:members:
|
||||
|
||||
|
||||
.. automodule:: autodoc_fodder
|
||||
:noindex:
|
||||
|
||||
.. autoclass:: MarkupError
|
||||
|
||||
|
||||
.. currentmodule:: autodoc_target
|
||||
|
||||
.. autoclass:: InstAttCls
|
||||
|
@ -1,7 +0,0 @@
|
||||
|
||||
class MarkupError(object):
|
||||
"""
|
||||
.. note:: This is a docstring with a
|
||||
small markup error which should have
|
||||
correct location information.
|
||||
"""
|
@ -4,9 +4,6 @@ Sphinx image handling
|
||||
.. first, a simple test with direct filename
|
||||
.. image:: img.png
|
||||
|
||||
.. a non-existing image with direct filename
|
||||
.. image:: foo.png
|
||||
|
||||
.. an image with path name (relative to this directory!)
|
||||
.. image:: subdir/img.png
|
||||
:height: 100
|
||||
|
@ -3,7 +3,6 @@ Testing downloadable files
|
||||
|
||||
Download :download:`img.png` here.
|
||||
Download :download:`this <subdir/img.png>` there.
|
||||
Don't download :download:`this <nonexisting.png>`.
|
||||
|
||||
Test file and literal inclusion
|
||||
===============================
|
||||
|
@ -2,4 +2,4 @@
|
||||
|
||||
.. Paths in included files are relative to the file that
|
||||
includes them
|
||||
.. image:: ../root/img.png
|
||||
.. image:: subdir/img.png
|
||||
|
@ -1,3 +1,2 @@
|
||||
html_theme = 'classic'
|
||||
exclude_patterns = ['_build']
|
||||
templates_path = ['_templates']
|
||||
|
@ -1,2 +1 @@
|
||||
html_theme = 'classic'
|
||||
exclude_patterns = ['_build']
|
||||
|
@ -1,2 +1 @@
|
||||
html_theme = 'classic'
|
||||
exclude_patterns = ['_build']
|
||||
|
@ -12,6 +12,8 @@ import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from sphinx.util import docutils
|
||||
|
||||
|
||||
@pytest.fixture(scope='module', autouse=True)
|
||||
def setup_module(rootdir):
|
||||
@ -26,7 +28,10 @@ def test_html_translator(app, status, warning):
|
||||
# no set_translator()
|
||||
translator_class = app.builder.get_translator_class()
|
||||
assert translator_class
|
||||
assert translator_class.__name__ == 'HTMLTranslator'
|
||||
if docutils.__version_info__ < (0, 13):
|
||||
assert translator_class.__name__ == 'HTMLTranslator'
|
||||
else:
|
||||
assert translator_class.__name__ == 'HTML5Translator'
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='api-set-translator')
|
||||
|
@ -1345,7 +1345,7 @@ def test_autofunction_for_callable(app):
|
||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||
def test_mocked_module_imports(app, warning):
|
||||
# no autodoc_mock_imports
|
||||
options = {"members": 'TestAutodoc,decoratedFunction'}
|
||||
options = {"members": 'TestAutodoc,decoratedFunction,func'}
|
||||
actual = do_autodoc(app, 'module', 'target.need_mocks', options)
|
||||
assert list(actual) == []
|
||||
assert "autodoc: failed to import module 'need_mocks'" in warning.getvalue()
|
||||
@ -1382,6 +1382,12 @@ def test_mocked_module_imports(app, warning):
|
||||
' :module: target.need_mocks',
|
||||
'',
|
||||
' decoratedFunction docstring',
|
||||
' ',
|
||||
'',
|
||||
'.. py:function:: func(arg: missing_module.Class)',
|
||||
' :module: target.need_mocks',
|
||||
'',
|
||||
' a function takes mocked object as an argument',
|
||||
' '
|
||||
]
|
||||
assert warning.getvalue() == ''
|
||||
@ -1511,6 +1517,12 @@ def test_autodoc_default_options(app):
|
||||
assert ' .. py:attribute:: EnumCls.val1' in actual
|
||||
assert ' .. py:attribute:: EnumCls.val4' not in actual
|
||||
|
||||
# with :members: = True
|
||||
app.config.autodoc_default_options = {'members': True}
|
||||
actual = do_autodoc(app, 'class', 'target.enum.EnumCls')
|
||||
assert ' .. py:attribute:: EnumCls.val1' in actual
|
||||
assert ' .. py:attribute:: EnumCls.val4' not in actual
|
||||
|
||||
# with :members: and :undoc-members:
|
||||
app.config.autodoc_default_options = {
|
||||
'members': None,
|
||||
|
@ -60,7 +60,7 @@ def nonascii_srcdir(request, rootdir, sphinx_test_tempdir):
|
||||
"buildername",
|
||||
[
|
||||
# note: no 'html' - if it's ok with dirhtml it's ok with html
|
||||
'dirhtml', 'singlehtml', 'pickle', 'json', 'text', 'htmlhelp',
|
||||
'dirhtml', 'singlehtml', 'pickle', 'json', 'text',
|
||||
'changes', 'xml', 'pseudoxml', 'linkcheck',
|
||||
],
|
||||
)
|
||||
|
@ -15,6 +15,8 @@ from xml.etree import ElementTree
|
||||
|
||||
import pytest
|
||||
|
||||
from sphinx.util import docutils
|
||||
|
||||
|
||||
# check given command is runnable
|
||||
def runnable(command):
|
||||
@ -350,6 +352,8 @@ def test_epub_css_files(app):
|
||||
'href="https://example.com/custom.css" />' not in content)
|
||||
|
||||
|
||||
@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
|
||||
reason='docutils-0.13 or above is required')
|
||||
@pytest.mark.sphinx('epub', testroot='roles-download')
|
||||
def test_html_download_role(app, status, warning):
|
||||
app.build()
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
import os
|
||||
import re
|
||||
from hashlib import md5
|
||||
from itertools import cycle, chain
|
||||
|
||||
import pytest
|
||||
@ -17,6 +18,7 @@ from html5lib import HTMLParser
|
||||
|
||||
from sphinx.errors import ConfigError
|
||||
from sphinx.testing.util import strip_escseq
|
||||
from sphinx.util import docutils
|
||||
from sphinx.util.inventory import InventoryFile
|
||||
|
||||
|
||||
@ -128,6 +130,11 @@ def test_html_warnings(app, warning):
|
||||
'--- Got:\n' + html_warnings
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', confoverrides={'html4_writer': True})
|
||||
def test_html4_output(app, status, warning):
|
||||
app.build()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("fname,expect", flat_dict({
|
||||
'images.html': [
|
||||
(".//img[@src='_images/img.png']", ''),
|
||||
@ -192,18 +199,18 @@ def test_html_warnings(app, warning):
|
||||
# an option list
|
||||
(".//span[@class='option']", '--help'),
|
||||
# admonitions
|
||||
(".//p[@class='first admonition-title']", 'My Admonition'),
|
||||
(".//p[@class='last']", 'Note text.'),
|
||||
(".//p[@class='last']", 'Warning text.'),
|
||||
(".//p[@class='admonition-title']", 'My Admonition'),
|
||||
(".//div[@class='admonition note']/p", 'Note text.'),
|
||||
(".//div[@class='admonition warning']/p", 'Warning text.'),
|
||||
# inline markup
|
||||
(".//li/strong", r'^command\\n$'),
|
||||
(".//li/strong", r'^program\\n$'),
|
||||
(".//li/em", r'^dfn\\n$'),
|
||||
(".//li/kbd", r'^kbd\\n$'),
|
||||
(".//li/span", 'File \N{TRIANGULAR BULLET} Close'),
|
||||
(".//li/code/span[@class='pre']", '^a/$'),
|
||||
(".//li/code/em/span[@class='pre']", '^varpart$'),
|
||||
(".//li/code/em/span[@class='pre']", '^i$'),
|
||||
(".//li/p/strong", r'^command\\n$'),
|
||||
(".//li/p/strong", r'^program\\n$'),
|
||||
(".//li/p/em", r'^dfn\\n$'),
|
||||
(".//li/p/kbd", r'^kbd\\n$'),
|
||||
(".//li/p/span", 'File \N{TRIANGULAR BULLET} Close'),
|
||||
(".//li/p/code/span[@class='pre']", '^a/$'),
|
||||
(".//li/p/code/em/span[@class='pre']", '^varpart$'),
|
||||
(".//li/p/code/em/span[@class='pre']", '^i$'),
|
||||
(".//a[@href='https://www.python.org/dev/peps/pep-0008']"
|
||||
"[@class='pep reference external']/strong", 'PEP 8'),
|
||||
(".//a[@href='https://www.python.org/dev/peps/pep-0008']"
|
||||
@ -236,13 +243,13 @@ def test_html_warnings(app, warning):
|
||||
(".//div[@class='versionchanged']/p",
|
||||
'Second paragraph of versionchanged'),
|
||||
# footnote reference
|
||||
(".//a[@class='footnote-reference']", r'\[1\]'),
|
||||
(".//a[@class='footnote-reference brackets']", r'1'),
|
||||
# created by reference lookup
|
||||
(".//a[@href='index.html#ref1']", ''),
|
||||
# ``seealso`` directive
|
||||
(".//div/p[@class='first admonition-title']", 'See also'),
|
||||
(".//div/p[@class='admonition-title']", 'See also'),
|
||||
# a ``hlist`` directive
|
||||
(".//table[@class='hlist']/tbody/tr/td/ul/li", '^This$'),
|
||||
(".//table[@class='hlist']/tbody/tr/td/ul/li/p", '^This$'),
|
||||
# a ``centered`` directive
|
||||
(".//p[@class='centered']/strong", 'LICENSE'),
|
||||
# a glossary
|
||||
@ -261,10 +268,10 @@ def test_html_warnings(app, warning):
|
||||
# tests for numeric labels
|
||||
(".//a[@href='#id1'][@class='reference internal']/span", 'Testing various markup'),
|
||||
# tests for smartypants
|
||||
(".//li", 'Smart “quotes” in English ‘text’.'),
|
||||
(".//li", 'Smart — long and – short dashes.'),
|
||||
(".//li", 'Ellipsis…'),
|
||||
(".//li//code//span[@class='pre']", 'foo--"bar"...'),
|
||||
(".//li/p", 'Smart “quotes” in English ‘text’.'),
|
||||
(".//li/p", 'Smart — long and – short dashes.'),
|
||||
(".//li/p", 'Ellipsis…'),
|
||||
(".//li/p/code/span[@class='pre']", 'foo--"bar"...'),
|
||||
(".//p", 'Этот «абзац» должен использовать „русские“ кавычки.'),
|
||||
(".//p", 'Il dit : « C’est “super” ! »'),
|
||||
],
|
||||
@ -294,24 +301,24 @@ def test_html_warnings(app, warning):
|
||||
(".//li[@class='toctree-l1']/a[@href='markup.html']",
|
||||
'Testing various markup'),
|
||||
# test unknown field names
|
||||
(".//th[@class='field-name']", 'Field_name:'),
|
||||
(".//th[@class='field-name']", 'Field_name all lower:'),
|
||||
(".//th[@class='field-name']", 'FIELD_NAME:'),
|
||||
(".//th[@class='field-name']", 'FIELD_NAME ALL CAPS:'),
|
||||
(".//th[@class='field-name']", 'Field_Name:'),
|
||||
(".//th[@class='field-name']", 'Field_Name All Word Caps:'),
|
||||
(".//th[@class='field-name']", 'Field_name:'),
|
||||
(".//th[@class='field-name']", 'Field_name First word cap:'),
|
||||
(".//th[@class='field-name']", 'FIELd_name:'),
|
||||
(".//th[@class='field-name']", 'FIELd_name PARTial caps:'),
|
||||
(".//dt[@class='field-odd']", 'Field_name'),
|
||||
(".//dt[@class='field-even']", 'Field_name all lower'),
|
||||
(".//dt[@class='field-odd']", 'FIELD_NAME'),
|
||||
(".//dt[@class='field-even']", 'FIELD_NAME ALL CAPS'),
|
||||
(".//dt[@class='field-odd']", 'Field_Name'),
|
||||
(".//dt[@class='field-even']", 'Field_Name All Word Caps'),
|
||||
(".//dt[@class='field-odd']", 'Field_name'),
|
||||
(".//dt[@class='field-even']", 'Field_name First word cap'),
|
||||
(".//dt[@class='field-odd']", 'FIELd_name'),
|
||||
(".//dt[@class='field-even']", 'FIELd_name PARTial caps'),
|
||||
# custom sidebar
|
||||
(".//h4", 'Custom sidebar'),
|
||||
# docfields
|
||||
(".//td[@class='field-body']/strong", '^moo$'),
|
||||
(".//td[@class='field-body']/strong", tail_check(r'\(Moo\) .* Moo')),
|
||||
(".//td[@class='field-body']/ul/li/strong", '^hour$'),
|
||||
(".//td[@class='field-body']/ul/li/em", '^DuplicateType$'),
|
||||
(".//td[@class='field-body']/ul/li/em", tail_check(r'.* Some parameter')),
|
||||
(".//dd[@class='field-odd']/p/strong", '^moo$'),
|
||||
(".//dd[@class='field-odd']/p/strong", tail_check(r'\(Moo\) .* Moo')),
|
||||
(".//dd[@class='field-odd']/ul/li/p/strong", '^hour$'),
|
||||
(".//dd[@class='field-odd']/ul/li/p/em", '^DuplicateType$'),
|
||||
(".//dd[@class='field-odd']/ul/li/p/em", tail_check(r'.* Some parameter')),
|
||||
# others
|
||||
(".//a[@class='reference internal'][@href='#cmdoption-perl-arg-p']/code/span",
|
||||
'perl'),
|
||||
@ -340,17 +347,17 @@ def test_html_warnings(app, warning):
|
||||
'index.html': [
|
||||
(".//meta[@name='hc'][@content='hcval']", ''),
|
||||
(".//meta[@name='hc_co'][@content='hcval_co']", ''),
|
||||
(".//td[@class='label']", r'\[Ref1\]'),
|
||||
(".//td[@class='label']", ''),
|
||||
(".//dt[@class='label']/span[@class='brackets']", r'Ref1'),
|
||||
(".//dt[@class='label']", ''),
|
||||
(".//li[@class='toctree-l1']/a", 'Testing various markup'),
|
||||
(".//li[@class='toctree-l2']/a", 'Inline markup'),
|
||||
(".//title", 'Sphinx <Tests>'),
|
||||
(".//div[@class='footer']", 'Georg Brandl & Team'),
|
||||
(".//a[@href='http://python.org/']"
|
||||
"[@class='reference external']", ''),
|
||||
(".//li/a[@href='genindex.html']/span", 'Index'),
|
||||
(".//li/a[@href='py-modindex.html']/span", 'Module Index'),
|
||||
(".//li/a[@href='search.html']/span", 'Search Page'),
|
||||
(".//li/p/a[@href='genindex.html']/span", 'Index'),
|
||||
(".//li/p/a[@href='py-modindex.html']/span", 'Module Index'),
|
||||
(".//li/p/a[@href='search.html']/span", 'Search Page'),
|
||||
# custom sidebar only for contents
|
||||
(".//h4", 'Contents sidebar'),
|
||||
# custom JavaScript
|
||||
@ -381,37 +388,41 @@ def test_html_warnings(app, warning):
|
||||
(".//li/a", "double"),
|
||||
],
|
||||
'footnote.html': [
|
||||
(".//a[@class='footnote-reference'][@href='#id9'][@id='id1']", r"\[1\]"),
|
||||
(".//a[@class='footnote-reference'][@href='#id10'][@id='id2']", r"\[2\]"),
|
||||
(".//a[@class='footnote-reference'][@href='#foo'][@id='id3']", r"\[3\]"),
|
||||
(".//a[@class='footnote-reference brackets'][@href='#id9'][@id='id1']", r"1"),
|
||||
(".//a[@class='footnote-reference brackets'][@href='#id10'][@id='id2']", r"2"),
|
||||
(".//a[@class='footnote-reference brackets'][@href='#foo'][@id='id3']", r"3"),
|
||||
(".//a[@class='reference internal'][@href='#bar'][@id='id4']", r"\[bar\]"),
|
||||
(".//a[@class='reference internal'][@href='#baz-qux'][@id='id5']", r"\[baz_qux\]"),
|
||||
(".//a[@class='footnote-reference'][@href='#id11'][@id='id6']", r"\[4\]"),
|
||||
(".//a[@class='footnote-reference'][@href='#id12'][@id='id7']", r"\[5\]"),
|
||||
(".//a[@class='fn-backref'][@href='#id1']", r"\[1\]"),
|
||||
(".//a[@class='fn-backref'][@href='#id2']", r"\[2\]"),
|
||||
(".//a[@class='fn-backref'][@href='#id3']", r"\[3\]"),
|
||||
(".//a[@class='fn-backref'][@href='#id4']", r"\[bar\]"),
|
||||
(".//a[@class='fn-backref'][@href='#id5']", r"\[baz_qux\]"),
|
||||
(".//a[@class='fn-backref'][@href='#id6']", r"\[4\]"),
|
||||
(".//a[@class='fn-backref'][@href='#id7']", r"\[5\]"),
|
||||
(".//a[@class='fn-backref'][@href='#id8']", r"\[6\]"),
|
||||
(".//a[@class='footnote-reference brackets'][@href='#id11'][@id='id6']", r"4"),
|
||||
(".//a[@class='footnote-reference brackets'][@href='#id12'][@id='id7']", r"5"),
|
||||
(".//a[@class='fn-backref'][@href='#id1']", r"1"),
|
||||
(".//a[@class='fn-backref'][@href='#id2']", r"2"),
|
||||
(".//a[@class='fn-backref'][@href='#id3']", r"3"),
|
||||
(".//a[@class='fn-backref'][@href='#id4']", r"bar"),
|
||||
(".//a[@class='fn-backref'][@href='#id5']", r"baz_qux"),
|
||||
(".//a[@class='fn-backref'][@href='#id6']", r"4"),
|
||||
(".//a[@class='fn-backref'][@href='#id7']", r"5"),
|
||||
(".//a[@class='fn-backref'][@href='#id8']", r"6"),
|
||||
],
|
||||
'otherext.html': [
|
||||
(".//h1", "Generated section"),
|
||||
(".//a[@href='_sources/otherext.foo.txt']", ''),
|
||||
]
|
||||
}))
|
||||
@pytest.mark.sphinx('html', tags=['testtag'], confoverrides={
|
||||
'html_context.hckey_co': 'hcval_co'})
|
||||
@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
|
||||
reason='docutils-0.13 or above is required')
|
||||
@pytest.mark.sphinx('html', tags=['testtag'],
|
||||
confoverrides={'html_context.hckey_co': 'hcval_co'})
|
||||
@pytest.mark.test_params(shared_result='test_build_html_output')
|
||||
def test_html_output(app, cached_etree_parse, fname, expect):
|
||||
def test_html5_output(app, cached_etree_parse, fname, expect):
|
||||
app.build()
|
||||
print(app.outdir / fname)
|
||||
check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect)
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', tags=['testtag'], confoverrides={
|
||||
'html_context.hckey_co': 'hcval_co'})
|
||||
@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
|
||||
reason='docutils-0.13 or above is required')
|
||||
@pytest.mark.sphinx('html')
|
||||
@pytest.mark.test_params(shared_result='test_build_html_output')
|
||||
def test_html_download(app):
|
||||
app.build()
|
||||
@ -435,6 +446,29 @@ def test_html_download(app):
|
||||
assert matched.group(1) == filename
|
||||
|
||||
|
||||
@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
|
||||
reason='docutils-0.13 or above is required')
|
||||
@pytest.mark.sphinx('html', testroot='roles-download')
|
||||
def test_html_download_role(app, status, warning):
|
||||
app.build()
|
||||
digest = md5((app.srcdir / 'dummy.dat').encode()).hexdigest()
|
||||
assert (app.outdir / '_downloads' / digest / 'dummy.dat').exists()
|
||||
|
||||
content = (app.outdir / 'index.html').text()
|
||||
assert (('<li><p><a class="reference download internal" download="" '
|
||||
'href="_downloads/%s/dummy.dat">'
|
||||
'<code class="xref download docutils literal notranslate">'
|
||||
'<span class="pre">dummy.dat</span></code></a></p></li>' % digest)
|
||||
in content)
|
||||
assert ('<li><p><code class="xref download docutils literal notranslate">'
|
||||
'<span class="pre">not_found.dat</span></code></p></li>' in content)
|
||||
assert ('<li><p><a class="reference download external" download="" '
|
||||
'href="http://www.sphinx-doc.org/en/master/_static/sphinxheader.png">'
|
||||
'<code class="xref download docutils literal notranslate">'
|
||||
'<span class="pre">Sphinx</span> <span class="pre">logo</span>'
|
||||
'</code></a></p></li>' in content)
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='build-html-translator')
|
||||
def test_html_translator(app):
|
||||
app.build()
|
||||
@ -473,6 +507,8 @@ def test_html_translator(app):
|
||||
(".//h1", '2.1.1. Baz A', True),
|
||||
],
|
||||
}))
|
||||
@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
|
||||
reason='docutils-0.13 or above is required')
|
||||
@pytest.mark.sphinx('html', testroot='tocdepth')
|
||||
@pytest.mark.test_params(shared_result='test_build_html_tocdepth')
|
||||
def test_tocdepth(app, cached_etree_parse, fname, expect):
|
||||
@ -508,6 +544,8 @@ def test_tocdepth(app, cached_etree_parse, fname, expect):
|
||||
(".//h4", '2.1.1. Baz A', True),
|
||||
],
|
||||
}))
|
||||
@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
|
||||
reason='docutils-0.13 or above is required')
|
||||
@pytest.mark.sphinx('singlehtml', testroot='tocdepth')
|
||||
@pytest.mark.test_params(shared_result='test_build_html_tocdepth')
|
||||
def test_tocdepth_singlehtml(app, cached_etree_parse, fname, expect):
|
||||
@ -527,44 +565,46 @@ def test_numfig_disabled_warn(app, warning):
|
||||
|
||||
@pytest.mark.parametrize("fname,expect", flat_dict({
|
||||
'index.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", None, True),
|
||||
(".//table/caption/span[@class='caption-number']", None, True),
|
||||
(".//div[@class='code-block-caption']/"
|
||||
"span[@class='caption-number']", None, True),
|
||||
(".//li/code/span", '^fig1$', True),
|
||||
(".//li/code/span", '^Figure%s$', True),
|
||||
(".//li/code/span", '^table-1$', True),
|
||||
(".//li/code/span", '^Table:%s$', True),
|
||||
(".//li/code/span", '^CODE_1$', True),
|
||||
(".//li/code/span", '^Code-%s$', True),
|
||||
(".//li/a/span", '^Section 1$', True),
|
||||
(".//li/a/span", '^Section 2.1$', True),
|
||||
(".//li/code/span", '^Fig.{number}$', True),
|
||||
(".//li/a/span", '^Sect.1 Foo$', True),
|
||||
(".//li/p/code/span", '^fig1$', True),
|
||||
(".//li/p/code/span", '^Figure%s$', True),
|
||||
(".//li/p/code/span", '^table-1$', True),
|
||||
(".//li/p/code/span", '^Table:%s$', True),
|
||||
(".//li/p/code/span", '^CODE_1$', True),
|
||||
(".//li/p/code/span", '^Code-%s$', True),
|
||||
(".//li/p/a/span", '^Section 1$', True),
|
||||
(".//li/p/a/span", '^Section 2.1$', True),
|
||||
(".//li/p/code/span", '^Fig.{number}$', True),
|
||||
(".//li/p/a/span", '^Sect.1 Foo$', True),
|
||||
],
|
||||
'foo.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", None, True),
|
||||
(".//table/caption/span[@class='caption-number']", None, True),
|
||||
(".//div[@class='code-block-caption']/"
|
||||
"span[@class='caption-number']", None, True),
|
||||
],
|
||||
'bar.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", None, True),
|
||||
(".//table/caption/span[@class='caption-number']", None, True),
|
||||
(".//div[@class='code-block-caption']/"
|
||||
"span[@class='caption-number']", None, True),
|
||||
],
|
||||
'baz.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", None, True),
|
||||
(".//table/caption/span[@class='caption-number']", None, True),
|
||||
(".//div[@class='code-block-caption']/"
|
||||
"span[@class='caption-number']", None, True),
|
||||
],
|
||||
}))
|
||||
@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
|
||||
reason='docutils-0.13 or above is required')
|
||||
@pytest.mark.sphinx('html', testroot='numfig')
|
||||
@pytest.mark.test_params(shared_result='test_build_html_numfig')
|
||||
def test_numfig_disabled(app, cached_etree_parse, fname, expect):
|
||||
@ -593,9 +633,9 @@ def test_numfig_without_numbered_toctree_warn(app, warning):
|
||||
|
||||
@pytest.mark.parametrize("fname,expect", flat_dict({
|
||||
'index.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 9 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 10 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 9 $', True),
|
||||
@ -605,25 +645,25 @@ def test_numfig_without_numbered_toctree_warn(app, warning):
|
||||
"span[@class='caption-number']", '^Listing 9 $', True),
|
||||
(".//div[@class='code-block-caption']/"
|
||||
"span[@class='caption-number']", '^Listing 10 $', True),
|
||||
(".//li/a/span", '^Fig. 9$', True),
|
||||
(".//li/a/span", '^Figure6$', True),
|
||||
(".//li/a/span", '^Table 9$', True),
|
||||
(".//li/a/span", '^Table:6$', True),
|
||||
(".//li/a/span", '^Listing 9$', True),
|
||||
(".//li/a/span", '^Code-6$', True),
|
||||
(".//li/code/span", '^foo$', True),
|
||||
(".//li/code/span", '^bar_a$', True),
|
||||
(".//li/a/span", '^Fig.9 should be Fig.1$', True),
|
||||
(".//li/code/span", '^Sect.{number}$', True),
|
||||
(".//li/p/a/span", '^Fig. 9$', True),
|
||||
(".//li/p/a/span", '^Figure6$', True),
|
||||
(".//li/p/a/span", '^Table 9$', True),
|
||||
(".//li/p/a/span", '^Table:6$', True),
|
||||
(".//li/p/a/span", '^Listing 9$', True),
|
||||
(".//li/p/a/span", '^Code-6$', True),
|
||||
(".//li/p/code/span", '^foo$', True),
|
||||
(".//li/p/code/span", '^bar_a$', True),
|
||||
(".//li/p/a/span", '^Fig.9 should be Fig.1$', True),
|
||||
(".//li/p/code/span", '^Sect.{number}$', True),
|
||||
],
|
||||
'foo.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 3 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 4 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 1 $', True),
|
||||
@ -643,11 +683,11 @@ def test_numfig_without_numbered_toctree_warn(app, warning):
|
||||
"span[@class='caption-number']", '^Listing 4 $', True),
|
||||
],
|
||||
'bar.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 5 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 7 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 8 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 5 $', True),
|
||||
@ -663,7 +703,7 @@ def test_numfig_without_numbered_toctree_warn(app, warning):
|
||||
"span[@class='caption-number']", '^Listing 8 $', True),
|
||||
],
|
||||
'baz.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 6 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 6 $', True),
|
||||
@ -671,6 +711,8 @@ def test_numfig_without_numbered_toctree_warn(app, warning):
|
||||
"span[@class='caption-number']", '^Listing 6 $', True),
|
||||
],
|
||||
}))
|
||||
@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
|
||||
reason='docutils-0.13 or above is required')
|
||||
@pytest.mark.sphinx(
|
||||
'html', testroot='numfig',
|
||||
srcdir='test_numfig_without_numbered_toctree',
|
||||
@ -699,9 +741,9 @@ def test_numfig_with_numbered_toctree_warn(app, warning):
|
||||
|
||||
@pytest.mark.parametrize("fname,expect", flat_dict({
|
||||
'index.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 1 $', True),
|
||||
@ -711,25 +753,25 @@ def test_numfig_with_numbered_toctree_warn(app, warning):
|
||||
"span[@class='caption-number']", '^Listing 1 $', True),
|
||||
(".//div[@class='code-block-caption']/"
|
||||
"span[@class='caption-number']", '^Listing 2 $', True),
|
||||
(".//li/a/span", '^Fig. 1$', True),
|
||||
(".//li/a/span", '^Figure2.2$', True),
|
||||
(".//li/a/span", '^Table 1$', True),
|
||||
(".//li/a/span", '^Table:2.2$', True),
|
||||
(".//li/a/span", '^Listing 1$', True),
|
||||
(".//li/a/span", '^Code-2.2$', True),
|
||||
(".//li/a/span", '^Section.1$', True),
|
||||
(".//li/a/span", '^Section.2.1$', True),
|
||||
(".//li/a/span", '^Fig.1 should be Fig.1$', True),
|
||||
(".//li/a/span", '^Sect.1 Foo$', True),
|
||||
(".//li/p/a/span", '^Fig. 1$', True),
|
||||
(".//li/p/a/span", '^Figure2.2$', True),
|
||||
(".//li/p/a/span", '^Table 1$', True),
|
||||
(".//li/p/a/span", '^Table:2.2$', True),
|
||||
(".//li/p/a/span", '^Listing 1$', True),
|
||||
(".//li/p/a/span", '^Code-2.2$', True),
|
||||
(".//li/p/a/span", '^Section.1$', True),
|
||||
(".//li/p/a/span", '^Section.2.1$', True),
|
||||
(".//li/p/a/span", '^Fig.1 should be Fig.1$', True),
|
||||
(".//li/p/a/span", '^Sect.1 Foo$', True),
|
||||
],
|
||||
'foo.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1.1 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1.2 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1.3 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1.4 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 1.1 $', True),
|
||||
@ -749,11 +791,11 @@ def test_numfig_with_numbered_toctree_warn(app, warning):
|
||||
"span[@class='caption-number']", '^Listing 1.4 $', True),
|
||||
],
|
||||
'bar.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2.1 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2.3 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2.4 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 2.1 $', True),
|
||||
@ -769,7 +811,7 @@ def test_numfig_with_numbered_toctree_warn(app, warning):
|
||||
"span[@class='caption-number']", '^Listing 2.4 $', True),
|
||||
],
|
||||
'baz.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2.2 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 2.2 $', True),
|
||||
@ -777,6 +819,8 @@ def test_numfig_with_numbered_toctree_warn(app, warning):
|
||||
"span[@class='caption-number']", '^Listing 2.2 $', True),
|
||||
],
|
||||
}))
|
||||
@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
|
||||
reason='docutils-0.13 or above is required')
|
||||
@pytest.mark.sphinx('html', testroot='numfig', confoverrides={'numfig': True})
|
||||
@pytest.mark.test_params(shared_result='test_build_html_numfig_on')
|
||||
def test_numfig_with_numbered_toctree(app, cached_etree_parse, fname, expect):
|
||||
@ -802,9 +846,9 @@ def test_numfig_with_prefix_warn(app, warning):
|
||||
|
||||
@pytest.mark.parametrize("fname,expect", flat_dict({
|
||||
'index.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Figure:1 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Figure:2 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Tab_1 $', True),
|
||||
@ -814,25 +858,25 @@ def test_numfig_with_prefix_warn(app, warning):
|
||||
"span[@class='caption-number']", '^Code-1 $', True),
|
||||
(".//div[@class='code-block-caption']/"
|
||||
"span[@class='caption-number']", '^Code-2 $', True),
|
||||
(".//li/a/span", '^Figure:1$', True),
|
||||
(".//li/a/span", '^Figure2.2$', True),
|
||||
(".//li/a/span", '^Tab_1$', True),
|
||||
(".//li/a/span", '^Table:2.2$', True),
|
||||
(".//li/a/span", '^Code-1$', True),
|
||||
(".//li/a/span", '^Code-2.2$', True),
|
||||
(".//li/a/span", '^SECTION-1$', True),
|
||||
(".//li/a/span", '^SECTION-2.1$', True),
|
||||
(".//li/a/span", '^Fig.1 should be Fig.1$', True),
|
||||
(".//li/a/span", '^Sect.1 Foo$', True),
|
||||
(".//li/p/a/span", '^Figure:1$', True),
|
||||
(".//li/p/a/span", '^Figure2.2$', True),
|
||||
(".//li/p/a/span", '^Tab_1$', True),
|
||||
(".//li/p/a/span", '^Table:2.2$', True),
|
||||
(".//li/p/a/span", '^Code-1$', True),
|
||||
(".//li/p/a/span", '^Code-2.2$', True),
|
||||
(".//li/p/a/span", '^SECTION-1$', True),
|
||||
(".//li/p/a/span", '^SECTION-2.1$', True),
|
||||
(".//li/p/a/span", '^Fig.1 should be Fig.1$', True),
|
||||
(".//li/p/a/span", '^Sect.1 Foo$', True),
|
||||
],
|
||||
'foo.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Figure:1.1 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Figure:1.2 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Figure:1.3 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Figure:1.4 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Tab_1.1 $', True),
|
||||
@ -852,11 +896,11 @@ def test_numfig_with_prefix_warn(app, warning):
|
||||
"span[@class='caption-number']", '^Code-1.4 $', True),
|
||||
],
|
||||
'bar.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Figure:2.1 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Figure:2.3 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Figure:2.4 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Tab_2.1 $', True),
|
||||
@ -872,7 +916,7 @@ def test_numfig_with_prefix_warn(app, warning):
|
||||
"span[@class='caption-number']", '^Code-2.4 $', True),
|
||||
],
|
||||
'baz.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Figure:2.2 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Tab_2.2 $', True),
|
||||
@ -880,20 +924,22 @@ def test_numfig_with_prefix_warn(app, warning):
|
||||
"span[@class='caption-number']", '^Code-2.2 $', True),
|
||||
],
|
||||
}))
|
||||
@pytest.mark.sphinx('html', testroot='numfig', confoverrides={
|
||||
'numfig': True,
|
||||
'numfig_format': {'figure': 'Figure:%s',
|
||||
'table': 'Tab_%s',
|
||||
'code-block': 'Code-%s',
|
||||
'section': 'SECTION-%s'}})
|
||||
@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
|
||||
reason='docutils-0.13 or above is required')
|
||||
@pytest.mark.sphinx('html', testroot='numfig',
|
||||
confoverrides={'numfig': True,
|
||||
'numfig_format': {'figure': 'Figure:%s',
|
||||
'table': 'Tab_%s',
|
||||
'code-block': 'Code-%s',
|
||||
'section': 'SECTION-%s'}})
|
||||
@pytest.mark.test_params(shared_result='test_build_html_numfig_format_warn')
|
||||
def test_numfig_with_prefix(app, cached_etree_parse, fname, expect):
|
||||
app.build()
|
||||
check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect)
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='numfig', confoverrides={
|
||||
'numfig': True, 'numfig_secnum_depth': 2})
|
||||
@pytest.mark.sphinx('html', testroot='numfig',
|
||||
confoverrides={'numfig': True, 'numfig_secnum_depth': 2})
|
||||
@pytest.mark.test_params(shared_result='test_build_html_numfig_depth_2')
|
||||
def test_numfig_with_secnum_depth_warn(app, warning):
|
||||
app.build()
|
||||
@ -906,9 +952,9 @@ def test_numfig_with_secnum_depth_warn(app, warning):
|
||||
|
||||
@pytest.mark.parametrize("fname,expect", flat_dict({
|
||||
'index.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 1 $', True),
|
||||
@ -918,25 +964,25 @@ def test_numfig_with_secnum_depth_warn(app, warning):
|
||||
"span[@class='caption-number']", '^Listing 1 $', True),
|
||||
(".//div[@class='code-block-caption']/"
|
||||
"span[@class='caption-number']", '^Listing 2 $', True),
|
||||
(".//li/a/span", '^Fig. 1$', True),
|
||||
(".//li/a/span", '^Figure2.1.2$', True),
|
||||
(".//li/a/span", '^Table 1$', True),
|
||||
(".//li/a/span", '^Table:2.1.2$', True),
|
||||
(".//li/a/span", '^Listing 1$', True),
|
||||
(".//li/a/span", '^Code-2.1.2$', True),
|
||||
(".//li/a/span", '^Section.1$', True),
|
||||
(".//li/a/span", '^Section.2.1$', True),
|
||||
(".//li/a/span", '^Fig.1 should be Fig.1$', True),
|
||||
(".//li/a/span", '^Sect.1 Foo$', True),
|
||||
(".//li/p/a/span", '^Fig. 1$', True),
|
||||
(".//li/p/a/span", '^Figure2.1.2$', True),
|
||||
(".//li/p/a/span", '^Table 1$', True),
|
||||
(".//li/p/a/span", '^Table:2.1.2$', True),
|
||||
(".//li/p/a/span", '^Listing 1$', True),
|
||||
(".//li/p/a/span", '^Code-2.1.2$', True),
|
||||
(".//li/p/a/span", '^Section.1$', True),
|
||||
(".//li/p/a/span", '^Section.2.1$', True),
|
||||
(".//li/p/a/span", '^Fig.1 should be Fig.1$', True),
|
||||
(".//li/p/a/span", '^Sect.1 Foo$', True),
|
||||
],
|
||||
'foo.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1.1 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1.1.1 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1.1.2 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1.2.1 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 1.1 $', True),
|
||||
@ -956,11 +1002,11 @@ def test_numfig_with_secnum_depth_warn(app, warning):
|
||||
"span[@class='caption-number']", '^Listing 1.2.1 $', True),
|
||||
],
|
||||
'bar.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2.1.1 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2.1.3 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2.2.1 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 2.1.1 $', True),
|
||||
@ -976,7 +1022,7 @@ def test_numfig_with_secnum_depth_warn(app, warning):
|
||||
"span[@class='caption-number']", '^Listing 2.2.1 $', True),
|
||||
],
|
||||
'baz.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2.1.2 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 2.1.2 $', True),
|
||||
@ -984,8 +1030,11 @@ def test_numfig_with_secnum_depth_warn(app, warning):
|
||||
"span[@class='caption-number']", '^Listing 2.1.2 $', True),
|
||||
],
|
||||
}))
|
||||
@pytest.mark.sphinx('html', testroot='numfig', confoverrides={
|
||||
'numfig': True, 'numfig_secnum_depth': 2})
|
||||
@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
|
||||
reason='docutils-0.13 or above is required')
|
||||
@pytest.mark.sphinx('html', testroot='numfig',
|
||||
confoverrides={'numfig': True,
|
||||
'numfig_secnum_depth': 2})
|
||||
@pytest.mark.test_params(shared_result='test_build_html_numfig_depth_2')
|
||||
def test_numfig_with_secnum_depth(app, cached_etree_parse, fname, expect):
|
||||
app.build()
|
||||
@ -994,9 +1043,9 @@ def test_numfig_with_secnum_depth(app, cached_etree_parse, fname, expect):
|
||||
|
||||
@pytest.mark.parametrize("fname,expect", flat_dict({
|
||||
'index.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 1 $', True),
|
||||
@ -1006,23 +1055,23 @@ def test_numfig_with_secnum_depth(app, cached_etree_parse, fname, expect):
|
||||
"span[@class='caption-number']", '^Listing 1 $', True),
|
||||
(".//div[@class='code-block-caption']/"
|
||||
"span[@class='caption-number']", '^Listing 2 $', True),
|
||||
(".//li/a/span", '^Fig. 1$', True),
|
||||
(".//li/a/span", '^Figure2.2$', True),
|
||||
(".//li/a/span", '^Table 1$', True),
|
||||
(".//li/a/span", '^Table:2.2$', True),
|
||||
(".//li/a/span", '^Listing 1$', True),
|
||||
(".//li/a/span", '^Code-2.2$', True),
|
||||
(".//li/a/span", '^Section.1$', True),
|
||||
(".//li/a/span", '^Section.2.1$', True),
|
||||
(".//li/a/span", '^Fig.1 should be Fig.1$', True),
|
||||
(".//li/a/span", '^Sect.1 Foo$', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//li/p/a/span", '^Fig. 1$', True),
|
||||
(".//li/p/a/span", '^Figure2.2$', True),
|
||||
(".//li/p/a/span", '^Table 1$', True),
|
||||
(".//li/p/a/span", '^Table:2.2$', True),
|
||||
(".//li/p/a/span", '^Listing 1$', True),
|
||||
(".//li/p/a/span", '^Code-2.2$', True),
|
||||
(".//li/p/a/span", '^Section.1$', True),
|
||||
(".//li/p/a/span", '^Section.2.1$', True),
|
||||
(".//li/p/a/span", '^Fig.1 should be Fig.1$', True),
|
||||
(".//li/p/a/span", '^Sect.1 Foo$', True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1.1 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1.2 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1.3 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 1.4 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 1.1 $', True),
|
||||
@ -1040,11 +1089,11 @@ def test_numfig_with_secnum_depth(app, cached_etree_parse, fname, expect):
|
||||
"span[@class='caption-number']", '^Listing 1.3 $', True),
|
||||
(".//div[@class='code-block-caption']/"
|
||||
"span[@class='caption-number']", '^Listing 1.4 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2.1 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2.3 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2.4 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 2.1 $', True),
|
||||
@ -1058,7 +1107,7 @@ def test_numfig_with_secnum_depth(app, cached_etree_parse, fname, expect):
|
||||
"span[@class='caption-number']", '^Listing 2.3 $', True),
|
||||
(".//div[@class='code-block-caption']/"
|
||||
"span[@class='caption-number']", '^Listing 2.4 $', True),
|
||||
(".//div[@class='figure']/p[@class='caption']/"
|
||||
(".//div[@class='figure align-center']/p[@class='caption']/"
|
||||
"span[@class='caption-number']", '^Fig. 2.2 $', True),
|
||||
(".//table/caption/span[@class='caption-number']",
|
||||
'^Table 2.2 $', True),
|
||||
@ -1066,8 +1115,9 @@ def test_numfig_with_secnum_depth(app, cached_etree_parse, fname, expect):
|
||||
"span[@class='caption-number']", '^Listing 2.2 $', True),
|
||||
],
|
||||
}))
|
||||
@pytest.mark.sphinx('singlehtml', testroot='numfig', confoverrides={
|
||||
'numfig': True})
|
||||
@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
|
||||
reason='docutils-0.13 or above is required')
|
||||
@pytest.mark.sphinx('singlehtml', testroot='numfig', confoverrides={'numfig': True})
|
||||
@pytest.mark.test_params(shared_result='test_build_html_numfig_on')
|
||||
def test_numfig_with_singlehtml(app, cached_etree_parse, fname, expect):
|
||||
app.build()
|
||||
@ -1076,25 +1126,25 @@ def test_numfig_with_singlehtml(app, cached_etree_parse, fname, expect):
|
||||
|
||||
@pytest.mark.parametrize("fname,expect", flat_dict({
|
||||
'index.html': [
|
||||
(".//div[@class='figure']/p[@class='caption']/span[@class='caption-number']",
|
||||
"Fig. 1", True),
|
||||
(".//div[@class='figure']/p[@class='caption']/span[@class='caption-number']",
|
||||
"Fig. 2", True),
|
||||
(".//div[@class='figure']/p[@class='caption']/span[@class='caption-number']",
|
||||
"Fig. 3", True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']"
|
||||
"/span[@class='caption-number']", "Fig. 1", True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']"
|
||||
"/span[@class='caption-number']", "Fig. 2", True),
|
||||
(".//div[@class='figure align-center']/p[@class='caption']"
|
||||
"/span[@class='caption-number']", "Fig. 3", True),
|
||||
(".//div//span[@class='caption-number']", "No.1 ", True),
|
||||
(".//div//span[@class='caption-number']", "No.2 ", True),
|
||||
(".//li/a/span", 'Fig. 1', True),
|
||||
(".//li/a/span", 'Fig. 2', True),
|
||||
(".//li/a/span", 'Fig. 3', True),
|
||||
(".//li/a/span", 'No.1', True),
|
||||
(".//li/a/span", 'No.2', True),
|
||||
(".//li/p/a/span", 'Fig. 1', True),
|
||||
(".//li/p/a/span", 'Fig. 2', True),
|
||||
(".//li/p/a/span", 'Fig. 3', True),
|
||||
(".//li/p/a/span", 'No.1', True),
|
||||
(".//li/p/a/span", 'No.2', True),
|
||||
],
|
||||
}))
|
||||
@pytest.mark.sphinx(
|
||||
'html', testroot='add_enumerable_node',
|
||||
srcdir='test_enumerable_node',
|
||||
)
|
||||
@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
|
||||
reason='docutils-0.13 or above is required')
|
||||
@pytest.mark.sphinx('html', testroot='add_enumerable_node',
|
||||
srcdir='test_enumerable_node')
|
||||
def test_enumerable_node(app, cached_etree_parse, fname, expect):
|
||||
app.build()
|
||||
check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect)
|
||||
|
@ -1,367 +0,0 @@
|
||||
"""
|
||||
test_build_html5
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Test the HTML5 writer and check output against XPath.
|
||||
|
||||
This code is digest to reduce test running time.
|
||||
Complete test code is here:
|
||||
|
||||
https://github.com/sphinx-doc/sphinx/pull/2805/files
|
||||
|
||||
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import re
|
||||
from hashlib import md5
|
||||
|
||||
import pytest
|
||||
from html5lib import HTMLParser
|
||||
from test_build_html import flat_dict, tail_check, check_xpath
|
||||
|
||||
from sphinx.util.docutils import is_html5_writer_available
|
||||
|
||||
|
||||
etree_cache = {}
|
||||
|
||||
|
||||
@pytest.mark.skipif(not is_html5_writer_available(), reason='HTML5 writer is not available')
|
||||
@pytest.fixture(scope='module')
|
||||
def cached_etree_parse():
|
||||
def parse(fname):
|
||||
if fname in etree_cache:
|
||||
return etree_cache[fname]
|
||||
with (fname).open('rb') as fp:
|
||||
etree = HTMLParser(namespaceHTMLElements=False).parse(fp)
|
||||
etree_cache.clear()
|
||||
etree_cache[fname] = etree
|
||||
return etree
|
||||
yield parse
|
||||
etree_cache.clear()
|
||||
|
||||
|
||||
@pytest.mark.skipif(not is_html5_writer_available(), reason='HTML5 writer is not available')
|
||||
@pytest.mark.parametrize("fname,expect", flat_dict({
|
||||
'images.html': [
|
||||
(".//img[@src='_images/img.png']", ''),
|
||||
(".//img[@src='_images/img1.png']", ''),
|
||||
(".//img[@src='_images/simg.png']", ''),
|
||||
(".//img[@src='_images/svgimg.svg']", ''),
|
||||
(".//a[@href='_sources/images.txt']", ''),
|
||||
],
|
||||
'subdir/images.html': [
|
||||
(".//img[@src='../_images/img1.png']", ''),
|
||||
(".//img[@src='../_images/rimg.png']", ''),
|
||||
],
|
||||
'subdir/includes.html': [
|
||||
(".//a[@class='reference download internal']", ''),
|
||||
(".//img[@src='../_images/img.png']", ''),
|
||||
(".//p", 'This is an include file.'),
|
||||
(".//pre/span", 'line 1'),
|
||||
(".//pre/span", 'line 2'),
|
||||
],
|
||||
'includes.html': [
|
||||
(".//pre", 'Max Strauß'),
|
||||
(".//a[@class='reference download internal']", ''),
|
||||
(".//pre/span", '"quotes"'),
|
||||
(".//pre/span", "'included'"),
|
||||
(".//pre/span[@class='s2']", 'üöä'),
|
||||
(".//div[@class='inc-pyobj1 highlight-text notranslate']//pre",
|
||||
r'^class Foo:\n pass\n\s*$'),
|
||||
(".//div[@class='inc-pyobj2 highlight-text notranslate']//pre",
|
||||
r'^ def baz\(\):\n pass\n\s*$'),
|
||||
(".//div[@class='inc-lines highlight-text notranslate']//pre",
|
||||
r'^class Foo:\n pass\nclass Bar:\n$'),
|
||||
(".//div[@class='inc-startend highlight-text notranslate']//pre",
|
||||
'^foo = "Including Unicode characters: üöä"\\n$'),
|
||||
(".//div[@class='inc-preappend highlight-text notranslate']//pre",
|
||||
r'(?m)^START CODE$'),
|
||||
(".//div[@class='inc-pyobj-dedent highlight-python notranslate']//span",
|
||||
r'def'),
|
||||
(".//div[@class='inc-tab3 highlight-text notranslate']//pre",
|
||||
r'-| |-'),
|
||||
(".//div[@class='inc-tab8 highlight-python notranslate']//pre/span",
|
||||
r'-| |-'),
|
||||
],
|
||||
'autodoc.html': [
|
||||
(".//dt[@id='autodoc_target.Class']", ''),
|
||||
(".//dt[@id='autodoc_target.function']/em", r'\*\*kwds'),
|
||||
(".//dd/p", r'Return spam\.'),
|
||||
],
|
||||
'extapi.html': [
|
||||
(".//strong", 'from class: Bar'),
|
||||
],
|
||||
'markup.html': [
|
||||
(".//title", 'set by title directive'),
|
||||
(".//p/em", 'Section author: Georg Brandl'),
|
||||
(".//p/em", 'Module author: Georg Brandl'),
|
||||
# created by the meta directive
|
||||
(".//meta[@name='author'][@content='Me']", ''),
|
||||
(".//meta[@name='keywords'][@content='docs, sphinx']", ''),
|
||||
# a label created by ``.. _label:``
|
||||
(".//div[@id='label']", ''),
|
||||
# code with standard code blocks
|
||||
(".//pre", '^some code$'),
|
||||
# an option list
|
||||
(".//span[@class='option']", '--help'),
|
||||
# admonitions
|
||||
(".//p[@class='admonition-title']", 'My Admonition'),
|
||||
(".//div[@class='admonition note']/p", 'Note text.'),
|
||||
(".//div[@class='admonition warning']/p", 'Warning text.'),
|
||||
# inline markup
|
||||
(".//li/p/strong", r'^command\\n$'),
|
||||
(".//li/p/strong", r'^program\\n$'),
|
||||
(".//li/p/em", r'^dfn\\n$'),
|
||||
(".//li/p/kbd", r'^kbd\\n$'),
|
||||
(".//li/p/span", 'File \N{TRIANGULAR BULLET} Close'),
|
||||
(".//li/p/code/span[@class='pre']", '^a/$'),
|
||||
(".//li/p/code/em/span[@class='pre']", '^varpart$'),
|
||||
(".//li/p/code/em/span[@class='pre']", '^i$'),
|
||||
(".//a[@href='https://www.python.org/dev/peps/pep-0008']"
|
||||
"[@class='pep reference external']/strong", 'PEP 8'),
|
||||
(".//a[@href='https://www.python.org/dev/peps/pep-0008']"
|
||||
"[@class='pep reference external']/strong",
|
||||
'Python Enhancement Proposal #8'),
|
||||
(".//a[@href='https://tools.ietf.org/html/rfc1.html']"
|
||||
"[@class='rfc reference external']/strong", 'RFC 1'),
|
||||
(".//a[@href='https://tools.ietf.org/html/rfc1.html']"
|
||||
"[@class='rfc reference external']/strong", 'Request for Comments #1'),
|
||||
(".//a[@href='objects.html#envvar-HOME']"
|
||||
"[@class='reference internal']/code/span[@class='pre']", 'HOME'),
|
||||
(".//a[@href='#with']"
|
||||
"[@class='reference internal']/code/span[@class='pre']", '^with$'),
|
||||
(".//a[@href='#grammar-token-try-stmt']"
|
||||
"[@class='reference internal']/code/span", '^statement$'),
|
||||
(".//a[@href='#some-label'][@class='reference internal']/span", '^here$'),
|
||||
(".//a[@href='#some-label'][@class='reference internal']/span", '^there$'),
|
||||
(".//a[@href='subdir/includes.html']"
|
||||
"[@class='reference internal']/span", 'Including in subdir'),
|
||||
(".//a[@href='objects.html#cmdoption-python-c']"
|
||||
"[@class='reference internal']/code/span[@class='pre']", '-c'),
|
||||
# abbreviations
|
||||
(".//abbr[@title='abbreviation']", '^abbr$'),
|
||||
# version stuff
|
||||
(".//div[@class='versionadded']/p/span", 'New in version 0.6: '),
|
||||
(".//div[@class='versionadded']/p/span",
|
||||
tail_check('First paragraph of versionadded')),
|
||||
(".//div[@class='versionchanged']/p/span",
|
||||
tail_check('First paragraph of versionchanged')),
|
||||
(".//div[@class='versionchanged']/p",
|
||||
'Second paragraph of versionchanged'),
|
||||
# footnote reference
|
||||
(".//a[@class='footnote-reference brackets']", r'1'),
|
||||
# created by reference lookup
|
||||
(".//a[@href='index.html#ref1']", ''),
|
||||
# ``seealso`` directive
|
||||
(".//div/p[@class='admonition-title']", 'See also'),
|
||||
# a ``hlist`` directive
|
||||
(".//table[@class='hlist']/tbody/tr/td/ul/li/p", '^This$'),
|
||||
# a ``centered`` directive
|
||||
(".//p[@class='centered']/strong", 'LICENSE'),
|
||||
# a glossary
|
||||
(".//dl/dt[@id='term-boson']", 'boson'),
|
||||
# a production list
|
||||
(".//pre/strong", 'try_stmt'),
|
||||
(".//pre/a[@href='#grammar-token-try1-stmt']/code/span", 'try1_stmt'),
|
||||
# tests for ``only`` directive
|
||||
(".//p", 'A global substitution.'),
|
||||
(".//p", 'In HTML.'),
|
||||
(".//p", 'In both.'),
|
||||
(".//p", 'Always present'),
|
||||
# tests for ``any`` role
|
||||
(".//a[@href='#with']/span", 'headings'),
|
||||
(".//a[@href='objects.html#func_without_body']/code/span", 'objects'),
|
||||
# tests for numeric labels
|
||||
(".//a[@href='#id1'][@class='reference internal']/span", 'Testing various markup'),
|
||||
],
|
||||
'objects.html': [
|
||||
(".//dt[@id='mod.Cls.meth1']", ''),
|
||||
(".//dt[@id='errmod.Error']", ''),
|
||||
(".//dt/code", r'long\(parameter,\s* list\)'),
|
||||
(".//dt/code", 'another one'),
|
||||
(".//a[@href='#mod.Cls'][@class='reference internal']", ''),
|
||||
(".//dl[@class='userdesc']", ''),
|
||||
(".//dt[@id='userdesc-myobj']", ''),
|
||||
(".//a[@href='#userdesc-myobj'][@class='reference internal']", ''),
|
||||
# docfields
|
||||
(".//a[@class='reference internal'][@href='#TimeInt']/em", 'TimeInt'),
|
||||
(".//a[@class='reference internal'][@href='#Time']", 'Time'),
|
||||
(".//a[@class='reference internal'][@href='#errmod.Error']/strong", 'Error'),
|
||||
# C references
|
||||
(".//span[@class='pre']", 'CFunction()'),
|
||||
(".//a[@href='#c.Sphinx_DoSomething']", ''),
|
||||
(".//a[@href='#c.SphinxStruct.member']", ''),
|
||||
(".//a[@href='#c.SPHINX_USE_PYTHON']", ''),
|
||||
(".//a[@href='#c.SphinxType']", ''),
|
||||
(".//a[@href='#c.sphinx_global']", ''),
|
||||
# test global TOC created by toctree()
|
||||
(".//ul[@class='current']/li[@class='toctree-l1 current']/a[@href='#']",
|
||||
'Testing object descriptions'),
|
||||
(".//li[@class='toctree-l1']/a[@href='markup.html']",
|
||||
'Testing various markup'),
|
||||
# test unknown field names
|
||||
(".//dt[@class='field-odd']", 'Field_name'),
|
||||
(".//dt[@class='field-even']", 'Field_name all lower'),
|
||||
(".//dt[@class='field-odd']", 'FIELD_NAME'),
|
||||
(".//dt[@class='field-even']", 'FIELD_NAME ALL CAPS'),
|
||||
(".//dt[@class='field-odd']", 'Field_Name'),
|
||||
(".//dt[@class='field-even']", 'Field_Name All Word Caps'),
|
||||
(".//dt[@class='field-odd']", 'Field_name'),
|
||||
(".//dt[@class='field-even']", 'Field_name First word cap'),
|
||||
(".//dt[@class='field-odd']", 'FIELd_name'),
|
||||
(".//dt[@class='field-even']", 'FIELd_name PARTial caps'),
|
||||
# custom sidebar
|
||||
(".//h4", 'Custom sidebar'),
|
||||
# docfields
|
||||
(".//dd[@class='field-odd']/p/strong", '^moo$'),
|
||||
(".//dd[@class='field-odd']/p/strong", tail_check(r'\(Moo\) .* Moo')),
|
||||
(".//dd[@class='field-odd']/ul/li/p/strong", '^hour$'),
|
||||
(".//dd[@class='field-odd']/ul/li/p/em", '^DuplicateType$'),
|
||||
(".//dd[@class='field-odd']/ul/li/p/em", tail_check(r'.* Some parameter')),
|
||||
# others
|
||||
(".//a[@class='reference internal'][@href='#cmdoption-perl-arg-p']/code/span",
|
||||
'perl'),
|
||||
(".//a[@class='reference internal'][@href='#cmdoption-perl-arg-p']/code/span",
|
||||
'\\+p'),
|
||||
(".//a[@class='reference internal'][@href='#cmdoption-perl-objc']/code/span",
|
||||
'--ObjC\\+\\+'),
|
||||
(".//a[@class='reference internal'][@href='#cmdoption-perl-plugin-option']/code/span",
|
||||
'--plugin.option'),
|
||||
(".//a[@class='reference internal'][@href='#cmdoption-perl-arg-create-auth-token']"
|
||||
"/code/span",
|
||||
'create-auth-token'),
|
||||
(".//a[@class='reference internal'][@href='#cmdoption-perl-arg-arg']/code/span",
|
||||
'arg'),
|
||||
(".//a[@class='reference internal'][@href='#cmdoption-hg-arg-commit']/code/span",
|
||||
'hg'),
|
||||
(".//a[@class='reference internal'][@href='#cmdoption-hg-arg-commit']/code/span",
|
||||
'commit'),
|
||||
(".//a[@class='reference internal'][@href='#cmdoption-git-commit-p']/code/span",
|
||||
'git'),
|
||||
(".//a[@class='reference internal'][@href='#cmdoption-git-commit-p']/code/span",
|
||||
'commit'),
|
||||
(".//a[@class='reference internal'][@href='#cmdoption-git-commit-p']/code/span",
|
||||
'-p'),
|
||||
],
|
||||
'index.html': [
|
||||
(".//meta[@name='hc'][@content='hcval']", ''),
|
||||
(".//meta[@name='hc_co'][@content='hcval_co']", ''),
|
||||
(".//dt[@class='label']/span[@class='brackets']", r'Ref1'),
|
||||
(".//dt[@class='label']", ''),
|
||||
(".//li[@class='toctree-l1']/a", 'Testing various markup'),
|
||||
(".//li[@class='toctree-l2']/a", 'Inline markup'),
|
||||
(".//title", 'Sphinx <Tests>'),
|
||||
(".//div[@class='footer']", 'Georg Brandl & Team'),
|
||||
(".//a[@href='http://python.org/']"
|
||||
"[@class='reference external']", ''),
|
||||
(".//li/p/a[@href='genindex.html']/span", 'Index'),
|
||||
(".//li/p/a[@href='py-modindex.html']/span", 'Module Index'),
|
||||
(".//li/p/a[@href='search.html']/span", 'Search Page'),
|
||||
# custom sidebar only for contents
|
||||
(".//h4", 'Contents sidebar'),
|
||||
# custom JavaScript
|
||||
(".//script[@src='file://moo.js']", ''),
|
||||
# URL in contents
|
||||
(".//a[@class='reference external'][@href='http://sphinx-doc.org/']",
|
||||
'http://sphinx-doc.org/'),
|
||||
(".//a[@class='reference external'][@href='http://sphinx-doc.org/latest/']",
|
||||
'Latest reference'),
|
||||
# Indirect hyperlink targets across files
|
||||
(".//a[@href='markup.html#some-label'][@class='reference internal']/span",
|
||||
'^indirect hyperref$'),
|
||||
],
|
||||
'bom.html': [
|
||||
(".//title", " File with UTF-8 BOM"),
|
||||
],
|
||||
'extensions.html': [
|
||||
(".//a[@href='http://python.org/dev/']", "http://python.org/dev/"),
|
||||
(".//a[@href='http://bugs.python.org/issue1000']", "issue 1000"),
|
||||
(".//a[@href='http://bugs.python.org/issue1042']", "explicit caption"),
|
||||
],
|
||||
'genindex.html': [
|
||||
# index entries
|
||||
(".//a/strong", "Main"),
|
||||
(".//a/strong", "[1]"),
|
||||
(".//a/strong", "Other"),
|
||||
(".//a", "entry"),
|
||||
(".//li/a", "double"),
|
||||
],
|
||||
'footnote.html': [
|
||||
(".//a[@class='footnote-reference brackets'][@href='#id9'][@id='id1']", r"1"),
|
||||
(".//a[@class='footnote-reference brackets'][@href='#id10'][@id='id2']", r"2"),
|
||||
(".//a[@class='footnote-reference brackets'][@href='#foo'][@id='id3']", r"3"),
|
||||
(".//a[@class='reference internal'][@href='#bar'][@id='id4']", r"\[bar\]"),
|
||||
(".//a[@class='reference internal'][@href='#baz-qux'][@id='id5']", r"\[baz_qux\]"),
|
||||
(".//a[@class='footnote-reference brackets'][@href='#id11'][@id='id6']", r"4"),
|
||||
(".//a[@class='footnote-reference brackets'][@href='#id12'][@id='id7']", r"5"),
|
||||
(".//a[@class='fn-backref'][@href='#id1']", r"1"),
|
||||
(".//a[@class='fn-backref'][@href='#id2']", r"2"),
|
||||
(".//a[@class='fn-backref'][@href='#id3']", r"3"),
|
||||
(".//a[@class='fn-backref'][@href='#id4']", r"bar"),
|
||||
(".//a[@class='fn-backref'][@href='#id5']", r"baz_qux"),
|
||||
(".//a[@class='fn-backref'][@href='#id6']", r"4"),
|
||||
(".//a[@class='fn-backref'][@href='#id7']", r"5"),
|
||||
(".//a[@class='fn-backref'][@href='#id8']", r"6"),
|
||||
],
|
||||
'otherext.html': [
|
||||
(".//h1", "Generated section"),
|
||||
(".//a[@href='_sources/otherext.foo.txt']", ''),
|
||||
]
|
||||
}))
|
||||
@pytest.mark.sphinx('html', tags=['testtag'], confoverrides={
|
||||
'html_context.hckey_co': 'hcval_co',
|
||||
'html_experimental_html5_writer': True})
|
||||
@pytest.mark.test_params(shared_result='test_build_html5_output')
|
||||
def test_html5_output(app, cached_etree_parse, fname, expect):
|
||||
app.build()
|
||||
print(app.outdir / fname)
|
||||
check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect)
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', tags=['testtag'], confoverrides={
|
||||
'html_context.hckey_co': 'hcval_co',
|
||||
'html_experimental_html5_writer': True})
|
||||
@pytest.mark.test_params(shared_result='test_build_html_output')
|
||||
def test_html_download(app):
|
||||
app.build()
|
||||
|
||||
# subdir/includes.html
|
||||
result = (app.outdir / 'subdir' / 'includes.html').text()
|
||||
pattern = ('<a class="reference download internal" download="" '
|
||||
'href="../(_downloads/.*/img.png)">')
|
||||
matched = re.search(pattern, result)
|
||||
assert matched
|
||||
assert (app.outdir / matched.group(1)).exists()
|
||||
filename = matched.group(1)
|
||||
|
||||
# includes.html
|
||||
result = (app.outdir / 'includes.html').text()
|
||||
pattern = ('<a class="reference download internal" download="" '
|
||||
'href="(_downloads/.*/img.png)">')
|
||||
matched = re.search(pattern, result)
|
||||
assert matched
|
||||
assert (app.outdir / matched.group(1)).exists()
|
||||
assert matched.group(1) == filename
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='roles-download',
|
||||
confoverrides={'html_experimental_html5_writer': True})
|
||||
def test_html_download_role(app, status, warning):
|
||||
app.build()
|
||||
digest = md5((app.srcdir / 'dummy.dat').encode()).hexdigest()
|
||||
assert (app.outdir / '_downloads' / digest / 'dummy.dat').exists()
|
||||
|
||||
content = (app.outdir / 'index.html').text()
|
||||
assert (('<li><p><a class="reference download internal" download="" '
|
||||
'href="_downloads/%s/dummy.dat">'
|
||||
'<code class="xref download docutils literal notranslate">'
|
||||
'<span class="pre">dummy.dat</span></code></a></p></li>' % digest)
|
||||
in content)
|
||||
assert ('<li><p><code class="xref download docutils literal notranslate">'
|
||||
'<span class="pre">not_found.dat</span></code></p></li>' in content)
|
||||
assert ('<li><p><a class="reference download external" download="" '
|
||||
'href="http://www.sphinx-doc.org/en/master/_static/sphinxheader.png">'
|
||||
'<code class="xref download docutils literal notranslate">'
|
||||
'<span class="pre">Sphinx</span> <span class="pre">logo</span>'
|
||||
'</code></a></p></li>' in content)
|
@ -1,63 +0,0 @@
|
||||
"""
|
||||
test_build_htmlhelp
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Test the HTML Help builder and check output against XPath.
|
||||
|
||||
:copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
import pytest
|
||||
|
||||
from sphinx.builders.htmlhelp import chm_htmlescape
|
||||
|
||||
from sphinx.builders.htmlhelp import default_htmlhelp_basename
|
||||
from sphinx.config import Config
|
||||
|
||||
|
||||
@pytest.mark.sphinx('htmlhelp', testroot='basic')
|
||||
def test_default_htmlhelp_file_suffix(app, warning):
|
||||
assert app.builder.out_suffix == '.html'
|
||||
|
||||
|
||||
@pytest.mark.sphinx('htmlhelp', testroot='basic',
|
||||
confoverrides={'htmlhelp_file_suffix': '.htm'})
|
||||
def test_htmlhelp_file_suffix(app, warning):
|
||||
assert app.builder.out_suffix == '.htm'
|
||||
|
||||
|
||||
def test_default_htmlhelp_basename():
|
||||
config = Config({'project': 'Sphinx Documentation'})
|
||||
config.init_values()
|
||||
assert default_htmlhelp_basename(config) == 'sphinxdoc'
|
||||
|
||||
|
||||
@pytest.mark.sphinx('htmlhelp', testroot='build-htmlhelp')
|
||||
def test_chm(app):
|
||||
app.build()
|
||||
|
||||
# check .hhk file
|
||||
outname = app.builder.config.htmlhelp_basename
|
||||
hhk_path = str(app.outdir / outname + '.hhk')
|
||||
|
||||
with open(hhk_path, 'rb') as f:
|
||||
data = f.read()
|
||||
m = re.search(br'&#[xX][0-9a-fA-F]+;', data)
|
||||
assert m is None, 'Hex escaping exists in .hhk file: ' + str(m.group(0))
|
||||
|
||||
|
||||
def test_chm_htmlescape():
|
||||
assert chm_htmlescape('Hello world') == 'Hello world'
|
||||
assert chm_htmlescape(u'Unicode 文字') == u'Unicode 文字'
|
||||
assert chm_htmlescape('E') == '&#x45'
|
||||
|
||||
assert chm_htmlescape('<Hello> "world"') == '<Hello> "world"'
|
||||
assert chm_htmlescape('<Hello> "world"', True) == '<Hello> "world"'
|
||||
assert chm_htmlescape('<Hello> "world"', False) == '<Hello> "world"'
|
||||
|
||||
assert chm_htmlescape("Hello 'world'") == "Hello 'world'"
|
||||
assert chm_htmlescape("Hello 'world'", True) == "Hello 'world'"
|
||||
assert chm_htmlescape("Hello 'world'", False) == "Hello 'world'"
|
@ -24,7 +24,10 @@ def test_defaults(app, status, warning):
|
||||
assert "Anchor 'does-not-exist' not found" in content
|
||||
# looking for non-existent URL should fail
|
||||
assert " Max retries exceeded with url: /doesnotexist" in content
|
||||
assert len(content.splitlines()) == 3
|
||||
# images should fail
|
||||
assert "Not Found for url: http://example.com/image.png" in content
|
||||
assert "Not Found for url: http://example.com/image2.png" in content
|
||||
assert len(content.splitlines()) == 5
|
||||
|
||||
|
||||
@pytest.mark.sphinx(
|
||||
@ -32,7 +35,9 @@ def test_defaults(app, status, warning):
|
||||
confoverrides={'linkcheck_anchors_ignore': ["^!", "^top$"],
|
||||
'linkcheck_ignore': [
|
||||
'https://localhost:7777/doesnotexist',
|
||||
'http://www.sphinx-doc.org/en/1.7/intro.html#']
|
||||
'http://www.sphinx-doc.org/en/1.7/intro.html#',
|
||||
'http://example.com/image.png',
|
||||
'http://example.com/image2.png']
|
||||
})
|
||||
def test_anchors_ignored(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
@ -10,11 +10,12 @@
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.sphinx('dummy', srcdir="test_builder")
|
||||
@pytest.mark.sphinx('dummy', srcdir="test_builder", freshenv=True)
|
||||
def test_incremental_reading(app):
|
||||
# first reading
|
||||
updated = app.builder.read()
|
||||
assert set(updated) == app.env.found_docs == set(app.env.all_docs)
|
||||
assert updated == sorted(updated) # sorted by alphanumeric
|
||||
|
||||
# test if exclude_patterns works ok
|
||||
assert 'subdir/excluded' not in app.env.found_docs
|
||||
@ -27,29 +28,20 @@ def test_incremental_reading(app):
|
||||
# second reading
|
||||
updated = app.builder.read()
|
||||
|
||||
# "includes" and "images" are in there because they contain references
|
||||
# to nonexisting downloadable or image files, which are given another
|
||||
# chance to exist
|
||||
assert set(updated) == set(['index', 'new', 'includes', 'images'])
|
||||
assert set(updated) == set(['index', 'new'])
|
||||
assert 'autodoc' not in app.env.all_docs
|
||||
assert 'autodoc' not in app.env.found_docs
|
||||
|
||||
|
||||
@pytest.mark.sphinx('dummy')
|
||||
def test_env_read_docs(app):
|
||||
"""By default, docnames are read in alphanumeric order"""
|
||||
def on_env_read_docs_1(app, env, docnames):
|
||||
pass
|
||||
@pytest.mark.sphinx('dummy', testroot='warnings', freshenv=True)
|
||||
def test_incremental_reading_for_missing_files(app):
|
||||
# first reading
|
||||
updated = app.builder.read()
|
||||
assert set(updated) == app.env.found_docs == set(app.env.all_docs)
|
||||
|
||||
app.connect('env-before-read-docs', on_env_read_docs_1)
|
||||
# second reading
|
||||
updated = app.builder.read()
|
||||
|
||||
read_docnames = app.builder.read()
|
||||
assert len(read_docnames) > 2 and read_docnames == sorted(read_docnames)
|
||||
|
||||
def on_env_read_docs_2(app, env, docnames):
|
||||
docnames.remove('images')
|
||||
|
||||
app.connect('env-before-read-docs', on_env_read_docs_2)
|
||||
|
||||
read_docnames = app.builder.read()
|
||||
assert len(read_docnames) == 2
|
||||
# "index" is listed up to updated because it contains references
|
||||
# to nonexisting downloadable or image files
|
||||
assert set(updated) == set(['index'])
|
||||
|
@ -17,6 +17,7 @@ import sphinx.domains.cpp as cppDomain
|
||||
from sphinx import addnodes
|
||||
from sphinx.domains.cpp import DefinitionParser, DefinitionError, NoOldIdError
|
||||
from sphinx.domains.cpp import Symbol, _max_id, _id_prefix
|
||||
from sphinx.util import docutils
|
||||
|
||||
|
||||
def parse(name, string):
|
||||
@ -734,12 +735,14 @@ def test_build_domain_cpp_misuse_of_roles(app, status, warning):
|
||||
# TODO: properly check for the warnings we expect
|
||||
|
||||
|
||||
@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
|
||||
reason='docutils-0.13 or above is required')
|
||||
@pytest.mark.sphinx(testroot='domain-cpp', confoverrides={'add_function_parentheses': True})
|
||||
def test_build_domain_cpp_with_add_function_parentheses_is_True(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
def check(spec, text, file):
|
||||
pattern = '<li>%s<a .*?><code .*?><span .*?>%s</span></code></a></li>' % spec
|
||||
pattern = '<li><p>%s<a .*?><code .*?><span .*?>%s</span></code></a></p></li>' % spec
|
||||
res = re.search(pattern, text)
|
||||
if not res:
|
||||
print("Pattern\n\t%s\nnot found in %s" % (pattern, file))
|
||||
@ -775,13 +778,14 @@ def test_build_domain_cpp_with_add_function_parentheses_is_True(app, status, war
|
||||
check(s, t, f)
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='domain-cpp', confoverrides={
|
||||
'add_function_parentheses': False})
|
||||
@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
|
||||
reason='docutils-0.13 or above is required')
|
||||
@pytest.mark.sphinx(testroot='domain-cpp', confoverrides={'add_function_parentheses': False})
|
||||
def test_build_domain_cpp_with_add_function_parentheses_is_False(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
def check(spec, text, file):
|
||||
pattern = '<li>%s<a .*?><code .*?><span .*?>%s</span></code></a></li>' % spec
|
||||
pattern = '<li><p>%s<a .*?><code .*?><span .*?>%s</span></code></a></p></li>' % spec
|
||||
res = re.search(pattern, text)
|
||||
if not res:
|
||||
print("Pattern\n\t%s\nnot found in %s" % (pattern, file))
|
||||
|
@ -17,8 +17,6 @@ from sphinx.testing.comparer import PathComparer
|
||||
@pytest.mark.sphinx('dummy')
|
||||
def test_images(app):
|
||||
app.build()
|
||||
assert ('image file not readable: foo.png'
|
||||
in app._warning.getvalue())
|
||||
|
||||
tree = app.env.get_doctree('images')
|
||||
htmlbuilder = StandaloneHTMLBuilder(app)
|
||||
|
@ -49,7 +49,6 @@ def apidoc_params(request):
|
||||
def test_simple(make_app, apidoc):
|
||||
outdir = apidoc.outdir
|
||||
assert (outdir / 'conf.py').isfile()
|
||||
assert (outdir / 'autodoc_fodder.rst').isfile()
|
||||
assert (outdir / 'index.rst').isfile()
|
||||
|
||||
app = make_app('text', srcdir=outdir)
|
||||
@ -273,7 +272,6 @@ def test_excludes_module_should_not_be_skipped(apidoc):
|
||||
def test_multibyte_parameters(make_app, apidoc):
|
||||
outdir = apidoc.outdir
|
||||
assert (outdir / 'conf.py').isfile()
|
||||
assert (outdir / 'autodoc_fodder.rst').isfile()
|
||||
assert (outdir / 'index.rst').isfile()
|
||||
|
||||
conf_py = (outdir / 'conf.py').text()
|
||||
|
@ -16,6 +16,21 @@ import pytest
|
||||
from sphinx.ext.autodoc.importer import _MockModule, _MockObject, mock
|
||||
|
||||
|
||||
def test_MockModule():
|
||||
mock = _MockModule('mocked_module', None)
|
||||
assert isinstance(mock.some_attr, _MockObject)
|
||||
assert isinstance(mock.some_method, _MockObject)
|
||||
assert isinstance(mock.attr1.attr2, _MockObject)
|
||||
assert isinstance(mock.attr1.attr2.meth(), _MockObject)
|
||||
|
||||
assert repr(mock.some_attr) == 'mocked_module.some_attr'
|
||||
assert repr(mock.some_method) == 'mocked_module.some_method'
|
||||
assert repr(mock.attr1.attr2) == 'mocked_module.attr1.attr2'
|
||||
assert repr(mock.attr1.attr2.meth) == 'mocked_module.attr1.attr2.meth'
|
||||
|
||||
assert repr(mock) == 'mocked_module'
|
||||
|
||||
|
||||
def test_MockObject():
|
||||
mock = _MockObject()
|
||||
assert isinstance(mock.some_attr, _MockObject)
|
||||
@ -25,6 +40,7 @@ def test_MockObject():
|
||||
|
||||
class SubClass(mock.SomeClass):
|
||||
"""docstring of SubClass"""
|
||||
|
||||
def method(self):
|
||||
return "string"
|
||||
|
||||
|
@ -12,37 +12,82 @@ import re
|
||||
|
||||
import pytest
|
||||
|
||||
from sphinx.util import docutils
|
||||
|
||||
|
||||
@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
|
||||
reason='docutils-0.13 or above is required')
|
||||
@pytest.mark.sphinx('html', testroot='ext-autosectionlabel')
|
||||
def test_autosectionlabel_html(app, status, warning):
|
||||
def test_autosectionlabel_html(app, status, warning, skipped_labels=False):
|
||||
app.builder.build_all()
|
||||
|
||||
content = (app.outdir / 'index.html').text()
|
||||
html = ('<li><a class="reference internal" href="#introduce-of-sphinx">'
|
||||
'<span class=".*?">Introduce of Sphinx</span></a></li>')
|
||||
html = ('<li><p><a class="reference internal" href="#introduce-of-sphinx">'
|
||||
'<span class=".*?">Introduce of Sphinx</span></a></p></li>')
|
||||
assert re.search(html, content, re.S)
|
||||
|
||||
html = ('<li><a class="reference internal" href="#installation">'
|
||||
'<span class="std std-ref">Installation</span></a></li>')
|
||||
html = ('<li><p><a class="reference internal" href="#installation">'
|
||||
'<span class="std std-ref">Installation</span></a></p></li>')
|
||||
assert re.search(html, content, re.S)
|
||||
|
||||
html = ('<li><a class="reference internal" href="#for-windows-users">'
|
||||
'<span class="std std-ref">For Windows users</span></a></li>')
|
||||
html = ('<li><p><a class="reference internal" href="#for-windows-users">'
|
||||
'<span class="std std-ref">For Windows users</span></a></p></li>')
|
||||
assert re.search(html, content, re.S)
|
||||
|
||||
html = ('<li><a class="reference internal" href="#for-unix-users">'
|
||||
'<span class="std std-ref">For UNIX users</span></a></li>')
|
||||
html = ('<li><p><a class="reference internal" href="#for-unix-users">'
|
||||
'<span class="std std-ref">For UNIX users</span></a></p></li>')
|
||||
assert re.search(html, content, re.S)
|
||||
|
||||
html = ('<li><p><a class="reference internal" href="#linux">'
|
||||
'<span class="std std-ref">Linux</span></a></p></li>')
|
||||
assert re.search(html, content, re.S)
|
||||
|
||||
html = ('<li><p><a class="reference internal" href="#freebsd">'
|
||||
'<span class="std std-ref">FreeBSD</span></a></p></li>')
|
||||
assert re.search(html, content, re.S)
|
||||
|
||||
# for smart_quotes (refs: #4027)
|
||||
html = ('<li><a class="reference internal" '
|
||||
html = ('<li><p><a class="reference internal" '
|
||||
'href="#this-one-s-got-an-apostrophe">'
|
||||
'<span class="std std-ref">This one’s got an apostrophe'
|
||||
'</span></a></li>')
|
||||
'</span></a></p></li>')
|
||||
assert re.search(html, content, re.S)
|
||||
|
||||
|
||||
# Re-use test definition from above, just change the test root directory
|
||||
@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
|
||||
reason='docutils-0.13 or above is required')
|
||||
@pytest.mark.sphinx('html', testroot='ext-autosectionlabel-prefix-document')
|
||||
def test_autosectionlabel_prefix_document_html(app, status, warning):
|
||||
return test_autosectionlabel_html(app, status, warning)
|
||||
test_autosectionlabel_html(app, status, warning)
|
||||
|
||||
|
||||
@pytest.mark.skipif(docutils.__version_info__ < (0, 13),
|
||||
reason='docutils-0.13 or above is required')
|
||||
@pytest.mark.sphinx('html', testroot='ext-autosectionlabel',
|
||||
confoverrides={'autosectionlabel_maxdepth': 3})
|
||||
def test_autosectionlabel_maxdepth(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
content = (app.outdir / 'index.html').text()
|
||||
|
||||
# depth: 1
|
||||
html = ('<li><p><a class="reference internal" href="#test-ext-autosectionlabel">'
|
||||
'<span class=".*?">test-ext-autosectionlabel</span></a></p></li>')
|
||||
assert re.search(html, content, re.S)
|
||||
|
||||
# depth: 2
|
||||
html = ('<li><p><a class="reference internal" href="#installation">'
|
||||
'<span class="std std-ref">Installation</span></a></p></li>')
|
||||
assert re.search(html, content, re.S)
|
||||
|
||||
# depth: 3
|
||||
html = ('<li><p><a class="reference internal" href="#for-windows-users">'
|
||||
'<span class="std std-ref">For Windows users</span></a></p></li>')
|
||||
assert re.search(html, content, re.S)
|
||||
|
||||
# depth: 4
|
||||
html = '<li><p><span class="xref std std-ref">Linux</span></p></li>'
|
||||
assert re.search(html, content, re.S)
|
||||
|
||||
assert 'WARNING: undefined label: linux' in warning.getvalue()
|
||||
|
@ -8,6 +8,7 @@
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from io import StringIO
|
||||
|
||||
import pytest
|
||||
@ -203,7 +204,7 @@ def test_autosummary_latex_table_colspec(app, status, warning):
|
||||
result = (app.outdir / 'python.tex').text(encoding='utf8')
|
||||
print(status.getvalue())
|
||||
print(warning.getvalue())
|
||||
assert r'\begin{longtable}{\X{1}{2}\X{1}{2}}' in result
|
||||
assert r'\begin{longtable}[c]{\X{1}{2}\X{1}{2}}' in result
|
||||
assert r'p{0.5\linewidth}' not in result
|
||||
|
||||
|
||||
@ -229,3 +230,15 @@ def test_import_by_name():
|
||||
assert obj == sphinx.ext.autosummary.Autosummary.get_items
|
||||
assert parent is sphinx.ext.autosummary.Autosummary
|
||||
assert modname == 'sphinx.ext.autosummary'
|
||||
|
||||
|
||||
@pytest.mark.sphinx('dummy', testroot='ext-autosummary-mock_imports')
|
||||
def test_autosummary_mock_imports(app, status, warning):
|
||||
try:
|
||||
app.build()
|
||||
assert warning.getvalue() == ''
|
||||
|
||||
# generated/foo is generated successfully
|
||||
assert app.env.get_doctree('generated/foo')
|
||||
finally:
|
||||
sys.modules.pop('foo', None) # unload foo module
|
||||
|
@ -15,3 +15,18 @@ import pytest
|
||||
def test_githubpages(app, status, warning):
|
||||
app.builder.build_all()
|
||||
assert (app.outdir / '.nojekyll').exists()
|
||||
assert not (app.outdir / 'CNAME').exists()
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-githubpages', confoverrides={'html_baseurl': 'https://sphinx-doc.github.io'})
|
||||
def test_no_cname_for_github_io_domain(app, status, warning):
|
||||
app.builder.build_all()
|
||||
assert (app.outdir / '.nojekyll').exists()
|
||||
assert not (app.outdir / 'CNAME').exists()
|
||||
|
||||
|
||||
@pytest.mark.sphinx('html', testroot='ext-githubpages', confoverrides={'html_baseurl': 'https://sphinx-doc.org'})
|
||||
def test_cname_for_custom_domain(app, status, warning):
|
||||
app.builder.build_all()
|
||||
assert (app.outdir / '.nojekyll').exists()
|
||||
assert (app.outdir / 'CNAME').text() == 'sphinx-doc.org'
|
||||
|
@ -21,7 +21,7 @@ def test_graphviz_png_html(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
content = (app.outdir / 'index.html').text()
|
||||
html = (r'<div class="figure" .*?>\s*'
|
||||
html = (r'<div class="figure align-center" .*?>\s*'
|
||||
r'<div class="graphviz"><img .*?/></div>\s*<p class="caption">'
|
||||
r'<span class="caption-text">caption of graph</span>.*</p>\s*</div>')
|
||||
assert re.search(html, content, re.S)
|
||||
@ -52,7 +52,7 @@ def test_graphviz_svg_html(app, status, warning):
|
||||
|
||||
content = (app.outdir / 'index.html').text()
|
||||
|
||||
html = (r'<div class=\"figure\" .*?>\n'
|
||||
html = (r'<div class=\"figure align-center\" .*?>\n'
|
||||
r'<div class="graphviz"><object data=\".*\.svg\".*>\n'
|
||||
r'\s*<p class=\"warning\">digraph foo {\n'
|
||||
r'bar -> baz\n'
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user