Merge branch '2.0'

This commit is contained in:
Takeshi KOMIYA 2019-11-30 21:52:42 +09:00
commit 15c266c445
33 changed files with 1309 additions and 2108 deletions

15
CHANGES
View File

@ -50,12 +50,15 @@ Deprecated
* ``sphinx.builders.gettext.POHEADER`` * ``sphinx.builders.gettext.POHEADER``
* ``sphinx.io.SphinxStandaloneReader.app`` * ``sphinx.io.SphinxStandaloneReader.app``
* ``sphinx.io.SphinxStandaloneReader.env`` * ``sphinx.io.SphinxStandaloneReader.env``
* ``sphinx.util.texescape.tex_escape_map``
* ``sphinx.util.texescape.tex_hl_escape_map_new``
Features added Features added
-------------- --------------
* #6707: C++, support bit-fields. * #6707: C++, support bit-fields.
* #267: html: Eliminate prompt characters of doctest block from copyable text * #267: html: Eliminate prompt characters of doctest block from copyable text
* #6548: html: Use favicon for OpenSearch if available
* #6729: html theme: agogo theme now supports ``rightsidebar`` option * #6729: html theme: agogo theme now supports ``rightsidebar`` option
* #6780: Add PEP-561 Support * #6780: Add PEP-561 Support
* #6762: latex: Allow to load additonal LaTeX packages via ``extrapackages`` key * #6762: latex: Allow to load additonal LaTeX packages via ``extrapackages`` key
@ -63,10 +66,14 @@ Features added
* #1331: Add new config variable: :confval:`user_agent` * #1331: Add new config variable: :confval:`user_agent`
* #6000: LaTeX: have backslash also be an inline literal word wrap break * #6000: LaTeX: have backslash also be an inline literal word wrap break
character character
* #4186: LaTeX: Support upLaTeX as a new :confval:`latex_engine` (experimental)
* #6812: Improve a warning message when extensions are not parallel safe * #6812: Improve a warning message when extensions are not parallel safe
* #6818: Improve Intersphinx performance for multiple remote inventories. * #6818: Improve Intersphinx performance for multiple remote inventories.
* #2546: apidoc: .so file support * #2546: apidoc: .so file support
* #6798: autosummary: emit ``autodoc-skip-member`` event on generating stub file
* #6483: i18n: make explicit titles in toctree translatable * #6483: i18n: make explicit titles in toctree translatable
* #6816: linkcheck: Add :confval:`linkcheck_auth` option to provide
authentication information when doing ``linkcheck`` builds
Bugs fixed Bugs fixed
---------- ----------
@ -80,6 +87,7 @@ Bugs fixed
supported LaTeX engines: ¶, §, €, ∞, ±, →, ‣, , superscript and subscript supported LaTeX engines: ¶, §, €, ∞, ±, →, ‣, , superscript and subscript
digits go through "as is" (as default OpenType font supports them) digits go through "as is" (as default OpenType font supports them)
* #6704: linkcheck: Be defensive and handle newly defined HTTP error code * #6704: linkcheck: Be defensive and handle newly defined HTTP error code
* #6806: linkcheck: Failure on parsing content
* #6655: image URLs containing ``data:`` causes gettext builder crashed * #6655: image URLs containing ``data:`` causes gettext builder crashed
* #6584: i18n: Error when compiling message catalogs on Hindi * #6584: i18n: Error when compiling message catalogs on Hindi
* #6718: i18n: KeyError is raised if section title and table title are same * #6718: i18n: KeyError is raised if section title and table title are same
@ -95,6 +103,13 @@ Bugs fixed
* #6809: LaTeX: code-block in a danger type admonition can easily spill over * #6809: LaTeX: code-block in a danger type admonition can easily spill over
bottom of page bottom of page
* #6793: texinfo: Code examples broken following "sidebar" * #6793: texinfo: Code examples broken following "sidebar"
* #6813: An orphan warning is emitted for included document on Windows. Thanks
to @drillan
* #6850: Fix smartypants module calls re.sub() with wrong options
* #6824: HTML search: If a search term is partially matched in the title and
fully matched in a text paragraph on the same page, the search does not
include this match.
* #6848: config.py shouldn't pop extensions from overrides
Testing Testing
-------- --------

View File

@ -41,6 +41,16 @@ The following is a list of deprecated interfaces.
- 4.0 - 4.0
- ``sphinx.io.SphinxStandaloneReader.setup()`` - ``sphinx.io.SphinxStandaloneReader.setup()``
* - ``sphinx.util.texescape.tex_escape_map``
- 2.3
- 4.0
- ``sphinx.util.texescape.escape()``
* - ``sphinx.util.texescape.tex_hl_escape_map_new``
- 2.3
- 4.0
- ``sphinx.util.texescape.hlescape()``
* - ``sphinx.domains.math.MathDomain.add_equation()`` * - ``sphinx.domains.math.MathDomain.add_equation()``
- 2.2 - 2.2
- 4.0 - 4.0

View File

@ -538,7 +538,7 @@ General configuration
directory pointed ``REQUESTS_CA_BUNDLE`` environment directory pointed ``REQUESTS_CA_BUNDLE`` environment
variable if ``tls_cacerts`` not set. variable if ``tls_cacerts`` not set.
.. _requests: http://docs.python-requests.org/en/master/ .. _requests: https://requests.readthedocs.io/en/master/
.. confval:: today .. confval:: today
today_fmt today_fmt
@ -662,12 +662,17 @@ documentation on :ref:`intl` for details.
Currently supported languages by Sphinx are: Currently supported languages by Sphinx are:
* ``ar`` -- Arabic
* ``bn`` -- Bengali * ``bn`` -- Bengali
* ``ca`` -- Catalan * ``ca`` -- Catalan
* ``cak`` -- Kaqchikel
* ``cs`` -- Czech * ``cs`` -- Czech
* ``cy`` -- Welsh
* ``da`` -- Danish * ``da`` -- Danish
* ``de`` -- German * ``de`` -- German
* ``el`` -- Greek
* ``en`` -- English * ``en`` -- English
* ``eo`` -- Esperanto
* ``es`` -- Spanish * ``es`` -- Spanish
* ``et`` -- Estonian * ``et`` -- Estonian
* ``eu`` -- Basque * ``eu`` -- Basque
@ -675,6 +680,7 @@ documentation on :ref:`intl` for details.
* ``fi`` -- Finnish * ``fi`` -- Finnish
* ``fr`` -- French * ``fr`` -- French
* ``he`` -- Hebrew * ``he`` -- Hebrew
* ``hi`` -- Hindi
* ``hr`` -- Croatian * ``hr`` -- Croatian
* ``hu`` -- Hungarian * ``hu`` -- Hungarian
* ``id`` -- Indonesian * ``id`` -- Indonesian
@ -688,15 +694,20 @@ documentation on :ref:`intl` for details.
* ``ne`` -- Nepali * ``ne`` -- Nepali
* ``nl`` -- Dutch * ``nl`` -- Dutch
* ``pl`` -- Polish * ``pl`` -- Polish
* ``pt`` -- Portuguese
* ``pt_BR`` -- Brazilian Portuguese * ``pt_BR`` -- Brazilian Portuguese
* ``pt_PT`` -- European Portuguese * ``pt_PT`` -- European Portuguese
* ``ro`` -- Romanian
* ``ru`` -- Russian * ``ru`` -- Russian
* ``si`` -- Sinhala * ``si`` -- Sinhala
* ``sk`` -- Slovak * ``sk`` -- Slovak
* ``sl`` -- Slovenian * ``sl`` -- Slovenian
* ``sr`` -- Serbian
* ``sv`` -- Swedish * ``sv`` -- Swedish
* ``ta`` -- Tamil
* ``tr`` -- Turkish * ``tr`` -- Turkish
* ``uk_UA`` -- Ukrainian * ``uk_UA`` -- Ukrainian
* ``ur`` -- Urdu
* ``vi`` -- Vietnamese * ``vi`` -- Vietnamese
* ``zh_CN`` -- Simplified Chinese * ``zh_CN`` -- Simplified Chinese
* ``zh_TW`` -- Traditional Chinese * ``zh_TW`` -- Traditional Chinese
@ -1836,6 +1847,7 @@ These options influence LaTeX output.
* ``'xelatex'`` -- XeLaTeX * ``'xelatex'`` -- XeLaTeX
* ``'lualatex'`` -- LuaLaTeX * ``'lualatex'`` -- LuaLaTeX
* ``'platex'`` -- pLaTeX (default if :confval:`language` is ``'ja'``) * ``'platex'`` -- pLaTeX (default if :confval:`language` is ``'ja'``)
* ``'uplatex'`` -- upLaTeX (experimental)
``'pdflatex'``\ 's support for Unicode characters is limited. ``'pdflatex'``\ 's support for Unicode characters is limited.
@ -1861,6 +1873,10 @@ These options influence LaTeX output.
Use ``xelatex`` by default for Greek documents. Use ``xelatex`` by default for Greek documents.
.. versionchanged:: 2.3
Add ``uplatex`` support.
Contrarily to :ref:`MathJaX math rendering in HTML output <math-support>`, Contrarily to :ref:`MathJaX math rendering in HTML output <math-support>`,
LaTeX requires some extra configuration to support Unicode literals in LaTeX requires some extra configuration to support Unicode literals in
:rst:dir:`math`: the only comprehensive solution (as far as we know) is to :rst:dir:`math`: the only comprehensive solution (as far as we know) is to
@ -2364,6 +2380,34 @@ Options for the linkcheck builder
.. versionadded:: 1.5 .. versionadded:: 1.5
.. confval:: linkcheck_auth
Pass authentication information when doing a ``linkcheck`` build.
A list of ``(regex_pattern, auth_info)`` tuples where the items are:
*regex_pattern*
A regular expression that matches a URI.
*auth_info*
Authentication information to use for that URI. The value can be anything
that is understood by the ``requests`` library (see `requests
Authentication <requests-auth>`_ for details).
.. _requests-auth: https://requests.readthedocs.io/en/master/user/authentication/
The ``linkcheck`` builder will use the first matching ``auth_info`` value
it can find in the :confval:`linkcheck_auth` list, so values earlier in the
list have higher priority.
Example::
linkcheck_auth = [
('https://foo\.yourcompany\.com/.+', ('johndoe', 'secret')),
('https://.+\.yourcompany\.com/.+', HTTPDigestAuth(...)),
]
.. versionadded:: 2.3
Options for the XML builder Options for the XML builder
--------------------------- ---------------------------

View File

@ -439,7 +439,7 @@ There are also config values that you can set:
* ``'signature'`` -- Show typehints as its signature (default) * ``'signature'`` -- Show typehints as its signature (default)
* ``'none'`` -- Do not show typehints * ``'none'`` -- Do not show typehints
.. versionadded: 2.1 .. versionadded:: 2.1
.. confval:: autodoc_warningiserror .. confval:: autodoc_warningiserror

View File

@ -143,6 +143,11 @@ also use these config values:
The new files will be placed in the directories specified in the The new files will be placed in the directories specified in the
``:toctree:`` options of the directives. ``:toctree:`` options of the directives.
.. versionchanged:: 2.3
Emits :event:`autodoc-skip-member` event as :mod:`~sphinx.ext.autodoc`
does.
.. confval:: autosummary_generate_overwrite .. confval:: autosummary_generate_overwrite
If true, autosummary already overwrites stub files by generated contents. If true, autosummary already overwrites stub files by generated contents.

View File

@ -56,7 +56,7 @@ source code files.
.. _Google: .. _Google:
https://google.github.io/styleguide/pyguide.html#Comments https://google.github.io/styleguide/pyguide.html#Comments
.. _NumPy: .. _NumPy:
https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt https://numpydoc.readthedocs.io/en/latest/format.html#docstring-standard
.. _Khan Academy: .. _Khan Academy:
https://github.com/Khan/style-guides/blob/master/style/python.md#docstrings https://github.com/Khan/style-guides/blob/master/style/python.md#docstrings

View File

@ -427,8 +427,12 @@ def default_latex_engine(config: Config) -> str:
def default_latex_docclass(config: Config) -> Dict[str, str]: def default_latex_docclass(config: Config) -> Dict[str, str]:
""" Better default latex_docclass settings for specific languages. """ """ Better default latex_docclass settings for specific languages. """
if config.language == 'ja': if config.language == 'ja':
return {'manual': 'jsbook', if config.latex_engine == 'uplatex':
'howto': 'jreport'} return {'manual': 'ujbook',
'howto': 'ujreport'}
else:
return {'manual': 'jsbook',
'howto': 'jreport'}
else: else:
return {} return {}
@ -440,10 +444,12 @@ def default_latex_use_xindy(config: Config) -> bool:
def default_latex_documents(config: Config) -> List[Tuple[str, str, str, str, str]]: def default_latex_documents(config: Config) -> List[Tuple[str, str, str, str, str]]:
""" Better default latex_documents settings. """ """ Better default latex_documents settings. """
project = texescape.escape(config.project, config.latex_engine)
author = texescape.escape(config.author, config.latex_engine)
return [(config.master_doc, return [(config.master_doc,
make_filename_from_project(config.project) + '.tex', make_filename_from_project(config.project) + '.tex',
texescape.escape_abbr(texescape.escape(config.project)), texescape.escape_abbr(project),
texescape.escape_abbr(texescape.escape(config.author)), texescape.escape_abbr(author),
'manual')] 'manual')]
@ -454,7 +460,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.connect('config-inited', validate_config_values) app.connect('config-inited', validate_config_values)
app.add_config_value('latex_engine', default_latex_engine, None, app.add_config_value('latex_engine', default_latex_engine, None,
ENUM('pdflatex', 'xelatex', 'lualatex', 'platex')) ENUM('pdflatex', 'xelatex', 'lualatex', 'platex', 'uplatex'))
app.add_config_value('latex_documents', default_latex_documents, None) app.add_config_value('latex_documents', default_latex_documents, None)
app.add_config_value('latex_logo', None, None, [str]) app.add_config_value('latex_logo', None, None, [str])
app.add_config_value('latex_appendices', [], None) app.add_config_value('latex_appendices', [], None)

View File

