Merge branch 'master' into simplify_conf.py

This commit is contained in:
Takeshi KOMIYA 2019-02-16 21:25:21 +09:00 committed by GitHub
commit 1ece29597e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
103 changed files with 1412 additions and 1414 deletions

View File

@ -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
View File

@ -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
--------

View File

@ -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;
}

View File

@ -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

View File

@ -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:

View File

@ -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.

View 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
-------------------------------

View File

@ -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

View File

@ -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).

View File

@ -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
---------------------

View File

@ -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.

View File

@ -18,6 +18,7 @@ install_requires = [
'sphinxcontrib-applehelp',
'sphinxcontrib-devhelp',
'sphinxcontrib-jsmath',
'sphinxcontrib-htmlhelp',
'sphinxcontrib-qthelp',
'Jinja2>=2.3',
'Pygments>=2.0',

View File

@ -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.

View File

@ -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

View File

@ -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):

View File

@ -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)

View File

@ -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&reg; 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: ``&#x27;`` -> ``&#39;``
html.escape() may generates a hex escaping ``&#x27;`` for single
quote ``'``, this wrapper fixes this.
"""
s = html.escape(s, quote)
s = s.replace('&#x27;', '&#39;') # 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',

View File

@ -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)

View File

@ -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())

View File

@ -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

View File

@ -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]

View File

@ -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]]

View File

@ -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

View File

@ -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'])

View File

@ -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

View File

@ -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]

View File

@ -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

View File

@ -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):

View File

@ -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(',')]

View File

@ -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):

View File

@ -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 {

View File

@ -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}

View File

@ -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}

View File

@ -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)

View File

@ -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,

View File

@ -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]

View File

@ -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

View 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&reg; 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>

View 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 %}

View 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

View File

@ -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;
}

View File

@ -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

View File

@ -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."""

View File

@ -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.

View File

@ -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

View File

@ -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:

View File

@ -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)

View File

@ -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]]]

View File

@ -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']:

View File

@ -1 +0,0 @@
project = 'test'

View File

@ -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

View File

@ -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

View File

@ -1,2 +1 @@
html_theme = 'classic'
exclude_patterns = ['_build']

View File

@ -1,2 +1 @@
html_theme = 'classic'
exclude_patterns = ['_build']

View File

@ -1,2 +1 @@
html_theme = 'classic'
exclude_patterns = ['_build']

View File

@ -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

View File

@ -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`

View File

@ -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`

View 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']

View File

@ -0,0 +1,6 @@
import unknown
class Foo(unknown.Class):
"""Foo class"""
pass

View File

@ -0,0 +1,7 @@
test-ext-autosummary-mock_imports
=================================
.. autosummary::
:toctree: generated
foo

View File

@ -1,2 +1 @@
html_theme = 'classic'
exclude_patterns = ['_build']

View File

@ -7,7 +7,7 @@ Empty cell
----------
.. list-table::
:header-rows: 1
- * un
*
* trois

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,2 +1 @@
html_theme = 'classic'
exclude_patterns = ['_build']

View File

@ -1,4 +1,3 @@
html_theme = 'classic'
exclude_patterns = ['_build']
latex_elements = {

View File

@ -1,2 +1 @@
html_theme = 'classic'
exclude_patterns = ['_build']

View File

@ -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

View File

@ -1,7 +0,0 @@
class MarkupError(object):
"""
.. note:: This is a docstring with a
small markup error which should have
correct location information.
"""

View File

@ -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

View File

@ -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
===============================

View File

@ -2,4 +2,4 @@
.. Paths in included files are relative to the file that
includes them
.. image:: ../root/img.png
.. image:: subdir/img.png

View File

@ -1,3 +1,2 @@
html_theme = 'classic'
exclude_patterns = ['_build']
templates_path = ['_templates']

View File

@ -1,2 +1 @@
html_theme = 'classic'
exclude_patterns = ['_build']

View File

@ -1,2 +1 @@
html_theme = 'classic'
exclude_patterns = ['_build']

View File

@ -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')

View File

@ -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,

View File

@ -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',
],
)

View File

@ -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()

View File

@ -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 : « Cest “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)

View File

@ -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)

View File

@ -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('&#x45') == '&amp;#x45'
assert chm_htmlescape('<Hello> "world"') == '&lt;Hello&gt; &quot;world&quot;'
assert chm_htmlescape('<Hello> "world"', True) == '&lt;Hello&gt; &quot;world&quot;'
assert chm_htmlescape('<Hello> "world"', False) == '&lt;Hello&gt; "world"'
assert chm_htmlescape("Hello 'world'") == "Hello &#39;world&#39;"
assert chm_htmlescape("Hello 'world'", True) == "Hello &#39;world&#39;"
assert chm_htmlescape("Hello 'world'", False) == "Hello 'world'"

View File

@ -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()

View File

@ -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'])

View File

@ -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))

View 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)

View File

@ -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()

View File

@ -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"

View File

@ -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 ones 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()

View File

@ -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

View File

@ -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'

View File

@ -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 -&gt; baz\n'

Some files were not shown because too many files have changed in this diff Show More