@ -59,6 +59,9 @@ def check_anchor(response: requests.requests.Response, anchor: str) -> bool:
# Read file in chunks. If we find a matching anchor, we break # Read file in chunks. If we find a matching anchor, we break
# the loop early in hopes not to have to download the whole thing. # the loop early in hopes not to have to download the whole thing.
for chunk in response.iter_content(chunk_size=4096, decode_unicode=True): for chunk in response.iter_content(chunk_size=4096, decode_unicode=True):
if isinstance(chunk, bytes): # requests failed to decode
chunk = chunk.decode() # manually try to decode it
parser.feed(chunk) parser.feed(chunk)
if parser.found: if parser.found:
break break
@ -78,6 +81,8 @@ class CheckExternalLinksBuilder(Builder):
self.to_ignore = [re.compile(x) for x in self.app.config.linkcheck_ignore] self.to_ignore = [re.compile(x) for x in self.app.config.linkcheck_ignore]
self.anchors_ignore = [re.compile(x) self.anchors_ignore = [re.compile(x)
for x in self.app.config.linkcheck_anchors_ignore] for x in self.app.config.linkcheck_anchors_ignore]
self.auth = [(re.compile(pattern), auth_info) for pattern, auth_info
in self.app.config.linkcheck_auth]
self.good = set() # type: Set[str] self.good = set() # type: Set[str]
self.broken = {} # type: Dict[str, str] self.broken = {} # type: Dict[str, str]
self.redirected = {} # type: Dict[str, Tuple[str, int]] self.redirected = {} # type: Dict[str, Tuple[str, int]]
@ -124,11 +129,18 @@ class CheckExternalLinksBuilder(Builder):
except UnicodeError: except UnicodeError:
req_url = encode_uri(req_url) req_url = encode_uri(req_url)
# Get auth info, if any
for pattern, auth_info in self.auth:
if pattern.match(uri):
break
else:
auth_info = None
try: try:
if anchor and self.app.config.linkcheck_anchors: if anchor and self.app.config.linkcheck_anchors:
# Read the whole document and see if #anchor exists # Read the whole document and see if #anchor exists
response = requests.get(req_url, stream=True, config=self.app.config, response = requests.get(req_url, stream=True, config=self.app.config,
**kwargs) auth=auth_info, **kwargs)
found = check_anchor(response, unquote(anchor)) found = check_anchor(response, unquote(anchor))
if not found: if not found:
@ -137,13 +149,14 @@ class CheckExternalLinksBuilder(Builder):
try: try:
# try a HEAD request first, which should be easier on # try a HEAD request first, which should be easier on
# the server and the network # the server and the network
response = requests.head(req_url, config=self.app.config, **kwargs) response = requests.head(req_url, config=self.app.config,
auth=auth_info, **kwargs)
response.raise_for_status() response.raise_for_status()
except HTTPError: except HTTPError:
# retry with GET request if that fails, some servers # retry with GET request if that fails, some servers
# don't like HEAD requests. # don't like HEAD requests.
response = requests.get(req_url, stream=True, config=self.app.config, response = requests.get(req_url, stream=True, config=self.app.config,
**kwargs) auth=auth_info, **kwargs)
response.raise_for_status() response.raise_for_status()
except HTTPError as err: except HTTPError as err:
if err.response.status_code == 401: if err.response.status_code == 401:
@ -302,6 +315,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_builder(CheckExternalLinksBuilder) app.add_builder(CheckExternalLinksBuilder)
app.add_config_value('linkcheck_ignore', [], None) app.add_config_value('linkcheck_ignore', [], None)
app.add_config_value('linkcheck_auth', [], None)
app.add_config_value('linkcheck_retries', 1, None) app.add_config_value('linkcheck_retries', 1, None)
app.add_config_value('linkcheck_timeout', None, None, [int]) app.add_config_value('linkcheck_timeout', None, None, [int])
app.add_config_value('linkcheck_workers', 5, None) app.add_config_value('linkcheck_workers', 5, None)

View File

@ -158,16 +158,16 @@ class Config:
def __init__(self, config={}, overrides={}): def __init__(self, config={}, overrides={}):
# type: (Dict[str, Any], Dict[str, Any]) -> None # type: (Dict[str, Any], Dict[str, Any]) -> None
self.overrides = overrides self.overrides = dict(overrides)
self.values = Config.config_values.copy() self.values = Config.config_values.copy()
self._raw_config = config self._raw_config = config
self.setup = config.get('setup', None) # type: Callable self.setup = config.get('setup', None) # type: Callable
if 'extensions' in overrides: if 'extensions' in self.overrides:
if isinstance(overrides['extensions'], str): if isinstance(self.overrides['extensions'], str):
config['extensions'] = overrides.pop('extensions').split(',') config['extensions'] = self.overrides.pop('extensions').split(',')
else: else:
config['extensions'] = overrides.pop('extensions') config['extensions'] = self.overrides.pop('extensions')
self.extensions = config.get('extensions', []) # type: List[str] self.extensions = config.get('extensions', []) # type: List[str]
@classmethod @classmethod

View File

@ -138,9 +138,20 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any,
if not template.exists(template_name): if not template.exists(template_name):
template_name = 'autosummary/base.rst' template_name = 'autosummary/base.rst'
def skip_member(obj: Any, name: str, objtype: str) -> bool:
try:
return app.emit_firstresult('autodoc-skip-member', objtype, name,
obj, False, {})
except Exception as exc:
logger.warning(__('autosummary: failed to determine %r to be documented.'
'the following exception was raised:\n%s'),
name, exc, type='autosummary')
return False
def get_members(obj: Any, types: Set[str], include_public: List[str] = [], def get_members(obj: Any, types: Set[str], include_public: List[str] = [],
imported: bool = True) -> Tuple[List[str], List[str]]: imported: bool = True) -> Tuple[List[str], List[str]]:
items = [] # type: List[str] items = [] # type: List[str]
public = [] # type: List[str]
for name in dir(obj): for name in dir(obj):
try: try:
value = safe_getattr(obj, name) value = safe_getattr(obj, name)
@ -148,11 +159,20 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any,
continue continue
documenter = get_documenter(app, value, obj) documenter = get_documenter(app, value, obj)
if documenter.objtype in types: if documenter.objtype in types:
# skip imported members if expected
if imported or getattr(value, '__module__', None) == obj.__name__: if imported or getattr(value, '__module__', None) == obj.__name__:
# skip imported members if expected skipped = skip_member(value, name, documenter.objtype)
items.append(name) if skipped is True:
public = [x for x in items pass
if x in include_public or not x.startswith('_')] elif skipped is False:
# show the member forcedly
items.append(name)
public.append(name)
else:
items.append(name)
if name in include_public or not name.startswith('_'):
# considers member as public
public.append(name)
return public, items return public, items
ns = {} # type: Dict[str, Any] ns = {} # type: Dict[str, Any]

View File

@ -27,10 +27,9 @@ from sphinx.domains import Domain
from sphinx.environment import BuildEnvironment from sphinx.environment import BuildEnvironment
from sphinx.errors import NoUri from sphinx.errors import NoUri
from sphinx.locale import _, __ from sphinx.locale import _, __
from sphinx.util import logging from sphinx.util import logging, texescape
from sphinx.util.docutils import SphinxDirective from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import make_refnode from sphinx.util.nodes import make_refnode
from sphinx.util.texescape import get_escape_func
from sphinx.writers.html import HTMLTranslator from sphinx.writers.html import HTMLTranslator
from sphinx.writers.latex import LaTeXTranslator from sphinx.writers.latex import LaTeXTranslator
@ -299,11 +298,12 @@ def depart_todo_node(self: HTMLTranslator, node: todo_node) -> None:
def latex_visit_todo_node(self: LaTeXTranslator, node: todo_node) -> None: def latex_visit_todo_node(self: LaTeXTranslator, node: todo_node) -> None:
if self.config.todo_include_todos: if self.config.todo_include_todos:
escape = get_escape_func(self.config.latex_engine)
self.body.append('\n\\begin{sphinxadmonition}{note}{') self.body.append('\n\\begin{sphinxadmonition}{note}{')
self.body.append(self.hypertarget_to(node)) self.body.append(self.hypertarget_to(node))
title_node = cast(nodes.title, node[0]) title_node = cast(nodes.title, node[0])
self.body.append('%s:}' % escape(title_node.astext())) title = texescape.escape(title_node.astext(), self.config.latex_engine)
self.body.append('%s:}' % title)
node.pop(0) node.pop(0)
else: else:
raise nodes.SkipNode raise nodes.SkipNode

View File

@ -23,8 +23,7 @@ from pygments.util import ClassNotFound
from sphinx.locale import __ from sphinx.locale import __
from sphinx.pygments_styles import SphinxStyle, NoneStyle from sphinx.pygments_styles import SphinxStyle, NoneStyle
from sphinx.util import logging from sphinx.util import logging, texescape
from sphinx.util.texescape import get_hlescape_func
if False: if False:
# For type annotation # For type annotation
@ -165,8 +164,7 @@ class PygmentsBridge:
if self.dest == 'html': if self.dest == 'html':
return hlsource return hlsource
else: else:
escape = get_hlescape_func(self.latex_engine) return texescape.hlescape(hlsource, self.latex_engine)
return escape(hlsource)
def get_stylesheet(self): def get_stylesheet(self):
# type: () -> str # type: () -> str

View File

@ -13,6 +13,7 @@ import os
from sphinx.locale import __ from sphinx.locale import __
from sphinx.util import get_matching_files from sphinx.util import get_matching_files
from sphinx.util import logging from sphinx.util import logging
from sphinx.util import path_stabilize
from sphinx.util.matching import compile_matchers from sphinx.util.matching import compile_matchers
from sphinx.util.osutil import SEP, relpath from sphinx.util.osutil import SEP, relpath
@ -71,6 +72,7 @@ class Project:
filename = relpath(filename, self.srcdir) filename = relpath(filename, self.srcdir)
for suffix in self.source_suffix: for suffix in self.source_suffix:
if filename.endswith(suffix): if filename.endswith(suffix):
filename = path_stabilize(filename)
return filename[:-len(suffix)] return filename[:-len(suffix)]
# the file does not have docname # the file does not have docname

View File

@ -36,7 +36,7 @@ XINDYOPTS += -M LatinRules.xdy
# format: pdf or dvi (used only by archive targets) # format: pdf or dvi (used only by archive targets)
FMT = pdf FMT = pdf
{% if latex_engine == 'platex' -%} {% if latex_engine in ('platex', 'uplatex') -%}
# latexmkrc is read then overridden by latexmkjarc # latexmkrc is read then overridden by latexmkjarc
LATEX = latexmk -r latexmkjarc -dvi LATEX = latexmk -r latexmkjarc -dvi
PDFLATEX = latexmk -r latexmkjarc -pdfdvi -dvi- -ps- PDFLATEX = latexmk -r latexmkjarc -pdfdvi -dvi- -ps-

View File

@ -1,4 +1,4 @@
$latex = 'platex ' . $ENV{'LATEXOPTS'} . ' -kanji=utf8 %O %S'; $latex = '{{ latex_engine }} ' . $ENV{'LATEXOPTS'} . ' -kanji=utf8 %O %S';
$dvipdf = 'dvipdfmx %O -o %D %S'; $dvipdf = 'dvipdfmx %O -o %D %S';
$makeindex = 'internal mendex %S %B %D'; $makeindex = 'internal mendex %S %B %D';
sub mendex { sub mendex {

View File

@ -6,5 +6,8 @@
<Url type="text/html" method="get" <Url type="text/html" method="get"
template="{{ use_opensearch }}/{{ pathto('search') }}?q={searchTerms}"/> template="{{ use_opensearch }}/{{ pathto('search') }}?q={searchTerms}"/>
<LongName>{{ docstitle|e }}</LongName> <LongName>{{ docstitle|e }}</LongName>
{%- if favicon %}
<Image height="16" width="16" type="image/x-icon">{{ use_opensearch }}/{{ pathto('_static/' + favicon, 1) }}</Image>
{%- endif %}
{% block extra %} {# Put e.g. an <Image> element here. #} {% endblock %} {% block extra %} {# Put e.g. an <Image> element here. #} {% endblock %}
</OpenSearchDescription> </OpenSearchDescription>

View File

@ -424,7 +424,7 @@ var Search = {
for (j = 0; j < _files.length; j++) { for (j = 0; j < _files.length; j++) {
file = _files[j]; file = _files[j];
if (!(file in scoreMap)) if (!(file in scoreMap))
scoreMap[file] = {} scoreMap[file] = {};
scoreMap[file][word] = o.score; scoreMap[file][word] = o.score;
} }
}); });
@ -432,7 +432,7 @@ var Search = {
// create the mapping // create the mapping
for (j = 0; j < files.length; j++) { for (j = 0; j < files.length; j++) {
file = files[j]; file = files[j];
if (file in fileMap) if (file in fileMap && fileMap[file].indexOf(word) === -1)
fileMap[file].push(word); fileMap[file].push(word);
else else
fileMap[file] = [word]; fileMap[file] = [word];

View File

@ -155,7 +155,7 @@ def educateQuotes(text: str, language: str = 'en') -> str:
# Special case for decade abbreviations (the '80s): # Special case for decade abbreviations (the '80s):
if language.startswith('en'): # TODO similar cases in other languages? if language.startswith('en'): # TODO similar cases in other languages?
text = re.sub(r"""'(?=\d{2}s)""", apostrophe, text, re.UNICODE) text = re.sub(r"""'(?=\d{2}s)""", apostrophe, text, flags=re.UNICODE)
close_class = r"""[^\ \t\r\n\[\{\(\-]""" close_class = r"""[^\ \t\r\n\[\{\(\-]"""
dec_dashes = r"""&#8211;|&#8212;""" dec_dashes = r"""&#8211;|&#8212;"""

View File

@ -9,6 +9,7 @@
""" """
import os import os
from functools import partial
from typing import Dict, List, Union from typing import Dict, List, Union
from jinja2.loaders import BaseLoader from jinja2.loaders import BaseLoader
@ -69,8 +70,9 @@ class LaTeXRenderer(SphinxRenderer):
super().__init__(template_path) super().__init__(template_path)
# use texescape as escape filter # use texescape as escape filter
self.env.filters['e'] = texescape.get_escape_func(latex_engine) escape = partial(texescape.escape, latex_engine=latex_engine)
self.env.filters['escape'] = texescape.get_escape_func(latex_engine) self.env.filters['e'] = escape
self.env.filters['escape'] = escape
self.env.filters['eabbr'] = texescape.escape_abbr self.env.filters['eabbr'] = texescape.escape_abbr
# use JSP/eRuby like tagging instead because curly bracket; the default # use JSP/eRuby like tagging instead because curly bracket; the default

View File

@ -9,7 +9,10 @@
""" """
import re import re
from typing import Callable, Dict from typing import Dict
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
tex_replacements = [ tex_replacements = [
# map TeX special chars # map TeX special chars
@ -32,8 +35,6 @@ tex_replacements = [
('`', r'{}`'), ('`', r'{}`'),
('<', r'\textless{}'), ('<', r'\textless{}'),
('>', r'\textgreater{}'), ('>', r'\textgreater{}'),
# map char for some unknown reason. TODO: remove this?
('|', r'\textbar{}'),
# map special Unicode characters to TeX commands # map special Unicode characters to TeX commands
('', r'\(\checkmark\)'), ('', r'\(\checkmark\)'),
('', r'\(\pmb{\checkmark}\)'), ('', r'\(\pmb{\checkmark}\)'),
@ -84,47 +85,38 @@ unicode_tex_replacements = [
('', r'\(\sb{\text{9}}\)'), ('', r'\(\sb{\text{9}}\)'),
] ]
tex_escape_map = {} # type: Dict[int, str] tex_replace_map = {} # type: Dict[int, str]
tex_escape_map_without_unicode = {} # type: Dict[int, str]
tex_replace_map = {} _tex_escape_map = {} # type: Dict[int, str]
tex_hl_escape_map_new = {} # type: Dict[int, str] _tex_escape_map_without_unicode = {} # type: Dict[int, str]
tex_hl_escape_map_new_without_unicode = {} # type: Dict[int, str] _tex_hlescape_map = {} # type: Dict[int, str]
_tex_hlescape_map_without_unicode = {} # type: Dict[int, str]
def get_escape_func(latex_engine: str) -> Callable[[str], str]: deprecated_alias('sphinx.util.texescape',
"""Get escape() function for given latex_engine.""" {
if latex_engine in ('lualatex', 'xelatex'): 'tex_escape_map': _tex_escape_map,
return escape_for_unicode_latex_engine 'tex_hl_escape_map_new': _tex_hlescape_map,
else: },
return escape RemovedInSphinx40Warning)
def escape(s: str) -> str: def escape(s: str, latex_engine: str = None) -> str:
"""Escape text for LaTeX output.""" """Escape text for LaTeX output."""
return s.translate(tex_escape_map)
def escape_for_unicode_latex_engine(s: str) -> str:
"""Escape text for unicode supporting LaTeX engine."""
return s.translate(tex_escape_map_without_unicode)
def get_hlescape_func(latex_engine: str) -> Callable[[str], str]:
"""Get hlescape() function for given latex_engine."""
if latex_engine in ('lualatex', 'xelatex'): if latex_engine in ('lualatex', 'xelatex'):
return hlescape_for_unicode_latex_engine # unicode based LaTeX engine
return s.translate(_tex_escape_map_without_unicode)
else: else:
return hlescape return s.translate(_tex_escape_map)
def hlescape(s: str) -> str: def hlescape(s: str, latex_engine: str = None) -> str:
"""Escape text for LaTeX highlighter.""" """Escape text for LaTeX highlighter."""
return s.translate(tex_hl_escape_map_new) if latex_engine in ('lualatex', 'xelatex'):
# unicode based LaTeX engine
return s.translate(_tex_hlescape_map_without_unicode)
def hlescape_for_unicode_latex_engine(s: str) -> str: else:
"""Escape text for unicode supporting LaTeX engine.""" return s.translate(_tex_hlescape_map)
return s.translate(tex_hl_escape_map_new_without_unicode)
def escape_abbr(text: str) -> str: def escape_abbr(text: str) -> str:
@ -134,19 +126,19 @@ def escape_abbr(text: str) -> str:
def init() -> None: def init() -> None:
for a, b in tex_replacements: for a, b in tex_replacements:
tex_escape_map[ord(a)] = b _tex_escape_map[ord(a)] = b
tex_escape_map_without_unicode[ord(a)] = b _tex_escape_map_without_unicode[ord(a)] = b
tex_replace_map[ord(a)] = '_' tex_replace_map[ord(a)] = '_'
for a, b in unicode_tex_replacements: for a, b in unicode_tex_replacements:
tex_escape_map[ord(a)] = b _tex_escape_map[ord(a)] = b
tex_replace_map[ord(a)] = '_' tex_replace_map[ord(a)] = '_'
for a, b in tex_replacements: for a, b in tex_replacements:
if a in '[]{}\\': if a in '[]{}\\':
continue continue
tex_hl_escape_map_new[ord(a)] = b _tex_hlescape_map[ord(a)] = b
tex_hl_escape_map_new_without_unicode[ord(a)] = b _tex_hlescape_map_without_unicode[ord(a)] = b
for a, b in unicode_tex_replacements: for a, b in unicode_tex_replacements:
tex_hl_escape_map_new[ord(a)] = b _tex_hlescape_map[ord(a)] = b

View File

@ -12,9 +12,11 @@ import copy
import os import os
import posixpath import posixpath
import warnings import warnings
from typing import Iterable, cast from typing import cast
from typing import Iterable
from docutils import nodes from docutils import nodes
from docutils.nodes import Element, Node, Text
from docutils.writers.html4css1 import Writer, HTMLTranslator as BaseTranslator from docutils.writers.html4css1 import Writer, HTMLTranslator as BaseTranslator
from sphinx import addnodes from sphinx import addnodes
@ -27,8 +29,7 @@ from sphinx.util.images import get_image_size
if False: if False:
# For type annotation # For type annotation
from typing import Any # NOQA from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.builders.html import StandaloneHTMLBuilder # NOQA
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -45,13 +46,11 @@ class HTMLWriter(Writer):
if '--embed-stylesheet' in _setting[1]: if '--embed-stylesheet' in _setting[1]:
_setting[2]['default'] = 0 _setting[2]['default'] = 0
def __init__(self, builder): def __init__(self, builder: "StandaloneHTMLBuilder") -> None:
# type: (StandaloneHTMLBuilder) -> None
super().__init__() super().__init__()
self.builder = builder self.builder = builder
def translate(self): def translate(self) -> None:
# type: () -> None
# sadly, this is mostly copied from parent class # sadly, this is mostly copied from parent class
visitor = self.builder.create_translator(self.document, self.builder) visitor = self.builder.create_translator(self.document, self.builder)
self.visitor = cast(HTMLTranslator, visitor) self.visitor = cast(HTMLTranslator, visitor)
@ -73,8 +72,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
builder = None # type: StandaloneHTMLBuilder builder = None # type: StandaloneHTMLBuilder
def __init__(self, *args): def __init__(self, *args) -> None:
# type: (Any) -> None
if isinstance(args[0], nodes.document) and isinstance(args[1], Builder): if isinstance(args[0], nodes.document) and isinstance(args[1], Builder):
document, builder = args document, builder = args
else: else:
@ -100,26 +98,21 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
self._fieldlist_row_index = 0 self._fieldlist_row_index = 0
self.required_params_left = 0 self.required_params_left = 0
def visit_start_of_file(self, node): def visit_start_of_file(self, node: Element) -> None:
# type: (nodes.Element) -> None
# only occurs in the single-file builder # only occurs in the single-file builder
self.docnames.append(node['docname']) self.docnames.append(node['docname'])
self.body.append('<span id="document-%s"></span>' % node['docname']) self.body.append('<span id="document-%s"></span>' % node['docname'])
def depart_start_of_file(self, node): def depart_start_of_file(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.docnames.pop() self.docnames.pop()
def visit_desc(self, node): def visit_desc(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(self.starttag(node, 'dl', CLASS=node['objtype'])) self.body.append(self.starttag(node, 'dl', CLASS=node['objtype']))
def depart_desc(self, node): def depart_desc(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</dl>\n\n') self.body.append('</dl>\n\n')
def visit_desc_signature(self, node): def visit_desc_signature(self, node: Element) -> None:
# type: (nodes.Element) -> None
# the id is set automatically # the id is set automatically
self.body.append(self.starttag(node, 'dt')) self.body.append(self.starttag(node, 'dt'))
# anchor for per-desc interactive data # anchor for per-desc interactive data
@ -127,57 +120,45 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
and node['ids'] and node['first']: and node['ids'] and node['first']:
self.body.append('<!--[%s]-->' % node['ids'][0]) self.body.append('<!--[%s]-->' % node['ids'][0])
def depart_desc_signature(self, node): def depart_desc_signature(self, node: Element) -> None:
# type: (nodes.Element) -> None
if not node.get('is_multiline'): if not node.get('is_multiline'):
self.add_permalink_ref(node, _('Permalink to this definition')) self.add_permalink_ref(node, _('Permalink to this definition'))
self.body.append('</dt>\n') self.body.append('</dt>\n')
def visit_desc_signature_line(self, node): def visit_desc_signature_line(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_desc_signature_line(self, node): def depart_desc_signature_line(self, node: Element) -> None:
# type: (nodes.Element) -> None
if node.get('add_permalink'): if node.get('add_permalink'):
# the permalink info is on the parent desc_signature node # the permalink info is on the parent desc_signature node
self.add_permalink_ref(node.parent, _('Permalink to this definition')) self.add_permalink_ref(node.parent, _('Permalink to this definition'))
self.body.append('<br />') self.body.append('<br />')
def visit_desc_addname(self, node): def visit_desc_addname(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(self.starttag(node, 'code', '', CLASS='descclassname')) self.body.append(self.starttag(node, 'code', '', CLASS='descclassname'))
def depart_desc_addname(self, node): def depart_desc_addname(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</code>') self.body.append('</code>')
def visit_desc_type(self, node): def visit_desc_type(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_desc_type(self, node): def depart_desc_type(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_desc_returns(self, node): def visit_desc_returns(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(' &#x2192; ') self.body.append(' &#x2192; ')
def depart_desc_returns(self, node): def depart_desc_returns(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_desc_name(self, node): def visit_desc_name(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(self.starttag(node, 'code', '', CLASS='descname')) self.body.append(self.starttag(node, 'code', '', CLASS='descname'))
def depart_desc_name(self, node): def depart_desc_name(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</code>') self.body.append('</code>')
def visit_desc_parameterlist(self, node): def visit_desc_parameterlist(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('<span class="sig-paren">(</span>') self.body.append('<span class="sig-paren">(</span>')
self.first_param = 1 self.first_param = 1
self.optional_param_level = 0 self.optional_param_level = 0
@ -186,8 +167,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
for c in node.children]) for c in node.children])
self.param_separator = node.child_text_separator self.param_separator = node.child_text_separator
def depart_desc_parameterlist(self, node): def depart_desc_parameterlist(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('<span class="sig-paren">)</span>') self.body.append('<span class="sig-paren">)</span>')
# If required parameters are still to come, then put the comma after # If required parameters are still to come, then put the comma after
@ -196,8 +176,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
# #
# foo([a, ]b, c[, d]) # foo([a, ]b, c[, d])
# #
def visit_desc_parameter(self, node): def visit_desc_parameter(self, node: Element) -> None:
# type: (nodes.Element) -> None
if self.first_param: if self.first_param:
self.first_param = 0 self.first_param = 0
elif not self.required_params_left: elif not self.required_params_left:
@ -207,50 +186,40 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
if not node.hasattr('noemph'): if not node.hasattr('noemph'):
self.body.append('<em>') self.body.append('<em>')
def depart_desc_parameter(self, node): def depart_desc_parameter(self, node: Element) -> None:
# type: (nodes.Element) -> None
if not node.hasattr('noemph'): if not node.hasattr('noemph'):
self.body.append('</em>') self.body.append('</em>')
if self.required_params_left: if self.required_params_left:
self.body.append(self.param_separator) self.body.append(self.param_separator)
def visit_desc_optional(self, node): def visit_desc_optional(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.optional_param_level += 1 self.optional_param_level += 1
self.body.append('<span class="optional">[</span>') self.body.append('<span class="optional">[</span>')
def depart_desc_optional(self, node): def depart_desc_optional(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.optional_param_level -= 1 self.optional_param_level -= 1
self.body.append('<span class="optional">]</span>') self.body.append('<span class="optional">]</span>')
def visit_desc_annotation(self, node): def visit_desc_annotation(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(self.starttag(node, 'em', '', CLASS='property')) self.body.append(self.starttag(node, 'em', '', CLASS='property'))
def depart_desc_annotation(self, node): def depart_desc_annotation(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</em>') self.body.append('</em>')
def visit_desc_content(self, node): def visit_desc_content(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(self.starttag(node, 'dd', '')) self.body.append(self.starttag(node, 'dd', ''))
def depart_desc_content(self, node): def depart_desc_content(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</dd>') self.body.append('</dd>')
def visit_versionmodified(self, node): def visit_versionmodified(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(self.starttag(node, 'div', CLASS=node['type'])) self.body.append(self.starttag(node, 'div', CLASS=node['type']))
def depart_versionmodified(self, node): def depart_versionmodified(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</div>\n') self.body.append('</div>\n')
# overwritten # overwritten
def visit_reference(self, node): def visit_reference(self, node: Element) -> None:
# type: (nodes.Element) -> None
atts = {'class': 'reference'} atts = {'class': 'reference'}
if node.get('internal') or 'refuri' not in node: if node.get('internal') or 'refuri' not in node:
atts['class'] += ' internal' atts['class'] += ' internal'
@ -278,38 +247,31 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
self.body.append(('%s' + self.secnumber_suffix) % self.body.append(('%s' + self.secnumber_suffix) %
'.'.join(map(str, node['secnumber']))) '.'.join(map(str, node['secnumber'])))
def visit_number_reference(self, node): def visit_number_reference(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_reference(node) self.visit_reference(node)
def depart_number_reference(self, node): def depart_number_reference(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_reference(node) self.depart_reference(node)
# overwritten -- we don't want source comments to show up in the HTML # overwritten -- we don't want source comments to show up in the HTML
def visit_comment(self, node): # type: ignore def visit_comment(self, node: Element) -> None: # type: ignore
# type: (nodes.Element) -> None
raise nodes.SkipNode raise nodes.SkipNode
# overwritten # overwritten
def visit_admonition(self, node, name=''): def visit_admonition(self, node: Element, name: str = '') -> None:
# type: (nodes.Element, str) -> None
self.body.append(self.starttag( self.body.append(self.starttag(
node, 'div', CLASS=('admonition ' + name))) node, 'div', CLASS=('admonition ' + name)))
if name: if name:
node.insert(0, nodes.title(name, admonitionlabels[name])) node.insert(0, nodes.title(name, admonitionlabels[name]))
self.set_first_last(node) self.set_first_last(node)
def visit_seealso(self, node): def visit_seealso(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_admonition(node, 'seealso') self.visit_admonition(node, 'seealso')
def depart_seealso(self, node): def depart_seealso(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_admonition(node) self.depart_admonition(node)
def add_secnumber(self, node): def add_secnumber(self, node: Element) -> None:
# type: (nodes.Element) -> None
if node.get('secnumber'): if node.get('secnumber'):
self.body.append('.'.join(map(str, node['secnumber'])) + self.body.append('.'.join(map(str, node['secnumber'])) +
self.secnumber_suffix) self.secnumber_suffix)
@ -328,10 +290,8 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
self.body.append('.'.join(map(str, numbers)) + self.body.append('.'.join(map(str, numbers)) +
self.secnumber_suffix) self.secnumber_suffix)
def add_fignumber(self, node): def add_fignumber(self, node: Element) -> None:
# type: (nodes.Element) -> None def append_fignumber(figtype: str, figure_id: str) -> None:
def append_fignumber(figtype, figure_id):
# type: (str, str) -> None
if self.builder.name == 'singlehtml': if self.builder.name == 'singlehtml':
key = "%s/%s" % (self.docnames[-1], figtype) key = "%s/%s" % (self.docnames[-1], figtype)
else: else:
@ -356,14 +316,12 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
else: else:
append_fignumber(figtype, node['ids'][0]) append_fignumber(figtype, node['ids'][0])
def add_permalink_ref(self, node, title): def add_permalink_ref(self, node: Element, title: str) -> None:
# type: (nodes.Element, str) -> None
if node['ids'] and self.permalink_text and self.builder.add_permalinks: if node['ids'] and self.permalink_text and self.builder.add_permalinks:
format = '<a class="headerlink" href="#%s" title="%s">%s</a>' format = '<a class="headerlink" href="#%s" title="%s">%s</a>'
self.body.append(format % (node['ids'][0], title, self.permalink_text)) self.body.append(format % (node['ids'][0], title, self.permalink_text))
def generate_targets_for_listing(self, node): def generate_targets_for_listing(self, node: Element) -> None:
# type: (nodes.Element) -> None
"""Generate hyperlink targets for listings. """Generate hyperlink targets for listings.
Original visit_bullet_list(), visit_definition_list() and visit_enumerated_list() Original visit_bullet_list(), visit_definition_list() and visit_enumerated_list()
@ -378,8 +336,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
node['ids'].remove(id) node['ids'].remove(id)
# overwritten # overwritten
def visit_bullet_list(self, node): def visit_bullet_list(self, node: Element) -> None:
# type: (nodes.Element) -> None
if len(node) == 1 and isinstance(node[0], addnodes.toctree): if len(node) == 1 and isinstance(node[0], addnodes.toctree):
# avoid emitting empty <ul></ul> # avoid emitting empty <ul></ul>
raise nodes.SkipNode raise nodes.SkipNode
@ -387,46 +344,39 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
super().visit_bullet_list(node) super().visit_bullet_list(node)
# overwritten # overwritten
def visit_enumerated_list(self, node): def visit_enumerated_list(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.generate_targets_for_listing(node) self.generate_targets_for_listing(node)
super().visit_enumerated_list(node) super().visit_enumerated_list(node)
# overwritten # overwritten
def visit_definition(self, node): def visit_definition(self, node: Element) -> None:
# type: (nodes.Element) -> None
# don't insert </dt> here. # don't insert </dt> here.
self.body.append(self.starttag(node, 'dd', '')) self.body.append(self.starttag(node, 'dd', ''))
# overwritten # overwritten
def depart_definition(self, node): def depart_definition(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</dd>\n') self.body.append('</dd>\n')
# overwritten # overwritten
def visit_classifier(self, node): def visit_classifier(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(self.starttag(node, 'span', '', CLASS='classifier')) self.body.append(self.starttag(node, 'span', '', CLASS='classifier'))
# overwritten # overwritten
def depart_classifier(self, node): def depart_classifier(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</span>') self.body.append('</span>')
next_node = node.next_node(descend=False, siblings=True) # type: nodes.Node next_node = node.next_node(descend=False, siblings=True) # type: Node
if not isinstance(next_node, nodes.classifier): if not isinstance(next_node, nodes.classifier):
# close `<dt>` tag at the tail of classifiers # close `<dt>` tag at the tail of classifiers
self.body.append('</dt>') self.body.append('</dt>')
# overwritten # overwritten
def visit_term(self, node): def visit_term(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(self.starttag(node, 'dt', '')) self.body.append(self.starttag(node, 'dt', ''))
# overwritten # overwritten
def depart_term(self, node): def depart_term(self, node: Element) -> None:
# type: (nodes.Element) -> None next_node = node.next_node(descend=False, siblings=True) # type: Node
next_node = node.next_node(descend=False, siblings=True) # type: nodes.Node
if isinstance(next_node, nodes.classifier): if isinstance(next_node, nodes.classifier):
# Leave the end tag to `self.depart_classifier()`, in case # Leave the end tag to `self.depart_classifier()`, in case
# there's a classifier. # there's a classifier.
@ -435,16 +385,14 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
self.body.append('</dt>') self.body.append('</dt>')
# overwritten # overwritten
def visit_title(self, node): def visit_title(self, node: Element) -> None:
# type: (nodes.Element) -> None
super().visit_title(node) super().visit_title(node)
self.add_secnumber(node) self.add_secnumber(node)
self.add_fignumber(node.parent) self.add_fignumber(node.parent)
if isinstance(node.parent, nodes.table): if isinstance(node.parent, nodes.table):
self.body.append('<span class="caption-text">') self.body.append('<span class="caption-text">')
def depart_title(self, node): def depart_title(self, node: Element) -> None:
# type: (nodes.Element) -> None
close_tag = self.context[-1] close_tag = self.context[-1]
if (self.permalink_text and self.builder.add_permalinks and if (self.permalink_text and self.builder.add_permalinks and
node.parent.hasattr('ids') and node.parent['ids']): node.parent.hasattr('ids') and node.parent['ids']):
@ -466,8 +414,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
super().depart_title(node) super().depart_title(node)
# overwritten # overwritten
def visit_literal_block(self, node): def visit_literal_block(self, node: Element) -> None:
# type: (nodes.Element) -> None
if node.rawsource != node.astext(): if node.rawsource != node.astext():
# most probably a parsed-literal block -- don't highlight # most probably a parsed-literal block -- don't highlight
return super().visit_literal_block(node) return super().visit_literal_block(node)
@ -491,8 +438,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
self.body.append(starttag + highlighted + '</div>\n') self.body.append(starttag + highlighted + '</div>\n')
raise nodes.SkipNode raise nodes.SkipNode
def visit_caption(self, node): def visit_caption(self, node: Element) -> None:
# type: (nodes.Element) -> None
if isinstance(node.parent, nodes.container) and node.parent.get('literal_block'): if isinstance(node.parent, nodes.container) and node.parent.get('literal_block'):
self.body.append('<div class="code-block-caption">') self.body.append('<div class="code-block-caption">')
else: else:
@ -500,8 +446,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
self.add_fignumber(node.parent) self.add_fignumber(node.parent)
self.body.append(self.starttag(node, 'span', '', CLASS='caption-text')) self.body.append(self.starttag(node, 'span', '', CLASS='caption-text'))
def depart_caption(self, node): def depart_caption(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</span>') self.body.append('</span>')
# append permalink if available # append permalink if available
@ -517,22 +462,18 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
else: else:
super().depart_caption(node) super().depart_caption(node)
def visit_doctest_block(self, node): def visit_doctest_block(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_literal_block(node) self.visit_literal_block(node)
# overwritten to add the <div> (for XHTML compliance) # overwritten to add the <div> (for XHTML compliance)
def visit_block_quote(self, node): def visit_block_quote(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(self.starttag(node, 'blockquote') + '<div>') self.body.append(self.starttag(node, 'blockquote') + '<div>')
def depart_block_quote(self, node): def depart_block_quote(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</div></blockquote>\n') self.body.append('</div></blockquote>\n')
# overwritten # overwritten
def visit_literal(self, node): def visit_literal(self, node: Element) -> None:
# type: (nodes.Element) -> None
if 'kbd' in node['classes']: if 'kbd' in node['classes']:
self.body.append(self.starttag(node, 'kbd', '', self.body.append(self.starttag(node, 'kbd', '',
CLASS='docutils literal notranslate')) CLASS='docutils literal notranslate'))
@ -541,16 +482,14 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
CLASS='docutils literal notranslate')) CLASS='docutils literal notranslate'))
self.protect_literal_text += 1 self.protect_literal_text += 1
def depart_literal(self, node): def depart_literal(self, node: Element) -> None:
# type: (nodes.Element) -> None
if 'kbd' in node['classes']: if 'kbd' in node['classes']:
self.body.append('</kbd>') self.body.append('</kbd>')
else: else:
self.protect_literal_text -= 1 self.protect_literal_text -= 1
self.body.append('</code>') self.body.append('</code>')
def visit_productionlist(self, node): def visit_productionlist(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(self.starttag(node, 'pre')) self.body.append(self.starttag(node, 'pre'))
names = [] names = []
productionlist = cast(Iterable[addnodes.production], node) productionlist = cast(Iterable[addnodes.production], node)
@ -570,30 +509,24 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
self.body.append('</pre>\n') self.body.append('</pre>\n')
raise nodes.SkipNode raise nodes.SkipNode
def depart_productionlist(self, node): def depart_productionlist(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_production(self, node): def visit_production(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_production(self, node): def depart_production(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_centered(self, node): def visit_centered(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(self.starttag(node, 'p', CLASS="centered") + self.body.append(self.starttag(node, 'p', CLASS="centered") +
'<strong>') '<strong>')
def depart_centered(self, node): def depart_centered(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</strong></p>') self.body.append('</strong></p>')
# overwritten # overwritten
def should_be_compact_paragraph(self, node): def should_be_compact_paragraph(self, node: Node) -> bool:
# type: (nodes.Node) -> bool
"""Determine if the <p> tags around paragraph can be omitted.""" """Determine if the <p> tags around paragraph can be omitted."""
if isinstance(node.parent, addnodes.desc_content): if isinstance(node.parent, addnodes.desc_content):
# Never compact desc_content items. # Never compact desc_content items.
@ -603,16 +536,13 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
return False return False
return super().should_be_compact_paragraph(node) return super().should_be_compact_paragraph(node)
def visit_compact_paragraph(self, node): def visit_compact_paragraph(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_compact_paragraph(self, node): def depart_compact_paragraph(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_download_reference(self, node): def visit_download_reference(self, node: Element) -> None:
# type: (nodes.Element) -> None
atts = {'class': 'reference download', atts = {'class': 'reference download',
'download': ''} 'download': ''}
@ -631,13 +561,11 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
else: else:
self.context.append('') self.context.append('')
def depart_download_reference(self, node): def depart_download_reference(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(self.context.pop()) self.body.append(self.context.pop())
# overwritten # overwritten
def visit_image(self, node): def visit_image(self, node: Element) -> None:
# type: (nodes.Element) -> None
olduri = node['uri'] olduri = node['uri']
# rewrite the URI if the environment knows about it # rewrite the URI if the environment knows about it
if olduri in self.builder.images: if olduri in self.builder.images:
@ -678,67 +606,53 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
super().visit_image(node) super().visit_image(node)
# overwritten # overwritten
def depart_image(self, node): def depart_image(self, node: Element) -> None:
# type: (nodes.Element) -> None
if node['uri'].lower().endswith(('svg', 'svgz')): if node['uri'].lower().endswith(('svg', 'svgz')):
self.body.append(self.context.pop()) self.body.append(self.context.pop())
else: else:
super().depart_image(node) super().depart_image(node)
def visit_toctree(self, node): def visit_toctree(self, node: Element) -> None:
# type: (nodes.Element) -> None
# this only happens when formatting a toc from env.tocs -- in this # this only happens when formatting a toc from env.tocs -- in this
# case we don't want to include the subtree # case we don't want to include the subtree
raise nodes.SkipNode raise nodes.SkipNode
def visit_index(self, node): def visit_index(self, node: Element) -> None:
# type: (nodes.Element) -> None
raise nodes.SkipNode raise nodes.SkipNode
def visit_tabular_col_spec(self, node): def visit_tabular_col_spec(self, node: Element) -> None:
# type: (nodes.Element) -> None
raise nodes.SkipNode raise nodes.SkipNode
def visit_glossary(self, node): def visit_glossary(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_glossary(self, node): def depart_glossary(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_acks(self, node): def visit_acks(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_acks(self, node): def depart_acks(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_hlist(self, node): def visit_hlist(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('<table class="hlist"><tr>') self.body.append('<table class="hlist"><tr>')
def depart_hlist(self, node): def depart_hlist(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</tr></table>\n') self.body.append('</tr></table>\n')
def visit_hlistcol(self, node): def visit_hlistcol(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('<td>') self.body.append('<td>')
def depart_hlistcol(self, node): def depart_hlistcol(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</td>') self.body.append('</td>')
def visit_option_group(self, node): def visit_option_group(self, node: Element) -> None:
# type: (nodes.Element) -> None
super().visit_option_group(node) super().visit_option_group(node)
self.context[-2] = self.context[-2].replace('&nbsp;', '&#160;') self.context[-2] = self.context[-2].replace('&nbsp;', '&#160;')
# overwritten # overwritten
def visit_Text(self, node): def visit_Text(self, node: Text) -> None:
# type: (nodes.Text) -> None
text = node.astext() text = node.astext()
encoded = self.encode(text) encoded = self.encode(text)
if self.protect_literal_text: if self.protect_literal_text:
@ -759,127 +673,99 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
encoded = self.cloak_email(encoded) encoded = self.cloak_email(encoded)
self.body.append(encoded) self.body.append(encoded)
def visit_note(self, node): def visit_note(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_admonition(node, 'note') self.visit_admonition(node, 'note')
def depart_note(self, node): def depart_note(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_admonition(node) self.depart_admonition(node)
def visit_warning(self, node): def visit_warning(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_admonition(node, 'warning') self.visit_admonition(node, 'warning')
def depart_warning(self, node): def depart_warning(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_admonition(node) self.depart_admonition(node)
def visit_attention(self, node): def visit_attention(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_admonition(node, 'attention') self.visit_admonition(node, 'attention')
def depart_attention(self, node): def depart_attention(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_admonition(node) self.depart_admonition(node)
def visit_caution(self, node): def visit_caution(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_admonition(node, 'caution') self.visit_admonition(node, 'caution')
def depart_caution(self, node): def depart_caution(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_admonition(node) self.depart_admonition(node)
def visit_danger(self, node): def visit_danger(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_admonition(node, 'danger') self.visit_admonition(node, 'danger')
def depart_danger(self, node): def depart_danger(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_admonition(node) self.depart_admonition(node)
def visit_error(self, node): def visit_error(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_admonition(node, 'error') self.visit_admonition(node, 'error')
def depart_error(self, node): def depart_error(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_admonition(node) self.depart_admonition(node)
def visit_hint(self, node): def visit_hint(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_admonition(node, 'hint') self.visit_admonition(node, 'hint')
def depart_hint(self, node): def depart_hint(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_admonition(node) self.depart_admonition(node)
def visit_important(self, node): def visit_important(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_admonition(node, 'important') self.visit_admonition(node, 'important')
def depart_important(self, node): def depart_important(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_admonition(node) self.depart_admonition(node)
def visit_tip(self, node): def visit_tip(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_admonition(node, 'tip') self.visit_admonition(node, 'tip')
def depart_tip(self, node): def depart_tip(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_admonition(node) self.depart_admonition(node)
def visit_literal_emphasis(self, node): def visit_literal_emphasis(self, node: Element) -> None:
# type: (nodes.Element) -> None
return self.visit_emphasis(node) return self.visit_emphasis(node)
def depart_literal_emphasis(self, node): def depart_literal_emphasis(self, node: Element) -> None:
# type: (nodes.Element) -> None
return self.depart_emphasis(node) return self.depart_emphasis(node)
def visit_literal_strong(self, node): def visit_literal_strong(self, node: Element) -> None:
# type: (nodes.Element) -> None
return self.visit_strong(node) return self.visit_strong(node)
def depart_literal_strong(self, node): def depart_literal_strong(self, node: Element) -> None:
# type: (nodes.Element) -> None
return self.depart_strong(node) return self.depart_strong(node)
def visit_abbreviation(self, node): def visit_abbreviation(self, node: Element) -> None:
# type: (nodes.Element) -> None
attrs = {} attrs = {}
if node.hasattr('explanation'): if node.hasattr('explanation'):
attrs['title'] = node['explanation'] attrs['title'] = node['explanation']
self.body.append(self.starttag(node, 'abbr', '', **attrs)) self.body.append(self.starttag(node, 'abbr', '', **attrs))
def depart_abbreviation(self, node): def depart_abbreviation(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</abbr>') self.body.append('</abbr>')
def visit_manpage(self, node): def visit_manpage(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_literal_emphasis(node) self.visit_literal_emphasis(node)
if self.manpages_url: if self.manpages_url:
node['refuri'] = self.manpages_url.format(**node.attributes) node['refuri'] = self.manpages_url.format(**node.attributes)
self.visit_reference(node) self.visit_reference(node)
def depart_manpage(self, node): def depart_manpage(self, node: Element) -> None:
# type: (nodes.Element) -> None
if self.manpages_url: if self.manpages_url:
self.depart_reference(node) self.depart_reference(node)
self.depart_literal_emphasis(node) self.depart_literal_emphasis(node)
# overwritten to add even/odd classes # overwritten to add even/odd classes
def visit_table(self, node): def visit_table(self, node: Element) -> None:
# type: (nodes.Element) -> None
self._table_row_index = 0 self._table_row_index = 0
return super().visit_table(node) return super().visit_table(node)
def visit_row(self, node): def visit_row(self, node: Element) -> None:
# type: (nodes.Element) -> None
self._table_row_index += 1 self._table_row_index += 1
if self._table_row_index % 2 == 0: if self._table_row_index % 2 == 0:
node['classes'].append('row-even') node['classes'].append('row-even')
@ -888,19 +774,16 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
self.body.append(self.starttag(node, 'tr', '')) self.body.append(self.starttag(node, 'tr', ''))
node.column = 0 # type: ignore node.column = 0 # type: ignore
def visit_entry(self, node): def visit_entry(self, node: Element) -> None:
# type: (nodes.Element) -> None
super().visit_entry(node) super().visit_entry(node)
if self.body[-1] == '&nbsp;': if self.body[-1] == '&nbsp;':
self.body[-1] = '&#160;' self.body[-1] = '&#160;'
def visit_field_list(self, node): def visit_field_list(self, node: Element) -> None:
# type: (nodes.Element) -> None
self._fieldlist_row_index = 0 self._fieldlist_row_index = 0
return super().visit_field_list(node) return super().visit_field_list(node)
def visit_field(self, node): def visit_field(self, node: Element) -> None:
# type: (nodes.Element) -> None
self._fieldlist_row_index += 1 self._fieldlist_row_index += 1
if self._fieldlist_row_index % 2 == 0: if self._fieldlist_row_index % 2 == 0:
node['classes'].append('field-even') node['classes'].append('field-even')
@ -908,39 +791,33 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
node['classes'].append('field-odd') node['classes'].append('field-odd')
self.body.append(self.starttag(node, 'tr', '', CLASS='field')) self.body.append(self.starttag(node, 'tr', '', CLASS='field'))
def visit_field_name(self, node): def visit_field_name(self, node: Element) -> None:
# type: (nodes.Element) -> None
context_count = len(self.context) context_count = len(self.context)
super().visit_field_name(node) super().visit_field_name(node)
if context_count != len(self.context): if context_count != len(self.context):
self.context[-1] = self.context[-1].replace('&nbsp;', '&#160;') self.context[-1] = self.context[-1].replace('&nbsp;', '&#160;')
def visit_math(self, node, math_env=''): def visit_math(self, node: Element, math_env: str = '') -> None:
# type: (nodes.Element, str) -> None
name = self.builder.math_renderer_name name = self.builder.math_renderer_name
visit, _ = self.builder.app.registry.html_inline_math_renderers[name] visit, _ = self.builder.app.registry.html_inline_math_renderers[name]
visit(self, node) visit(self, node)
def depart_math(self, node, math_env=''): def depart_math(self, node: Element, math_env: str = '') -> None:
# type: (nodes.Element, str) -> None
name = self.builder.math_renderer_name name = self.builder.math_renderer_name
_, depart = self.builder.app.registry.html_inline_math_renderers[name] _, depart = self.builder.app.registry.html_inline_math_renderers[name]
if depart: if depart:
depart(self, node) depart(self, node)
def visit_math_block(self, node, math_env=''): def visit_math_block(self, node: Element, math_env: str = '') -> None:
# type: (nodes.Element, str) -> None
name = self.builder.math_renderer_name name = self.builder.math_renderer_name
visit, _ = self.builder.app.registry.html_block_math_renderers[name] visit, _ = self.builder.app.registry.html_block_math_renderers[name]
visit(self, node) visit(self, node)
def depart_math_block(self, node, math_env=''): def depart_math_block(self, node: Element, math_env: str = '') -> None:
# type: (nodes.Element, str) -> None
name = self.builder.math_renderer_name name = self.builder.math_renderer_name
_, depart = self.builder.app.registry.html_block_math_renderers[name] _, depart = self.builder.app.registry.html_block_math_renderers[name]
if depart: if depart:
depart(self, node) depart(self, node)
def unknown_visit(self, node): def unknown_visit(self, node: Node) -> None:
# type: (nodes.Node) -> None
raise NotImplementedError('Unknown node: ' + node.__class__.__name__) raise NotImplementedError('Unknown node: ' + node.__class__.__name__)

View File

@ -11,9 +11,11 @@
import os import os
import posixpath import posixpath
import warnings import warnings
from typing import Iterable, cast from typing import cast
from typing import Iterable
from docutils import nodes from docutils import nodes
from docutils.nodes import Element, Node, Text
from docutils.writers.html5_polyglot import HTMLTranslator as BaseTranslator from docutils.writers.html5_polyglot import HTMLTranslator as BaseTranslator
from sphinx import addnodes from sphinx import addnodes
@ -26,8 +28,7 @@ from sphinx.util.images import get_image_size
if False: if False:
# For type annotation # For type annotation
from typing import Any # NOQA from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.builders.html import StandaloneHTMLBuilder # NOQA
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -43,8 +44,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
builder = None # type: StandaloneHTMLBuilder builder = None # type: StandaloneHTMLBuilder
def __init__(self, *args): def __init__(self, *args) -> None:
# type: (Any) -> None
if isinstance(args[0], nodes.document) and isinstance(args[1], Builder): if isinstance(args[0], nodes.document) and isinstance(args[1], Builder):
document, builder = args document, builder = args
else: else:
@ -70,26 +70,21 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
self._fieldlist_row_index = 0 self._fieldlist_row_index = 0
self.required_params_left = 0 self.required_params_left = 0
def visit_start_of_file(self, node): def visit_start_of_file(self, node: Element) -> None:
# type: (nodes.Element) -> None
# only occurs in the single-file builder # only occurs in the single-file builder
self.docnames.append(node['docname']) self.docnames.append(node['docname'])
self.body.append('<span id="document-%s"></span>' % node['docname']) self.body.append('<span id="document-%s"></span>' % node['docname'])
def depart_start_of_file(self, node): def depart_start_of_file(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.docnames.pop() self.docnames.pop()
def visit_desc(self, node): def visit_desc(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(self.starttag(node, 'dl', CLASS=node['objtype'])) self.body.append(self.starttag(node, 'dl', CLASS=node['objtype']))
def depart_desc(self, node): def depart_desc(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</dl>\n\n') self.body.append('</dl>\n\n')
def visit_desc_signature(self, node): def visit_desc_signature(self, node: Element) -> None:
# type: (nodes.Element) -> None
# the id is set automatically # the id is set automatically
self.body.append(self.starttag(node, 'dt')) self.body.append(self.starttag(node, 'dt'))
# anchor for per-desc interactive data # anchor for per-desc interactive data
@ -97,57 +92,45 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
and node['ids'] and node['first']: and node['ids'] and node['first']:
self.body.append('<!--[%s]-->' % node['ids'][0]) self.body.append('<!--[%s]-->' % node['ids'][0])
def depart_desc_signature(self, node): def depart_desc_signature(self, node: Element) -> None:
# type: (nodes.Element) -> None
if not node.get('is_multiline'): if not node.get('is_multiline'):
self.add_permalink_ref(node, _('Permalink to this definition')) self.add_permalink_ref(node, _('Permalink to this definition'))
self.body.append('</dt>\n') self.body.append('</dt>\n')
def visit_desc_signature_line(self, node): def visit_desc_signature_line(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_desc_signature_line(self, node): def depart_desc_signature_line(self, node: Element) -> None:
# type: (nodes.Element) -> None
if node.get('add_permalink'): if node.get('add_permalink'):
# the permalink info is on the parent desc_signature node # the permalink info is on the parent desc_signature node
self.add_permalink_ref(node.parent, _('Permalink to this definition')) self.add_permalink_ref(node.parent, _('Permalink to this definition'))
self.body.append('<br />') self.body.append('<br />')
def visit_desc_addname(self, node): def visit_desc_addname(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(self.starttag(node, 'code', '', CLASS='sig-prename descclassname')) self.body.append(self.starttag(node, 'code', '', CLASS='sig-prename descclassname'))
def depart_desc_addname(self, node): def depart_desc_addname(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</code>') self.body.append('</code>')
def visit_desc_type(self, node): def visit_desc_type(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_desc_type(self, node): def depart_desc_type(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_desc_returns(self, node): def visit_desc_returns(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(' &#x2192; ') self.body.append(' &#x2192; ')
def depart_desc_returns(self, node): def depart_desc_returns(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_desc_name(self, node): def visit_desc_name(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(self.starttag(node, 'code', '', CLASS='sig-name descname')) self.body.append(self.starttag(node, 'code', '', CLASS='sig-name descname'))
def depart_desc_name(self, node): def depart_desc_name(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</code>') self.body.append('</code>')
def visit_desc_parameterlist(self, node): def visit_desc_parameterlist(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('<span class="sig-paren">(</span>') self.body.append('<span class="sig-paren">(</span>')
self.first_param = 1 self.first_param = 1
self.optional_param_level = 0 self.optional_param_level = 0
@ -156,8 +139,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
for c in node.children]) for c in node.children])
self.param_separator = node.child_text_separator self.param_separator = node.child_text_separator
def depart_desc_parameterlist(self, node): def depart_desc_parameterlist(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('<span class="sig-paren">)</span>') self.body.append('<span class="sig-paren">)</span>')
# If required parameters are still to come, then put the comma after # If required parameters are still to come, then put the comma after
@ -166,8 +148,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
# #
# foo([a, ]b, c[, d]) # foo([a, ]b, c[, d])
# #
def visit_desc_parameter(self, node): def visit_desc_parameter(self, node: Element) -> None:
# type: (nodes.Element) -> None
if self.first_param: if self.first_param:
self.first_param = 0 self.first_param = 0
elif not self.required_params_left: elif not self.required_params_left:
@ -177,50 +158,40 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
if not node.hasattr('noemph'): if not node.hasattr('noemph'):
self.body.append('<em class="sig-param">') self.body.append('<em class="sig-param">')
def depart_desc_parameter(self, node): def depart_desc_parameter(self, node: Element) -> None:
# type: (nodes.Element) -> None
if not node.hasattr('noemph'): if not node.hasattr('noemph'):
self.body.append('</em>') self.body.append('</em>')
if self.required_params_left: if self.required_params_left:
self.body.append(self.param_separator) self.body.append(self.param_separator)
def visit_desc_optional(self, node): def visit_desc_optional(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.optional_param_level += 1 self.optional_param_level += 1
self.body.append('<span class="optional">[</span>') self.body.append('<span class="optional">[</span>')
def depart_desc_optional(self, node): def depart_desc_optional(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.optional_param_level -= 1 self.optional_param_level -= 1
self.body.append('<span class="optional">]</span>') self.body.append('<span class="optional">]</span>')
def visit_desc_annotation(self, node): def visit_desc_annotation(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(self.starttag(node, 'em', '', CLASS='property')) self.body.append(self.starttag(node, 'em', '', CLASS='property'))
def depart_desc_annotation(self, node): def depart_desc_annotation(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</em>') self.body.append('</em>')
def visit_desc_content(self, node): def visit_desc_content(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(self.starttag(node, 'dd', '')) self.body.append(self.starttag(node, 'dd', ''))
def depart_desc_content(self, node): def depart_desc_content(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</dd>') self.body.append('</dd>')
def visit_versionmodified(self, node): def visit_versionmodified(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(self.starttag(node, 'div', CLASS=node['type'])) self.body.append(self.starttag(node, 'div', CLASS=node['type']))
def depart_versionmodified(self, node): def depart_versionmodified(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</div>\n') self.body.append('</div>\n')
# overwritten # overwritten
def visit_reference(self, node): def visit_reference(self, node: Element) -> None:
# type: (nodes.Element) -> None
atts = {'class': 'reference'} atts = {'class': 'reference'}
if node.get('internal') or 'refuri' not in node: if node.get('internal') or 'refuri' not in node:
atts['class'] += ' internal' atts['class'] += ' internal'
@ -248,37 +219,30 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
self.body.append(('%s' + self.secnumber_suffix) % self.body.append(('%s' + self.secnumber_suffix) %
'.'.join(map(str, node['secnumber']))) '.'.join(map(str, node['secnumber'])))
def visit_number_reference(self, node): def visit_number_reference(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_reference(node) self.visit_reference(node)
def depart_number_reference(self, node): def depart_number_reference(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_reference(node) self.depart_reference(node)
# overwritten -- we don't want source comments to show up in the HTML # overwritten -- we don't want source comments to show up in the HTML
def visit_comment(self, node): # type: ignore def visit_comment(self, node: Element) -> None: # type: ignore
# type: (nodes.Element) -> None
raise nodes.SkipNode raise nodes.SkipNode
# overwritten # overwritten
def visit_admonition(self, node, name=''): def visit_admonition(self, node: Element, name: str = '') -> None:
# type: (nodes.Element, str) -> None
self.body.append(self.starttag( self.body.append(self.starttag(
node, 'div', CLASS=('admonition ' + name))) node, 'div', CLASS=('admonition ' + name)))
if name: if name:
node.insert(0, nodes.title(name, admonitionlabels[name])) node.insert(0, nodes.title(name, admonitionlabels[name]))
def visit_seealso(self, node): def visit_seealso(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_admonition(node, 'seealso') self.visit_admonition(node, 'seealso')
def depart_seealso(self, node): def depart_seealso(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_admonition(node) self.depart_admonition(node)
def add_secnumber(self, node): def add_secnumber(self, node: Element) -> None:
# type: (nodes.Element) -> None
if node.get('secnumber'): if node.get('secnumber'):
self.body.append('.'.join(map(str, node['secnumber'])) + self.body.append('.'.join(map(str, node['secnumber'])) +
self.secnumber_suffix) self.secnumber_suffix)
@ -297,10 +261,8 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
self.body.append('.'.join(map(str, numbers)) + self.body.append('.'.join(map(str, numbers)) +
self.secnumber_suffix) self.secnumber_suffix)
def add_fignumber(self, node): def add_fignumber(self, node: Element) -> None:
# type: (nodes.Element) -> None def append_fignumber(figtype: str, figure_id: str) -> None:
def append_fignumber(figtype, figure_id):
# type: (str, str) -> None
if self.builder.name == 'singlehtml': if self.builder.name == 'singlehtml':
key = "%s/%s" % (self.docnames[-1], figtype) key = "%s/%s" % (self.docnames[-1], figtype)
else: else:
@ -325,55 +287,47 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
else: else:
append_fignumber(figtype, node['ids'][0]) append_fignumber(figtype, node['ids'][0])
def add_permalink_ref(self, node, title): def add_permalink_ref(self, node: Element, title: str) -> None:
# type: (nodes.Element, str) -> None
if node['ids'] and self.permalink_text and self.builder.add_permalinks: if node['ids'] and self.permalink_text and self.builder.add_permalinks:
format = '<a class="headerlink" href="#%s" title="%s">%s</a>' format = '<a class="headerlink" href="#%s" title="%s">%s</a>'
self.body.append(format % (node['ids'][0], title, self.permalink_text)) self.body.append(format % (node['ids'][0], title, self.permalink_text))
# overwritten # overwritten
def visit_bullet_list(self, node): def visit_bullet_list(self, node: Element) -> None:
# type: (nodes.Element) -> None
if len(node) == 1 and isinstance(node[0], addnodes.toctree): if len(node) == 1 and isinstance(node[0], addnodes.toctree):
# avoid emitting empty <ul></ul> # avoid emitting empty <ul></ul>
raise nodes.SkipNode raise nodes.SkipNode
super().visit_bullet_list(node) super().visit_bullet_list(node)
# overwritten # overwritten
def visit_definition(self, node): def visit_definition(self, node: Element) -> None:
# type: (nodes.Element) -> None
# don't insert </dt> here. # don't insert </dt> here.
self.body.append(self.starttag(node, 'dd', '')) self.body.append(self.starttag(node, 'dd', ''))
# overwritten # overwritten
def depart_definition(self, node): def depart_definition(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</dd>\n') self.body.append('</dd>\n')
# overwritten # overwritten
def visit_classifier(self, node): def visit_classifier(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(self.starttag(node, 'span', '', CLASS='classifier')) self.body.append(self.starttag(node, 'span', '', CLASS='classifier'))
# overwritten # overwritten
def depart_classifier(self, node): def depart_classifier(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</span>') self.body.append('</span>')
next_node = node.next_node(descend=False, siblings=True) # type: nodes.Node next_node = node.next_node(descend=False, siblings=True) # type: Node
if not isinstance(next_node, nodes.classifier): if not isinstance(next_node, nodes.classifier):
# close `<dt>` tag at the tail of classifiers # close `<dt>` tag at the tail of classifiers
self.body.append('</dt>') self.body.append('</dt>')
# overwritten # overwritten
def visit_term(self, node): def visit_term(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(self.starttag(node, 'dt', '')) self.body.append(self.starttag(node, 'dt', ''))
# overwritten # overwritten
def depart_term(self, node): def depart_term(self, node: Element) -> None:
# type: (nodes.Element) -> None next_node = node.next_node(descend=False, siblings=True) # type: Node
next_node = node.next_node(descend=False, siblings=True) # type: nodes.Node
if isinstance(next_node, nodes.classifier): if isinstance(next_node, nodes.classifier):
# Leave the end tag to `self.depart_classifier()`, in case # Leave the end tag to `self.depart_classifier()`, in case
# there's a classifier. # there's a classifier.
@ -382,16 +336,14 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
self.body.append('</dt>') self.body.append('</dt>')
# overwritten # overwritten
def visit_title(self, node): def visit_title(self, node: Element) -> None:
# type: (nodes.Element) -> None
super().visit_title(node) super().visit_title(node)
self.add_secnumber(node) self.add_secnumber(node)
self.add_fignumber(node.parent) self.add_fignumber(node.parent)
if isinstance(node.parent, nodes.table): if isinstance(node.parent, nodes.table):
self.body.append('<span class="caption-text">') self.body.append('<span class="caption-text">')
def depart_title(self, node): def depart_title(self, node: Element) -> None:
# type: (nodes.Element) -> None
close_tag = self.context[-1] close_tag = self.context[-1]
if (self.permalink_text and self.builder.add_permalinks and if (self.permalink_text and self.builder.add_permalinks and
node.parent.hasattr('ids') and node.parent['ids']): node.parent.hasattr('ids') and node.parent['ids']):
@ -413,8 +365,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
super().depart_title(node) super().depart_title(node)
# overwritten # overwritten
def visit_literal_block(self, node): def visit_literal_block(self, node: Element) -> None:
# type: (nodes.Element) -> None
if node.rawsource != node.astext(): if node.rawsource != node.astext():
# most probably a parsed-literal block -- don't highlight # most probably a parsed-literal block -- don't highlight
return super().visit_literal_block(node) return super().visit_literal_block(node)
@ -438,8 +389,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
self.body.append(starttag + highlighted + '</div>\n') self.body.append(starttag + highlighted + '</div>\n')
raise nodes.SkipNode raise nodes.SkipNode
def visit_caption(self, node): def visit_caption(self, node: Element) -> None:
# type: (nodes.Element) -> None
if isinstance(node.parent, nodes.container) and node.parent.get('literal_block'): if isinstance(node.parent, nodes.container) and node.parent.get('literal_block'):
self.body.append('<div class="code-block-caption">') self.body.append('<div class="code-block-caption">')
else: else:
@ -447,8 +397,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
self.add_fignumber(node.parent) self.add_fignumber(node.parent)
self.body.append(self.starttag(node, 'span', '', CLASS='caption-text')) self.body.append(self.starttag(node, 'span', '', CLASS='caption-text'))
def depart_caption(self, node): def depart_caption(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</span>') self.body.append('</span>')
# append permalink if available # append permalink if available
@ -464,22 +413,18 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
else: else:
super().depart_caption(node) super().depart_caption(node)
def visit_doctest_block(self, node): def visit_doctest_block(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_literal_block(node) self.visit_literal_block(node)
# overwritten to add the <div> (for XHTML compliance) # overwritten to add the <div> (for XHTML compliance)
def visit_block_quote(self, node): def visit_block_quote(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(self.starttag(node, 'blockquote') + '<div>') self.body.append(self.starttag(node, 'blockquote') + '<div>')
def depart_block_quote(self, node): def depart_block_quote(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</div></blockquote>\n') self.body.append('</div></blockquote>\n')
# overwritten # overwritten
def visit_literal(self, node): def visit_literal(self, node: Element) -> None:
# type: (nodes.Element) -> None
if 'kbd' in node['classes']: if 'kbd' in node['classes']:
self.body.append(self.starttag(node, 'kbd', '', self.body.append(self.starttag(node, 'kbd', '',
CLASS='docutils literal notranslate')) CLASS='docutils literal notranslate'))
@ -488,16 +433,14 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
CLASS='docutils literal notranslate')) CLASS='docutils literal notranslate'))
self.protect_literal_text += 1 self.protect_literal_text += 1
def depart_literal(self, node): def depart_literal(self, node: Element) -> None:
# type: (nodes.Element) -> None
if 'kbd' in node['classes']: if 'kbd' in node['classes']:
self.body.append('</kbd>') self.body.append('</kbd>')
else: else:
self.protect_literal_text -= 1 self.protect_literal_text -= 1
self.body.append('</code>') self.body.append('</code>')
def visit_productionlist(self, node): def visit_productionlist(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(self.starttag(node, 'pre')) self.body.append(self.starttag(node, 'pre'))
names = [] names = []
productionlist = cast(Iterable[addnodes.production], node) productionlist = cast(Iterable[addnodes.production], node)
@ -517,37 +460,29 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
self.body.append('</pre>\n') self.body.append('</pre>\n')
raise nodes.SkipNode raise nodes.SkipNode
def depart_productionlist(self, node): def depart_productionlist(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_production(self, node): def visit_production(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_production(self, node): def depart_production(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_centered(self, node): def visit_centered(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(self.starttag(node, 'p', CLASS="centered") + self.body.append(self.starttag(node, 'p', CLASS="centered") +
'<strong>') '<strong>')
def depart_centered(self, node): def depart_centered(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</strong></p>') self.body.append('</strong></p>')
def visit_compact_paragraph(self, node): def visit_compact_paragraph(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_compact_paragraph(self, node): def depart_compact_paragraph(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_download_reference(self, node): def visit_download_reference(self, node: Element) -> None:
# type: (nodes.Element) -> None
atts = {'class': 'reference download', atts = {'class': 'reference download',
'download': ''} 'download': ''}
@ -566,13 +501,11 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
else: else:
self.context.append('') self.context.append('')
def depart_download_reference(self, node): def depart_download_reference(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(self.context.pop()) self.body.append(self.context.pop())
# overwritten # overwritten
def visit_image(self, node): def visit_image(self, node: Element) -> None:
# type: (nodes.Element) -> None
olduri = node['uri'] olduri = node['uri']
# rewrite the URI if the environment knows about it # rewrite the URI if the environment knows about it
if olduri in self.builder.images: if olduri in self.builder.images:
@ -613,62 +546,49 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
super().visit_image(node) super().visit_image(node)
# overwritten # overwritten
def depart_image(self, node): def depart_image(self, node: Element) -> None:
# type: (nodes.Element) -> None
if node['uri'].lower().endswith(('svg', 'svgz')): if node['uri'].lower().endswith(('svg', 'svgz')):
self.body.append(self.context.pop()) self.body.append(self.context.pop())
else: else:
super().depart_image(node) super().depart_image(node)
def visit_toctree(self, node): def visit_toctree(self, node: Element) -> None:
# type: (nodes.Element) -> None
# this only happens when formatting a toc from env.tocs -- in this # this only happens when formatting a toc from env.tocs -- in this
# case we don't want to include the subtree # case we don't want to include the subtree
raise nodes.SkipNode raise nodes.SkipNode
def visit_index(self, node): def visit_index(self, node: Element) -> None:
# type: (nodes.Element) -> None
raise nodes.SkipNode raise nodes.SkipNode
def visit_tabular_col_spec(self, node): def visit_tabular_col_spec(self, node: Element) -> None:
# type: (nodes.Element) -> None
raise nodes.SkipNode raise nodes.SkipNode
def visit_glossary(self, node): def visit_glossary(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_glossary(self, node): def depart_glossary(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_acks(self, node): def visit_acks(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_acks(self, node): def depart_acks(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_hlist(self, node): def visit_hlist(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('<table class="hlist"><tr>') self.body.append('<table class="hlist"><tr>')
def depart_hlist(self, node): def depart_hlist(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</tr></table>\n') self.body.append('</tr></table>\n')
def visit_hlistcol(self, node): def visit_hlistcol(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('<td>') self.body.append('<td>')
def depart_hlistcol(self, node): def depart_hlistcol(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</td>') self.body.append('</td>')
# overwritten # overwritten
def visit_Text(self, node): def visit_Text(self, node: Text) -> None:
# type: (nodes.Text) -> None
text = node.astext() text = node.astext()
encoded = self.encode(text) encoded = self.encode(text)
if self.protect_literal_text: if self.protect_literal_text:
@ -689,122 +609,95 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
encoded = self.cloak_email(encoded) encoded = self.cloak_email(encoded)
self.body.append(encoded) self.body.append(encoded)
def visit_note(self, node): def visit_note(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_admonition(node, 'note') self.visit_admonition(node, 'note')
def depart_note(self, node): def depart_note(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_admonition(node) self.depart_admonition(node)
def visit_warning(self, node): def visit_warning(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_admonition(node, 'warning') self.visit_admonition(node, 'warning')
def depart_warning(self, node): def depart_warning(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_admonition(node) self.depart_admonition(node)
def visit_attention(self, node): def visit_attention(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_admonition(node, 'attention') self.visit_admonition(node, 'attention')
def depart_attention(self, node): def depart_attention(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_admonition(node) self.depart_admonition(node)
def visit_caution(self, node): def visit_caution(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_admonition(node, 'caution') self.visit_admonition(node, 'caution')
def depart_caution(self, node): def depart_caution(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_admonition(node) self.depart_admonition(node)
def visit_danger(self, node): def visit_danger(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_admonition(node, 'danger') self.visit_admonition(node, 'danger')
def depart_danger(self, node): def depart_danger(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_admonition(node) self.depart_admonition(node)
def visit_error(self, node): def visit_error(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_admonition(node, 'error') self.visit_admonition(node, 'error')
def depart_error(self, node): def depart_error(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_admonition(node) self.depart_admonition(node)
def visit_hint(self, node): def visit_hint(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_admonition(node, 'hint') self.visit_admonition(node, 'hint')
def depart_hint(self, node): def depart_hint(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_admonition(node) self.depart_admonition(node)
def visit_important(self, node): def visit_important(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_admonition(node, 'important') self.visit_admonition(node, 'important')
def depart_important(self, node): def depart_important(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_admonition(node) self.depart_admonition(node)
def visit_tip(self, node): def visit_tip(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_admonition(node, 'tip') self.visit_admonition(node, 'tip')
def depart_tip(self, node): def depart_tip(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_admonition(node) self.depart_admonition(node)
def visit_literal_emphasis(self, node): def visit_literal_emphasis(self, node: Element) -> None:
# type: (nodes.Element) -> None
return self.visit_emphasis(node) return self.visit_emphasis(node)
def depart_literal_emphasis(self, node): def depart_literal_emphasis(self, node: Element) -> None:
# type: (nodes.Element) -> None
return self.depart_emphasis(node) return self.depart_emphasis(node)
def visit_literal_strong(self, node): def visit_literal_strong(self, node: Element) -> None:
# type: (nodes.Element) -> None
return self.visit_strong(node) return self.visit_strong(node)
def depart_literal_strong(self, node): def depart_literal_strong(self, node: Element) -> None:
# type: (nodes.Element) -> None
return self.depart_strong(node) return self.depart_strong(node)
def visit_abbreviation(self, node): def visit_abbreviation(self, node: Element) -> None:
# type: (nodes.Element) -> None
attrs = {} attrs = {}
if node.hasattr('explanation'): if node.hasattr('explanation'):
attrs['title'] = node['explanation'] attrs['title'] = node['explanation']
self.body.append(self.starttag(node, 'abbr', '', **attrs)) self.body.append(self.starttag(node, 'abbr', '', **attrs))
def depart_abbreviation(self, node): def depart_abbreviation(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('</abbr>') self.body.append('</abbr>')
def visit_manpage(self, node): def visit_manpage(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_literal_emphasis(node) self.visit_literal_emphasis(node)
if self.manpages_url: if self.manpages_url:
node['refuri'] = self.manpages_url.format(**node.attributes) node['refuri'] = self.manpages_url.format(**node.attributes)
self.visit_reference(node) self.visit_reference(node)
def depart_manpage(self, node): def depart_manpage(self, node: Element) -> None:
# type: (nodes.Element) -> None
if self.manpages_url: if self.manpages_url:
self.depart_reference(node) self.depart_reference(node)
self.depart_literal_emphasis(node) self.depart_literal_emphasis(node)
# overwritten to add even/odd classes # overwritten to add even/odd classes
def generate_targets_for_table(self, node): def generate_targets_for_table(self, node: Element) -> None:
# type: (nodes.Element) -> None
"""Generate hyperlink targets for tables. """Generate hyperlink targets for tables.
Original visit_table() generates hyperlink targets inside table tags Original visit_table() generates hyperlink targets inside table tags
@ -817,8 +710,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
self.body.append('<span id="%s"></span>' % id) self.body.append('<span id="%s"></span>' % id)
node['ids'].remove(id) node['ids'].remove(id)
def visit_table(self, node): def visit_table(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.generate_targets_for_table(node) self.generate_targets_for_table(node)
self._table_row_index = 0 self._table_row_index = 0
@ -830,8 +722,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
tag = self.starttag(node, 'table', CLASS=' '.join(classes)) tag = self.starttag(node, 'table', CLASS=' '.join(classes))
self.body.append(tag) self.body.append(tag)
def visit_row(self, node): def visit_row(self, node: Element) -> None:
# type: (nodes.Element) -> None
self._table_row_index += 1 self._table_row_index += 1
if self._table_row_index % 2 == 0: if self._table_row_index % 2 == 0:
node['classes'].append('row-even') node['classes'].append('row-even')
@ -840,45 +731,38 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
self.body.append(self.starttag(node, 'tr', '')) self.body.append(self.starttag(node, 'tr', ''))
node.column = 0 # type: ignore node.column = 0 # type: ignore
def visit_field_list(self, node): def visit_field_list(self, node: Element) -> None:
# type: (nodes.Element) -> None
self._fieldlist_row_index = 0 self._fieldlist_row_index = 0
return super().visit_field_list(node) return super().visit_field_list(node)
def visit_field(self, node): def visit_field(self, node: Element) -> None:
# type: (nodes.Element) -> None
self._fieldlist_row_index += 1 self._fieldlist_row_index += 1
if self._fieldlist_row_index % 2 == 0: if self._fieldlist_row_index % 2 == 0:
node['classes'].append('field-even') node['classes'].append('field-even')
else: else:
node['classes'].append('field-odd') node['classes'].append('field-odd')
def visit_math(self, node, math_env=''): def visit_math(self, node: Element, math_env: str = '') -> None:
# type: (nodes.Element, str) -> None
name = self.builder.math_renderer_name name = self.builder.math_renderer_name
visit, _ = self.builder.app.registry.html_inline_math_renderers[name] visit, _ = self.builder.app.registry.html_inline_math_renderers[name]
visit(self, node) visit(self, node)
def depart_math(self, node, math_env=''): def depart_math(self, node: Element, math_env: str = '') -> None:
# type: (nodes.Element, str) -> None
name = self.builder.math_renderer_name name = self.builder.math_renderer_name
_, depart = self.builder.app.registry.html_inline_math_renderers[name] _, depart = self.builder.app.registry.html_inline_math_renderers[name]
if depart: if depart:
depart(self, node) depart(self, node)
def visit_math_block(self, node, math_env=''): def visit_math_block(self, node: Element, math_env: str = '') -> None:
# type: (nodes.Element, str) -> None
name = self.builder.math_renderer_name name = self.builder.math_renderer_name
visit, _ = self.builder.app.registry.html_block_math_renderers[name] visit, _ = self.builder.app.registry.html_block_math_renderers[name]
visit(self, node) visit(self, node)
def depart_math_block(self, node, math_env=''): def depart_math_block(self, node: Element, math_env: str = '') -> None:
# type: (nodes.Element, str) -> None
name = self.builder.math_renderer_name name = self.builder.math_renderer_name
_, depart = self.builder.app.registry.html_block_math_renderers[name] _, depart = self.builder.app.registry.html_block_math_renderers[name]
if depart: if depart:
depart(self, node) depart(self, node)
def unknown_visit(self, node): def unknown_visit(self, node: Node) -> None:
# type: (nodes.Node) -> None
raise NotImplementedError('Unknown node: ' + node.__class__.__name__) raise NotImplementedError('Unknown node: ' + node.__class__.__name__)

File diff suppressed because it is too large Load Diff

View File

@ -9,9 +9,11 @@
""" """
import warnings import warnings
from typing import Iterable, cast from typing import Any, Dict, Iterable
from typing import cast
from docutils import nodes from docutils import nodes
from docutils.nodes import Element, Node, TextElement
from docutils.writers.manpage import ( from docutils.writers.manpage import (
Writer, Writer,
Translator as BaseTranslator Translator as BaseTranslator
@ -26,21 +28,16 @@ from sphinx.util.docutils import SphinxTranslator
from sphinx.util.i18n import format_date from sphinx.util.i18n import format_date
from sphinx.util.nodes import NodeMatcher from sphinx.util.nodes import NodeMatcher
if False:
# For type annotation
from typing import Any, Dict # NOQA
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class ManualPageWriter(Writer): class ManualPageWriter(Writer):
def __init__(self, builder): def __init__(self, builder: Builder) -> None:
# type: (Builder) -> None
super().__init__() super().__init__()
self.builder = builder self.builder = builder
def translate(self): def translate(self) -> None:
# type: () -> None
transform = NestedInlineTransform(self.document) transform = NestedInlineTransform(self.document)
transform.apply() transform.apply()
visitor = self.builder.create_translator(self.document, self.builder) visitor = self.builder.create_translator(self.document, self.builder)
@ -60,14 +57,12 @@ class NestedInlineTransform:
<strong>foo=</strong><emphasis>var</emphasis> <strong>foo=</strong><emphasis>var</emphasis>
<strong>&bar=</strong><emphasis>2</emphasis> <strong>&bar=</strong><emphasis>2</emphasis>
""" """
def __init__(self, document): def __init__(self, document: nodes.document) -> None:
# type: (nodes.document) -> None
self.document = document self.document = document
def apply(self, **kwargs): def apply(self, **kwargs) -> None:
# type: (Any) -> None
matcher = NodeMatcher(nodes.literal, nodes.emphasis, nodes.strong) matcher = NodeMatcher(nodes.literal, nodes.emphasis, nodes.strong)
for node in self.document.traverse(matcher): # type: nodes.TextElement for node in self.document.traverse(matcher): # type: TextElement
if any(matcher(subnode) for subnode in node): if any(matcher(subnode) for subnode in node):
pos = node.parent.index(node) pos = node.parent.index(node)
for subnode in reversed(node[1:]): for subnode in reversed(node[1:]):
@ -86,8 +81,7 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator):
_docinfo = {} # type: Dict[str, Any] _docinfo = {} # type: Dict[str, Any]
def __init__(self, *args): def __init__(self, *args) -> None:
# type: (Any) -> None
if isinstance(args[0], nodes.document) and isinstance(args[1], Builder): if isinstance(args[0], nodes.document) and isinstance(args[1], Builder):
document, builder = args document, builder = args
else: else:
@ -126,153 +120,120 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator):
self.language.labels[label] = self.deunicode(translation) # type: ignore self.language.labels[label] = self.deunicode(translation) # type: ignore
# overwritten -- added quotes around all .TH arguments # overwritten -- added quotes around all .TH arguments
def header(self): def header(self) -> str:
# type: () -> str
tmpl = (".TH \"%(title_upper)s\" \"%(manual_section)s\"" tmpl = (".TH \"%(title_upper)s\" \"%(manual_section)s\""
" \"%(date)s\" \"%(version)s\" \"%(manual_group)s\"\n" " \"%(date)s\" \"%(version)s\" \"%(manual_group)s\"\n"
".SH NAME\n" ".SH NAME\n"
"%(title)s \\- %(subtitle)s\n") "%(title)s \\- %(subtitle)s\n")
return tmpl % self._docinfo return tmpl % self._docinfo
def visit_start_of_file(self, node): def visit_start_of_file(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_start_of_file(self, node): def depart_start_of_file(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_desc(self, node): def visit_desc(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_definition_list(node) self.visit_definition_list(node)
def depart_desc(self, node): def depart_desc(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_definition_list(node) self.depart_definition_list(node)
def visit_desc_signature(self, node): def visit_desc_signature(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_definition_list_item(node) self.visit_definition_list_item(node)
self.visit_term(node) self.visit_term(node)
def depart_desc_signature(self, node): def depart_desc_signature(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_term(node) self.depart_term(node)
def visit_desc_signature_line(self, node): def visit_desc_signature_line(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_desc_signature_line(self, node): def depart_desc_signature_line(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(' ') self.body.append(' ')
def visit_desc_addname(self, node): def visit_desc_addname(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_desc_addname(self, node): def depart_desc_addname(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_desc_type(self, node): def visit_desc_type(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_desc_type(self, node): def depart_desc_type(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_desc_returns(self, node): def visit_desc_returns(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(' -> ') self.body.append(' -> ')
def depart_desc_returns(self, node): def depart_desc_returns(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_desc_name(self, node): def visit_desc_name(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_desc_name(self, node): def depart_desc_name(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_desc_parameterlist(self, node): def visit_desc_parameterlist(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('(') self.body.append('(')
self.first_param = 1 self.first_param = 1
def depart_desc_parameterlist(self, node): def depart_desc_parameterlist(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(')') self.body.append(')')
def visit_desc_parameter(self, node): def visit_desc_parameter(self, node: Element) -> None:
# type: (nodes.Element) -> None
if not self.first_param: if not self.first_param:
self.body.append(', ') self.body.append(', ')
else: else:
self.first_param = 0 self.first_param = 0
def depart_desc_parameter(self, node): def depart_desc_parameter(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_desc_optional(self, node): def visit_desc_optional(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('[') self.body.append('[')
def depart_desc_optional(self, node): def depart_desc_optional(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(']') self.body.append(']')
def visit_desc_annotation(self, node): def visit_desc_annotation(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_desc_annotation(self, node): def depart_desc_annotation(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_desc_content(self, node): def visit_desc_content(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_definition(node) self.visit_definition(node)
def depart_desc_content(self, node): def depart_desc_content(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_definition(node) self.depart_definition(node)
def visit_versionmodified(self, node): def visit_versionmodified(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_paragraph(node) self.visit_paragraph(node)
def depart_versionmodified(self, node): def depart_versionmodified(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_paragraph(node) self.depart_paragraph(node)
# overwritten -- don't make whole of term bold if it includes strong node # overwritten -- don't make whole of term bold if it includes strong node
def visit_term(self, node): def visit_term(self, node: Element) -> None:
# type: (nodes.Element) -> None
if node.traverse(nodes.strong): if node.traverse(nodes.strong):
self.body.append('\n') self.body.append('\n')
else: else:
super().visit_term(node) super().visit_term(node)
# overwritten -- we don't want source comments to show up # overwritten -- we don't want source comments to show up
def visit_comment(self, node): # type: ignore def visit_comment(self, node: Element) -> None: # type: ignore
# type: (nodes.Element) -> None
raise nodes.SkipNode raise nodes.SkipNode
# overwritten -- added ensure_eol() # overwritten -- added ensure_eol()
def visit_footnote(self, node): def visit_footnote(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.ensure_eol() self.ensure_eol()
super().visit_footnote(node) super().visit_footnote(node)
# overwritten -- handle footnotes rubric # overwritten -- handle footnotes rubric
def visit_rubric(self, node): def visit_rubric(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.ensure_eol() self.ensure_eol()
if len(node) == 1 and node.astext() in ('Footnotes', _('Footnotes')): if len(node) == 1 and node.astext() in ('Footnotes', _('Footnotes')):
self.body.append('.SH ' + self.deunicode(node.astext()).upper() + '\n') self.body.append('.SH ' + self.deunicode(node.astext()).upper() + '\n')
@ -280,20 +241,16 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator):
else: else:
self.body.append('.sp\n') self.body.append('.sp\n')
def depart_rubric(self, node): def depart_rubric(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('\n') self.body.append('\n')
def visit_seealso(self, node): def visit_seealso(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_admonition(node, 'seealso') self.visit_admonition(node, 'seealso')
def depart_seealso(self, node): def depart_seealso(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_admonition(node) self.depart_admonition(node)
def visit_productionlist(self, node): def visit_productionlist(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.ensure_eol() self.ensure_eol()
names = [] names = []
self.in_productionlist += 1 self.in_productionlist += 1
@ -318,25 +275,21 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator):
self.in_productionlist -= 1 self.in_productionlist -= 1
raise nodes.SkipNode raise nodes.SkipNode
def visit_production(self, node): def visit_production(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_production(self, node): def depart_production(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
# overwritten -- don't emit a warning for images # overwritten -- don't emit a warning for images
def visit_image(self, node): def visit_image(self, node: Element) -> None:
# type: (nodes.Element) -> None
if 'alt' in node.attributes: if 'alt' in node.attributes:
self.body.append(_('[image: %s]') % node['alt'] + '\n') self.body.append(_('[image: %s]') % node['alt'] + '\n')
self.body.append(_('[image]') + '\n') self.body.append(_('[image]') + '\n')
raise nodes.SkipNode raise nodes.SkipNode
# overwritten -- don't visit inner marked up nodes # overwritten -- don't visit inner marked up nodes
def visit_reference(self, node): def visit_reference(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append(self.defs['reference'][0]) self.body.append(self.defs['reference'][0])
# avoid repeating escaping code... fine since # avoid repeating escaping code... fine since
# visit_Text calls astext() and only works on that afterwards # visit_Text calls astext() and only works on that afterwards
@ -357,59 +310,46 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator):
'>']) '>'])
raise nodes.SkipNode raise nodes.SkipNode
def visit_number_reference(self, node): def visit_number_reference(self, node: Element) -> None:
# type: (nodes.Element) -> None
text = nodes.Text(node.get('title', '#')) text = nodes.Text(node.get('title', '#'))
self.visit_Text(text) self.visit_Text(text)
raise nodes.SkipNode raise nodes.SkipNode
def visit_centered(self, node): def visit_centered(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.ensure_eol() self.ensure_eol()
self.body.append('.sp\n.ce\n') self.body.append('.sp\n.ce\n')
def depart_centered(self, node): def depart_centered(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.body.append('\n.ce 0\n') self.body.append('\n.ce 0\n')
def visit_compact_paragraph(self, node): def visit_compact_paragraph(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_compact_paragraph(self, node): def depart_compact_paragraph(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_download_reference(self, node): def visit_download_reference(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_download_reference(self, node): def depart_download_reference(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_toctree(self, node): def visit_toctree(self, node: Element) -> None:
# type: (nodes.Element) -> None
raise nodes.SkipNode raise nodes.SkipNode
def visit_index(self, node): def visit_index(self, node: Element) -> None:
# type: (nodes.Element) -> None
raise nodes.SkipNode raise nodes.SkipNode
def visit_tabular_col_spec(self, node): def visit_tabular_col_spec(self, node: Element) -> None:
# type: (nodes.Element) -> None
raise nodes.SkipNode raise nodes.SkipNode
def visit_glossary(self, node): def visit_glossary(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_glossary(self, node): def depart_glossary(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_acks(self, node): def visit_acks(self, node: Element) -> None:
# type: (nodes.Element) -> None
bullet_list = cast(nodes.bullet_list, node[0]) bullet_list = cast(nodes.bullet_list, node[0])
list_items = cast(Iterable[nodes.list_item], bullet_list) list_items = cast(Iterable[nodes.list_item], bullet_list)
self.ensure_eol() self.ensure_eol()
@ -419,72 +359,57 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator):
self.body.append('\n') self.body.append('\n')
raise nodes.SkipNode raise nodes.SkipNode
def visit_hlist(self, node): def visit_hlist(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_bullet_list(node) self.visit_bullet_list(node)
def depart_hlist(self, node): def depart_hlist(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_bullet_list(node) self.depart_bullet_list(node)
def visit_hlistcol(self, node): def visit_hlistcol(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_hlistcol(self, node): def depart_hlistcol(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_literal_emphasis(self, node): def visit_literal_emphasis(self, node: Element) -> None:
# type: (nodes.Element) -> None
return self.visit_emphasis(node) return self.visit_emphasis(node)
def depart_literal_emphasis(self, node): def depart_literal_emphasis(self, node: Element) -> None:
# type: (nodes.Element) -> None
return self.depart_emphasis(node) return self.depart_emphasis(node)
def visit_literal_strong(self, node): def visit_literal_strong(self, node: Element) -> None:
# type: (nodes.Element) -> None
return self.visit_strong(node) return self.visit_strong(node)
def depart_literal_strong(self, node): def depart_literal_strong(self, node: Element) -> None:
# type: (nodes.Element) -> None
return self.depart_strong(node) return self.depart_strong(node)
def visit_abbreviation(self, node): def visit_abbreviation(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_abbreviation(self, node): def depart_abbreviation(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_manpage(self, node): def visit_manpage(self, node: Element) -> None:
# type: (nodes.Element) -> None
return self.visit_strong(node) return self.visit_strong(node)
def depart_manpage(self, node): def depart_manpage(self, node: Element) -> None:
# type: (nodes.Element) -> None
return self.depart_strong(node) return self.depart_strong(node)
# overwritten: handle section titles better than in 0.6 release # overwritten: handle section titles better than in 0.6 release
def visit_caption(self, node): def visit_caption(self, node: Element) -> None:
# type: (nodes.Element) -> None
if isinstance(node.parent, nodes.container) and node.parent.get('literal_block'): if isinstance(node.parent, nodes.container) and node.parent.get('literal_block'):
self.body.append('.sp\n') self.body.append('.sp\n')
else: else:
super().visit_caption(node) super().visit_caption(node)
def depart_caption(self, node): def depart_caption(self, node: Element) -> None:
# type: (nodes.Element) -> None
if isinstance(node.parent, nodes.container) and node.parent.get('literal_block'): if isinstance(node.parent, nodes.container) and node.parent.get('literal_block'):
self.body.append('\n') self.body.append('\n')
else: else:
super().depart_caption(node) super().depart_caption(node)
# overwritten: handle section titles better than in 0.6 release # overwritten: handle section titles better than in 0.6 release
def visit_title(self, node): def visit_title(self, node: Element) -> None:
# type: (nodes.Element) -> None
if isinstance(node.parent, addnodes.seealso): if isinstance(node.parent, addnodes.seealso):
self.body.append('.IP "') self.body.append('.IP "')
return return
@ -498,47 +423,37 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator):
raise nodes.SkipNode raise nodes.SkipNode
return super().visit_title(node) return super().visit_title(node)
def depart_title(self, node): def depart_title(self, node: Element) -> None:
# type: (nodes.Element) -> None
if isinstance(node.parent, addnodes.seealso): if isinstance(node.parent, addnodes.seealso):
self.body.append('"\n') self.body.append('"\n')
return return
return super().depart_title(node) return super().depart_title(node)
def visit_raw(self, node): def visit_raw(self, node: Element) -> None:
# type: (nodes.Element) -> None
if 'manpage' in node.get('format', '').split(): if 'manpage' in node.get('format', '').split():
self.body.append(node.astext()) self.body.append(node.astext())
raise nodes.SkipNode raise nodes.SkipNode
def visit_meta(self, node): def visit_meta(self, node: Element) -> None:
# type: (nodes.Element) -> None
raise nodes.SkipNode raise nodes.SkipNode
def visit_inline(self, node): def visit_inline(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_inline(self, node): def depart_inline(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_math(self, node): def visit_math(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def depart_math(self, node): def depart_math(self, node: Element) -> None:
# type: (nodes.Element) -> None
pass pass
def visit_math_block(self, node): def visit_math_block(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.visit_centered(node) self.visit_centered(node)
def depart_math_block(self, node): def depart_math_block(self, node: Element) -> None:
# type: (nodes.Element) -> None
self.depart_centered(node) self.depart_centered(node)
def unknown_visit(self, node): def unknown_visit(self, node: Node) -> None:
# type: (nodes.Node) -> None
raise NotImplementedError('Unknown node: ' + node.__class__.__name__) raise NotImplementedError('Unknown node: ' + node.__class__.__name__)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -10,22 +10,16 @@
from docutils.writers.docutils_xml import Writer as BaseXMLWriter from docutils.writers.docutils_xml import Writer as BaseXMLWriter
if False: from sphinx.builders import Builder
# For type annotation
from typing import Any # NOQA
from sphinx.builders import Builder # NOQA
class XMLWriter(BaseXMLWriter): class XMLWriter(BaseXMLWriter):
def __init__(self, builder: Builder) -> None:
def __init__(self, builder):
# type: (Builder) -> None
super().__init__() super().__init__()
self.builder = builder self.builder = builder
self.translator_class = self.builder.get_translator_class() self.translator_class = self.builder.get_translator_class()
def translate(self, *args, **kwargs): def translate(self, *args, **kwargs) -> None:
# type: (Any, Any) -> None
self.document.settings.newlines = \ self.document.settings.newlines = \
self.document.settings.indents = \ self.document.settings.indents = \
self.builder.env.config.xml_pretty self.builder.env.config.xml_pretty
@ -45,16 +39,13 @@ class PseudoXMLWriter(BaseXMLWriter):
output = None output = None
"""Final translated form of `document`.""" """Final translated form of `document`."""
def __init__(self, builder): def __init__(self, builder: Builder) -> None:
# type: (Builder) -> None
super().__init__() super().__init__()
self.builder = builder self.builder = builder
def translate(self): def translate(self) -> None:
# type: () -> None
self.output = self.document.pformat() self.output = self.document.pformat()
def supports(self, format): def supports(self, format: str) -> bool:
# type: (str) -> bool
"""This writer supports all format-specific elements.""" """This writer supports all format-specific elements."""
return True return True

View File

@ -0,0 +1,20 @@
import os
import sys
sys.path.insert(0, os.path.abspath('.'))
extensions = ['sphinx.ext.autosummary']
autosummary_generate = True
autodoc_default_options = {'members': True}
def skip_member(app, what, name, obj, skip, options):
if name == 'skipmeth':
return True
elif name == '_privatemeth':
return False
def setup(app):
app.connect('autodoc-skip-member', skip_member)

View File

@ -0,0 +1,4 @@
.. autosummary::
:toctree: generate
target.Foo

View File

@ -0,0 +1,14 @@
class Foo:
"""docstring of Foo."""
def meth(self):
"""docstring of meth."""
pass
def skipmeth(self):
"""docstring of skipmeth."""
pass
def _privatemeth(self):
"""docstring of _privatemeth."""
pass

View File

@ -315,7 +315,7 @@ def test_numref_with_prefix2(app, status, warning):
assert ('\\hyperref[\\detokenize{baz:table22}]' assert ('\\hyperref[\\detokenize{baz:table22}]'
'{Table:\\ref{\\detokenize{baz:table22}}}') in result '{Table:\\ref{\\detokenize{baz:table22}}}') in result
assert ('\\hyperref[\\detokenize{index:code-1}]{Code-\\ref{\\detokenize{index:code-1}} ' assert ('\\hyperref[\\detokenize{index:code-1}]{Code-\\ref{\\detokenize{index:code-1}} '
'\\textbar{} }') in result '| }') in result
assert ('\\hyperref[\\detokenize{baz:code22}]' assert ('\\hyperref[\\detokenize{baz:code22}]'
'{Code-\\ref{\\detokenize{baz:code22}}}') in result '{Code-\\ref{\\detokenize{baz:code22}}}') in result
assert ('\\hyperref[\\detokenize{foo:foo}]' assert ('\\hyperref[\\detokenize{foo:foo}]'
@ -1414,6 +1414,7 @@ def test_default_latex_documents():
'project': 'STASI™ Documentation', 'project': 'STASI™ Documentation',
'author': "Wolfgang Schäuble & G'Beckstein."}) 'author': "Wolfgang Schäuble & G'Beckstein."})
config.init_values() config.init_values()
config.add('latex_engine', None, True, None)
expected = [('index', 'stasi.tex', 'STASI™ Documentation', expected = [('index', 'stasi.tex', 'STASI™ Documentation',
r"Wolfgang Schäuble \& G'Beckstein.\@{}", 'manual')] r"Wolfgang Schäuble \& G'Beckstein.\@{}", 'manual')]
assert default_latex_documents(config) == expected assert default_latex_documents(config) == expected

View File

@ -8,6 +8,7 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
from unittest import mock
import pytest import pytest
@ -47,3 +48,25 @@ def test_anchors_ignored(app, status, warning):
# expect all ok when excluding #top # expect all ok when excluding #top
assert not content assert not content
@pytest.mark.sphinx(
'linkcheck', testroot='linkcheck', freshenv=True,
confoverrides={'linkcheck_auth': [
(r'.+google\.com/image.+', 'authinfo1'),
(r'.+google\.com.+', 'authinfo2'),
]
})
def test_auth(app, status, warning):
mock_req = mock.MagicMock()
mock_req.return_value = 'fake-response'
with mock.patch.multiple('requests', get=mock_req, head=mock_req):
app.builder.build_all()
for c_args, c_kwargs in mock_req.call_args_list:
if 'google.com/image' in c_args[0]:
assert c_kwargs['auth'] == 'authinfo1'
elif 'google.com' in c_args[0]:
assert c_kwargs['auth'] == 'authinfo2'
else:
assert not c_kwargs['auth']

View File

@ -37,6 +37,11 @@ default_kw = {
} }
@pytest.fixture(scope='function', autouse=True)
def unload_target_module():
sys.modules.pop('target', None)
def test_mangle_signature(): def test_mangle_signature():
TEST = """ TEST = """
() :: () () :: ()
@ -335,6 +340,15 @@ def test_generate_autosummary_docs_property(app):
".. autoproperty:: Base.prop") ".. autoproperty:: Base.prop")
@pytest.mark.sphinx(testroot='ext-autosummary-skip-member')
def test_autosummary_skip_member(app):
app.build()
content = (app.srcdir / 'generate' / 'target.Foo.rst').text()
assert 'Foo.skipmeth' not in content
assert 'Foo._privatemeth' in content
@pytest.mark.sphinx('dummy', testroot='ext-autosummary', @pytest.mark.sphinx('dummy', testroot='ext-autosummary',
confoverrides={'autosummary_generate': []}) confoverrides={'autosummary_generate': []})
def test_empty_autosummary_generate(app, status, warning): def test_empty_autosummary_generate(app, status, warning):