diff --git a/CHANGES b/CHANGES index ba92a7115..bfa438378 100644 --- a/CHANGES +++ b/CHANGES @@ -50,12 +50,15 @@ Deprecated * ``sphinx.builders.gettext.POHEADER`` * ``sphinx.io.SphinxStandaloneReader.app`` * ``sphinx.io.SphinxStandaloneReader.env`` +* ``sphinx.util.texescape.tex_escape_map`` +* ``sphinx.util.texescape.tex_hl_escape_map_new`` Features added -------------- * #6707: C++, support bit-fields. * #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 * #6780: Add PEP-561 Support * #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` * #6000: LaTeX: have backslash also be an inline literal word wrap break character +* #4186: LaTeX: Support upLaTeX as a new :confval:`latex_engine` (experimental) * #6812: Improve a warning message when extensions are not parallel safe * #6818: Improve Intersphinx performance for multiple remote inventories. * #2546: apidoc: .so file support +* #6798: autosummary: emit ``autodoc-skip-member`` event on generating stub file * #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 ---------- @@ -80,6 +87,7 @@ Bugs fixed supported LaTeX engines: ¶, §, €, ∞, ±, →, ‣, –, superscript and subscript digits go through "as is" (as default OpenType font supports them) * #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 * #6584: i18n: Error when compiling message catalogs on Hindi * #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 bottom of page * #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 -------- diff --git a/doc/extdev/deprecated.rst b/doc/extdev/deprecated.rst index 8c201b525..958f4ee4d 100644 --- a/doc/extdev/deprecated.rst +++ b/doc/extdev/deprecated.rst @@ -41,6 +41,16 @@ The following is a list of deprecated interfaces. - 4.0 - ``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()`` - 2.2 - 4.0 diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst index 0d828634f..4c139695e 100644 --- a/doc/usage/configuration.rst +++ b/doc/usage/configuration.rst @@ -538,7 +538,7 @@ General configuration directory pointed ``REQUESTS_CA_BUNDLE`` environment variable if ``tls_cacerts`` not set. - .. _requests: http://docs.python-requests.org/en/master/ + .. _requests: https://requests.readthedocs.io/en/master/ .. confval:: today today_fmt @@ -662,12 +662,17 @@ documentation on :ref:`intl` for details. Currently supported languages by Sphinx are: + * ``ar`` -- Arabic * ``bn`` -- Bengali * ``ca`` -- Catalan + * ``cak`` -- Kaqchikel * ``cs`` -- Czech + * ``cy`` -- Welsh * ``da`` -- Danish * ``de`` -- German + * ``el`` -- Greek * ``en`` -- English + * ``eo`` -- Esperanto * ``es`` -- Spanish * ``et`` -- Estonian * ``eu`` -- Basque @@ -675,6 +680,7 @@ documentation on :ref:`intl` for details. * ``fi`` -- Finnish * ``fr`` -- French * ``he`` -- Hebrew + * ``hi`` -- Hindi * ``hr`` -- Croatian * ``hu`` -- Hungarian * ``id`` -- Indonesian @@ -688,15 +694,20 @@ documentation on :ref:`intl` for details. * ``ne`` -- Nepali * ``nl`` -- Dutch * ``pl`` -- Polish + * ``pt`` -- Portuguese * ``pt_BR`` -- Brazilian Portuguese * ``pt_PT`` -- European Portuguese + * ``ro`` -- Romanian * ``ru`` -- Russian * ``si`` -- Sinhala * ``sk`` -- Slovak * ``sl`` -- Slovenian + * ``sr`` -- Serbian * ``sv`` -- Swedish + * ``ta`` -- Tamil * ``tr`` -- Turkish * ``uk_UA`` -- Ukrainian + * ``ur`` -- Urdu * ``vi`` -- Vietnamese * ``zh_CN`` -- Simplified Chinese * ``zh_TW`` -- Traditional Chinese @@ -1836,6 +1847,7 @@ These options influence LaTeX output. * ``'xelatex'`` -- XeLaTeX * ``'lualatex'`` -- LuaLaTeX * ``'platex'`` -- pLaTeX (default if :confval:`language` is ``'ja'``) + * ``'uplatex'`` -- upLaTeX (experimental) ``'pdflatex'``\ 's support for Unicode characters is limited. @@ -1861,6 +1873,10 @@ These options influence LaTeX output. Use ``xelatex`` by default for Greek documents. + .. versionchanged:: 2.3 + + Add ``uplatex`` support. + Contrarily to :ref:`MathJaX math rendering in HTML output `, LaTeX requires some extra configuration to support Unicode literals in :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 +.. 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 `_ 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 --------------------------- diff --git a/doc/usage/extensions/autodoc.rst b/doc/usage/extensions/autodoc.rst index 74c0b609d..3bb9630bd 100644 --- a/doc/usage/extensions/autodoc.rst +++ b/doc/usage/extensions/autodoc.rst @@ -439,7 +439,7 @@ There are also config values that you can set: * ``'signature'`` -- Show typehints as its signature (default) * ``'none'`` -- Do not show typehints - .. versionadded: 2.1 + .. versionadded:: 2.1 .. confval:: autodoc_warningiserror diff --git a/doc/usage/extensions/autosummary.rst b/doc/usage/extensions/autosummary.rst index 6d5f33a85..fedc0a3cf 100644 --- a/doc/usage/extensions/autosummary.rst +++ b/doc/usage/extensions/autosummary.rst @@ -143,6 +143,11 @@ also use these config values: The new files will be placed in the directories specified in the ``:toctree:`` options of the directives. + .. versionchanged:: 2.3 + + Emits :event:`autodoc-skip-member` event as :mod:`~sphinx.ext.autodoc` + does. + .. confval:: autosummary_generate_overwrite If true, autosummary already overwrites stub files by generated contents. diff --git a/doc/usage/extensions/napoleon.rst b/doc/usage/extensions/napoleon.rst index 0460f1607..b12dd03ba 100644 --- a/doc/usage/extensions/napoleon.rst +++ b/doc/usage/extensions/napoleon.rst @@ -56,7 +56,7 @@ source code files. .. _Google: https://google.github.io/styleguide/pyguide.html#Comments .. _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: https://github.com/Khan/style-guides/blob/master/style/python.md#docstrings diff --git a/sphinx/builders/latex/__init__.py b/sphinx/builders/latex/__init__.py index 9c34b5568..c24e87a13 100644 --- a/sphinx/builders/latex/__init__.py +++ b/sphinx/builders/latex/__init__.py @@ -427,8 +427,12 @@ def default_latex_engine(config: Config) -> str: def default_latex_docclass(config: Config) -> Dict[str, str]: """ Better default latex_docclass settings for specific languages. """ if config.language == 'ja': - return {'manual': 'jsbook', - 'howto': 'jreport'} + if config.latex_engine == 'uplatex': + return {'manual': 'ujbook', + 'howto': 'ujreport'} + else: + return {'manual': 'jsbook', + 'howto': 'jreport'} else: 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]]: """ 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, make_filename_from_project(config.project) + '.tex', - texescape.escape_abbr(texescape.escape(config.project)), - texescape.escape_abbr(texescape.escape(config.author)), + texescape.escape_abbr(project), + texescape.escape_abbr(author), 'manual')] @@ -454,7 +460,7 @@ def setup(app: Sphinx) -> Dict[str, Any]: app.connect('config-inited', validate_config_values) 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_logo', None, None, [str]) app.add_config_value('latex_appendices', [], None) diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py index 635d9df98..acca8998a 100644 --- a/sphinx/builders/linkcheck.py +++ b/sphinx/builders/linkcheck.py @@ -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 # 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): + if isinstance(chunk, bytes): # requests failed to decode + chunk = chunk.decode() # manually try to decode it + parser.feed(chunk) if parser.found: break @@ -78,6 +81,8 @@ class CheckExternalLinksBuilder(Builder): self.to_ignore = [re.compile(x) for x in self.app.config.linkcheck_ignore] self.anchors_ignore = [re.compile(x) 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.broken = {} # type: Dict[str, str] self.redirected = {} # type: Dict[str, Tuple[str, int]] @@ -124,11 +129,18 @@ class CheckExternalLinksBuilder(Builder): except UnicodeError: 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: if anchor and self.app.config.linkcheck_anchors: # Read the whole document and see if #anchor exists response = requests.get(req_url, stream=True, config=self.app.config, - **kwargs) + auth=auth_info, **kwargs) found = check_anchor(response, unquote(anchor)) if not found: @@ -137,13 +149,14 @@ class CheckExternalLinksBuilder(Builder): try: # try a HEAD request first, which should be easier on # 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() except HTTPError: # retry with GET request if that fails, some servers # don't like HEAD requests. response = requests.get(req_url, stream=True, config=self.app.config, - **kwargs) + auth=auth_info, **kwargs) response.raise_for_status() except HTTPError as err: if err.response.status_code == 401: @@ -302,6 +315,7 @@ def setup(app: Sphinx) -> Dict[str, Any]: app.add_builder(CheckExternalLinksBuilder) 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_timeout', None, None, [int]) app.add_config_value('linkcheck_workers', 5, None) diff --git a/sphinx/config.py b/sphinx/config.py index 2bf2ae730..691cdac11 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -158,16 +158,16 @@ class Config: def __init__(self, config={}, overrides={}): # type: (Dict[str, Any], Dict[str, Any]) -> None - self.overrides = overrides + self.overrides = dict(overrides) self.values = Config.config_values.copy() self._raw_config = config self.setup = config.get('setup', None) # type: Callable - if 'extensions' in overrides: - if isinstance(overrides['extensions'], str): - config['extensions'] = overrides.pop('extensions').split(',') + if 'extensions' in self.overrides: + if isinstance(self.overrides['extensions'], str): + config['extensions'] = self.overrides.pop('extensions').split(',') else: - config['extensions'] = overrides.pop('extensions') + config['extensions'] = self.overrides.pop('extensions') self.extensions = config.get('extensions', []) # type: List[str] @classmethod diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index c21f7585d..28a5f1fbf 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -138,9 +138,20 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any, if not template.exists(template_name): 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] = [], imported: bool = True) -> Tuple[List[str], List[str]]: items = [] # type: List[str] + public = [] # type: List[str] for name in dir(obj): try: value = safe_getattr(obj, name) @@ -148,11 +159,20 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any, continue documenter = get_documenter(app, value, obj) if documenter.objtype in types: + # skip imported members if expected if imported or getattr(value, '__module__', None) == obj.__name__: - # skip imported members if expected - items.append(name) - public = [x for x in items - if x in include_public or not x.startswith('_')] + skipped = skip_member(value, name, documenter.objtype) + if skipped is True: + pass + 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 ns = {} # type: Dict[str, Any] diff --git a/sphinx/ext/todo.py b/sphinx/ext/todo.py index d2a8a666d..99dc02d3d 100644 --- a/sphinx/ext/todo.py +++ b/sphinx/ext/todo.py @@ -27,10 +27,9 @@ from sphinx.domains import Domain from sphinx.environment import BuildEnvironment from sphinx.errors import NoUri from sphinx.locale import _, __ -from sphinx.util import logging +from sphinx.util import logging, texescape from sphinx.util.docutils import SphinxDirective from sphinx.util.nodes import make_refnode -from sphinx.util.texescape import get_escape_func from sphinx.writers.html import HTMLTranslator 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: if self.config.todo_include_todos: - escape = get_escape_func(self.config.latex_engine) self.body.append('\n\\begin{sphinxadmonition}{note}{') self.body.append(self.hypertarget_to(node)) + 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) else: raise nodes.SkipNode diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py index b2ff39a06..ca2bdc48f 100644 --- a/sphinx/highlighting.py +++ b/sphinx/highlighting.py @@ -23,8 +23,7 @@ from pygments.util import ClassNotFound from sphinx.locale import __ from sphinx.pygments_styles import SphinxStyle, NoneStyle -from sphinx.util import logging -from sphinx.util.texescape import get_hlescape_func +from sphinx.util import logging, texescape if False: # For type annotation @@ -165,8 +164,7 @@ class PygmentsBridge: if self.dest == 'html': return hlsource else: - escape = get_hlescape_func(self.latex_engine) - return escape(hlsource) + return texescape.hlescape(hlsource, self.latex_engine) def get_stylesheet(self): # type: () -> str diff --git a/sphinx/project.py b/sphinx/project.py index 81d2b43e0..13ac6d9fd 100644 --- a/sphinx/project.py +++ b/sphinx/project.py @@ -13,6 +13,7 @@ import os from sphinx.locale import __ from sphinx.util import get_matching_files from sphinx.util import logging +from sphinx.util import path_stabilize from sphinx.util.matching import compile_matchers from sphinx.util.osutil import SEP, relpath @@ -71,6 +72,7 @@ class Project: filename = relpath(filename, self.srcdir) for suffix in self.source_suffix: if filename.endswith(suffix): + filename = path_stabilize(filename) return filename[:-len(suffix)] # the file does not have docname diff --git a/sphinx/texinputs/Makefile_t b/sphinx/texinputs/Makefile_t index c92465124..90cee829b 100644 --- a/sphinx/texinputs/Makefile_t +++ b/sphinx/texinputs/Makefile_t @@ -36,7 +36,7 @@ XINDYOPTS += -M LatinRules.xdy # format: pdf or dvi (used only by archive targets) FMT = pdf -{% if latex_engine == 'platex' -%} +{% if latex_engine in ('platex', 'uplatex') -%} # latexmkrc is read then overridden by latexmkjarc LATEX = latexmk -r latexmkjarc -dvi PDFLATEX = latexmk -r latexmkjarc -pdfdvi -dvi- -ps- diff --git a/sphinx/texinputs/latexmkjarc b/sphinx/texinputs/latexmkjarc_t similarity index 89% rename from sphinx/texinputs/latexmkjarc rename to sphinx/texinputs/latexmkjarc_t index 5b315d6c9..c7403a90b 100644 --- a/sphinx/texinputs/latexmkjarc +++ b/sphinx/texinputs/latexmkjarc_t @@ -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'; $makeindex = 'internal mendex %S %B %D'; sub mendex { diff --git a/sphinx/themes/basic/opensearch.xml b/sphinx/themes/basic/opensearch.xml index a750b3041..bac08b4a2 100644 --- a/sphinx/themes/basic/opensearch.xml +++ b/sphinx/themes/basic/opensearch.xml @@ -6,5 +6,8 @@ {{ docstitle|e }} +{%- if favicon %} + {{ use_opensearch }}/{{ pathto('_static/' + favicon, 1) }} +{%- endif %} {% block extra %} {# Put e.g. an element here. #} {% endblock %} diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index 7e0174e1c..ad845872e 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -424,7 +424,7 @@ var Search = { for (j = 0; j < _files.length; j++) { file = _files[j]; if (!(file in scoreMap)) - scoreMap[file] = {} + scoreMap[file] = {}; scoreMap[file][word] = o.score; } }); @@ -432,7 +432,7 @@ var Search = { // create the mapping for (j = 0; j < files.length; j++) { file = files[j]; - if (file in fileMap) + if (file in fileMap && fileMap[file].indexOf(word) === -1) fileMap[file].push(word); else fileMap[file] = [word]; diff --git a/sphinx/util/smartypants.py b/sphinx/util/smartypants.py index 47f8b59b2..43f8bc724 100644 --- a/sphinx/util/smartypants.py +++ b/sphinx/util/smartypants.py @@ -155,7 +155,7 @@ def educateQuotes(text: str, language: str = 'en') -> str: # Special case for decade abbreviations (the '80s): 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\[\{\(\-]""" dec_dashes = r"""–|—""" diff --git a/sphinx/util/template.py b/sphinx/util/template.py index 3a43db9a5..e6e72d079 100644 --- a/sphinx/util/template.py +++ b/sphinx/util/template.py @@ -9,6 +9,7 @@ """ import os +from functools import partial from typing import Dict, List, Union from jinja2.loaders import BaseLoader @@ -69,8 +70,9 @@ class LaTeXRenderer(SphinxRenderer): super().__init__(template_path) # use texescape as escape filter - self.env.filters['e'] = texescape.get_escape_func(latex_engine) - self.env.filters['escape'] = texescape.get_escape_func(latex_engine) + escape = partial(texescape.escape, latex_engine=latex_engine) + self.env.filters['e'] = escape + self.env.filters['escape'] = escape self.env.filters['eabbr'] = texescape.escape_abbr # use JSP/eRuby like tagging instead because curly bracket; the default diff --git a/sphinx/util/texescape.py b/sphinx/util/texescape.py index e3c45adb4..b7928ce37 100644 --- a/sphinx/util/texescape.py +++ b/sphinx/util/texescape.py @@ -9,7 +9,10 @@ """ import re -from typing import Callable, Dict +from typing import Dict + +from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias + tex_replacements = [ # map TeX special chars @@ -32,8 +35,6 @@ tex_replacements = [ ('`', r'{}`'), ('<', r'\textless{}'), ('>', r'\textgreater{}'), - # map char for some unknown reason. TODO: remove this? - ('|', r'\textbar{}'), # map special Unicode characters to TeX commands ('✓', r'\(\checkmark\)'), ('✔', r'\(\pmb{\checkmark}\)'), @@ -84,47 +85,38 @@ unicode_tex_replacements = [ ('₉', r'\(\sb{\text{9}}\)'), ] -tex_escape_map = {} # type: Dict[int, str] -tex_escape_map_without_unicode = {} # type: Dict[int, str] -tex_replace_map = {} -tex_hl_escape_map_new = {} # type: Dict[int, str] -tex_hl_escape_map_new_without_unicode = {} # type: Dict[int, str] +tex_replace_map = {} # type: Dict[int, str] + +_tex_escape_map = {} # type: Dict[int, str] +_tex_escape_map_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]: - """Get escape() function for given latex_engine.""" - if latex_engine in ('lualatex', 'xelatex'): - return escape_for_unicode_latex_engine - else: - return escape +deprecated_alias('sphinx.util.texescape', + { + 'tex_escape_map': _tex_escape_map, + 'tex_hl_escape_map_new': _tex_hlescape_map, + }, + RemovedInSphinx40Warning) -def escape(s: str) -> str: +def escape(s: str, latex_engine: str = None) -> str: """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'): - return hlescape_for_unicode_latex_engine + # unicode based LaTeX engine + return s.translate(_tex_escape_map_without_unicode) 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.""" - return s.translate(tex_hl_escape_map_new) - - -def hlescape_for_unicode_latex_engine(s: str) -> str: - """Escape text for unicode supporting LaTeX engine.""" - return s.translate(tex_hl_escape_map_new_without_unicode) + if latex_engine in ('lualatex', 'xelatex'): + # unicode based LaTeX engine + return s.translate(_tex_hlescape_map_without_unicode) + else: + return s.translate(_tex_hlescape_map) def escape_abbr(text: str) -> str: @@ -134,19 +126,19 @@ def escape_abbr(text: str) -> str: def init() -> None: for a, b in tex_replacements: - tex_escape_map[ord(a)] = b - tex_escape_map_without_unicode[ord(a)] = b + _tex_escape_map[ord(a)] = b + _tex_escape_map_without_unicode[ord(a)] = b tex_replace_map[ord(a)] = '_' for a, b in unicode_tex_replacements: - tex_escape_map[ord(a)] = b + _tex_escape_map[ord(a)] = b tex_replace_map[ord(a)] = '_' for a, b in tex_replacements: if a in '[]{}\\': continue - tex_hl_escape_map_new[ord(a)] = b - tex_hl_escape_map_new_without_unicode[ord(a)] = b + _tex_hlescape_map[ord(a)] = b + _tex_hlescape_map_without_unicode[ord(a)] = b for a, b in unicode_tex_replacements: - tex_hl_escape_map_new[ord(a)] = b + _tex_hlescape_map[ord(a)] = b diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index 9ac76901b..0cbaef527 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -12,9 +12,11 @@ import copy import os import posixpath import warnings -from typing import Iterable, cast +from typing import cast +from typing import Iterable from docutils import nodes +from docutils.nodes import Element, Node, Text from docutils.writers.html4css1 import Writer, HTMLTranslator as BaseTranslator from sphinx import addnodes @@ -27,8 +29,7 @@ from sphinx.util.images import get_image_size if False: # For type annotation - from typing import Any # NOQA - from sphinx.builders.html import StandaloneHTMLBuilder # NOQA + from sphinx.builders.html import StandaloneHTMLBuilder logger = logging.getLogger(__name__) @@ -45,13 +46,11 @@ class HTMLWriter(Writer): if '--embed-stylesheet' in _setting[1]: _setting[2]['default'] = 0 - def __init__(self, builder): - # type: (StandaloneHTMLBuilder) -> None + def __init__(self, builder: "StandaloneHTMLBuilder") -> None: super().__init__() self.builder = builder - def translate(self): - # type: () -> None + def translate(self) -> None: # sadly, this is mostly copied from parent class visitor = self.builder.create_translator(self.document, self.builder) self.visitor = cast(HTMLTranslator, visitor) @@ -73,8 +72,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): builder = None # type: StandaloneHTMLBuilder - def __init__(self, *args): - # type: (Any) -> None + def __init__(self, *args) -> None: if isinstance(args[0], nodes.document) and isinstance(args[1], Builder): document, builder = args else: @@ -100,26 +98,21 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): self._fieldlist_row_index = 0 self.required_params_left = 0 - def visit_start_of_file(self, node): - # type: (nodes.Element) -> None + def visit_start_of_file(self, node: Element) -> None: # only occurs in the single-file builder self.docnames.append(node['docname']) self.body.append('' % node['docname']) - def depart_start_of_file(self, node): - # type: (nodes.Element) -> None + def depart_start_of_file(self, node: Element) -> None: self.docnames.pop() - def visit_desc(self, node): - # type: (nodes.Element) -> None + def visit_desc(self, node: Element) -> None: self.body.append(self.starttag(node, 'dl', CLASS=node['objtype'])) - def depart_desc(self, node): - # type: (nodes.Element) -> None + def depart_desc(self, node: Element) -> None: self.body.append('\n\n') - def visit_desc_signature(self, node): - # type: (nodes.Element) -> None + def visit_desc_signature(self, node: Element) -> None: # the id is set automatically self.body.append(self.starttag(node, 'dt')) # anchor for per-desc interactive data @@ -127,57 +120,45 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): and node['ids'] and node['first']: self.body.append('' % node['ids'][0]) - def depart_desc_signature(self, node): - # type: (nodes.Element) -> None + def depart_desc_signature(self, node: Element) -> None: if not node.get('is_multiline'): self.add_permalink_ref(node, _('Permalink to this definition')) self.body.append('\n') - def visit_desc_signature_line(self, node): - # type: (nodes.Element) -> None + def visit_desc_signature_line(self, node: Element) -> None: pass - def depart_desc_signature_line(self, node): - # type: (nodes.Element) -> None + def depart_desc_signature_line(self, node: Element) -> None: if node.get('add_permalink'): # the permalink info is on the parent desc_signature node self.add_permalink_ref(node.parent, _('Permalink to this definition')) self.body.append('
') - def visit_desc_addname(self, node): - # type: (nodes.Element) -> None + def visit_desc_addname(self, node: Element) -> None: self.body.append(self.starttag(node, 'code', '', CLASS='descclassname')) - def depart_desc_addname(self, node): - # type: (nodes.Element) -> None + def depart_desc_addname(self, node: Element) -> None: self.body.append('') - def visit_desc_type(self, node): - # type: (nodes.Element) -> None + def visit_desc_type(self, node: Element) -> None: pass - def depart_desc_type(self, node): - # type: (nodes.Element) -> None + def depart_desc_type(self, node: Element) -> None: pass - def visit_desc_returns(self, node): - # type: (nodes.Element) -> None + def visit_desc_returns(self, node: Element) -> None: self.body.append(' → ') - def depart_desc_returns(self, node): - # type: (nodes.Element) -> None + def depart_desc_returns(self, node: Element) -> None: pass - def visit_desc_name(self, node): - # type: (nodes.Element) -> None + def visit_desc_name(self, node: Element) -> None: self.body.append(self.starttag(node, 'code', '', CLASS='descname')) - def depart_desc_name(self, node): - # type: (nodes.Element) -> None + def depart_desc_name(self, node: Element) -> None: self.body.append('') - def visit_desc_parameterlist(self, node): - # type: (nodes.Element) -> None + def visit_desc_parameterlist(self, node: Element) -> None: self.body.append('(') self.first_param = 1 self.optional_param_level = 0 @@ -186,8 +167,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): for c in node.children]) self.param_separator = node.child_text_separator - def depart_desc_parameterlist(self, node): - # type: (nodes.Element) -> None + def depart_desc_parameterlist(self, node: Element) -> None: self.body.append(')') # 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]) # - def visit_desc_parameter(self, node): - # type: (nodes.Element) -> None + def visit_desc_parameter(self, node: Element) -> None: if self.first_param: self.first_param = 0 elif not self.required_params_left: @@ -207,50 +186,40 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): if not node.hasattr('noemph'): self.body.append('') - def depart_desc_parameter(self, node): - # type: (nodes.Element) -> None + def depart_desc_parameter(self, node: Element) -> None: if not node.hasattr('noemph'): self.body.append('') if self.required_params_left: self.body.append(self.param_separator) - def visit_desc_optional(self, node): - # type: (nodes.Element) -> None + def visit_desc_optional(self, node: Element) -> None: self.optional_param_level += 1 self.body.append('[') - def depart_desc_optional(self, node): - # type: (nodes.Element) -> None + def depart_desc_optional(self, node: Element) -> None: self.optional_param_level -= 1 self.body.append(']') - def visit_desc_annotation(self, node): - # type: (nodes.Element) -> None + def visit_desc_annotation(self, node: Element) -> None: self.body.append(self.starttag(node, 'em', '', CLASS='property')) - def depart_desc_annotation(self, node): - # type: (nodes.Element) -> None + def depart_desc_annotation(self, node: Element) -> None: self.body.append('') - def visit_desc_content(self, node): - # type: (nodes.Element) -> None + def visit_desc_content(self, node: Element) -> None: self.body.append(self.starttag(node, 'dd', '')) - def depart_desc_content(self, node): - # type: (nodes.Element) -> None + def depart_desc_content(self, node: Element) -> None: self.body.append('') - def visit_versionmodified(self, node): - # type: (nodes.Element) -> None + def visit_versionmodified(self, node: Element) -> None: self.body.append(self.starttag(node, 'div', CLASS=node['type'])) - def depart_versionmodified(self, node): - # type: (nodes.Element) -> None + def depart_versionmodified(self, node: Element) -> None: self.body.append('\n') # overwritten - def visit_reference(self, node): - # type: (nodes.Element) -> None + def visit_reference(self, node: Element) -> None: atts = {'class': 'reference'} if node.get('internal') or 'refuri' not in node: atts['class'] += ' internal' @@ -278,38 +247,31 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): self.body.append(('%s' + self.secnumber_suffix) % '.'.join(map(str, node['secnumber']))) - def visit_number_reference(self, node): - # type: (nodes.Element) -> None + def visit_number_reference(self, node: Element) -> None: self.visit_reference(node) - def depart_number_reference(self, node): - # type: (nodes.Element) -> None + def depart_number_reference(self, node: Element) -> None: self.depart_reference(node) # overwritten -- we don't want source comments to show up in the HTML - def visit_comment(self, node): # type: ignore - # type: (nodes.Element) -> None + def visit_comment(self, node: Element) -> None: # type: ignore raise nodes.SkipNode # overwritten - def visit_admonition(self, node, name=''): - # type: (nodes.Element, str) -> None + def visit_admonition(self, node: Element, name: str = '') -> None: self.body.append(self.starttag( node, 'div', CLASS=('admonition ' + name))) if name: node.insert(0, nodes.title(name, admonitionlabels[name])) self.set_first_last(node) - def visit_seealso(self, node): - # type: (nodes.Element) -> None + def visit_seealso(self, node: Element) -> None: self.visit_admonition(node, 'seealso') - def depart_seealso(self, node): - # type: (nodes.Element) -> None + def depart_seealso(self, node: Element) -> None: self.depart_admonition(node) - def add_secnumber(self, node): - # type: (nodes.Element) -> None + def add_secnumber(self, node: Element) -> None: if node.get('secnumber'): self.body.append('.'.join(map(str, node['secnumber'])) + self.secnumber_suffix) @@ -328,10 +290,8 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): self.body.append('.'.join(map(str, numbers)) + self.secnumber_suffix) - def add_fignumber(self, node): - # type: (nodes.Element) -> None - def append_fignumber(figtype, figure_id): - # type: (str, str) -> None + def add_fignumber(self, node: Element) -> None: + def append_fignumber(figtype: str, figure_id: str) -> None: if self.builder.name == 'singlehtml': key = "%s/%s" % (self.docnames[-1], figtype) else: @@ -356,14 +316,12 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): else: append_fignumber(figtype, node['ids'][0]) - def add_permalink_ref(self, node, title): - # type: (nodes.Element, str) -> None + def add_permalink_ref(self, node: Element, title: str) -> None: if node['ids'] and self.permalink_text and self.builder.add_permalinks: format = '%s' self.body.append(format % (node['ids'][0], title, self.permalink_text)) - def generate_targets_for_listing(self, node): - # type: (nodes.Element) -> None + def generate_targets_for_listing(self, node: Element) -> None: """Generate hyperlink targets for listings. Original visit_bullet_list(), visit_definition_list() and visit_enumerated_list() @@ -378,8 +336,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): node['ids'].remove(id) # overwritten - def visit_bullet_list(self, node): - # type: (nodes.Element) -> None + def visit_bullet_list(self, node: Element) -> None: if len(node) == 1 and isinstance(node[0], addnodes.toctree): # avoid emitting empty
    raise nodes.SkipNode @@ -387,46 +344,39 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): super().visit_bullet_list(node) # overwritten - def visit_enumerated_list(self, node): - # type: (nodes.Element) -> None + def visit_enumerated_list(self, node: Element) -> None: self.generate_targets_for_listing(node) super().visit_enumerated_list(node) # overwritten - def visit_definition(self, node): - # type: (nodes.Element) -> None + def visit_definition(self, node: Element) -> None: # don't insert here. self.body.append(self.starttag(node, 'dd', '')) # overwritten - def depart_definition(self, node): - # type: (nodes.Element) -> None + def depart_definition(self, node: Element) -> None: self.body.append('\n') # overwritten - def visit_classifier(self, node): - # type: (nodes.Element) -> None + def visit_classifier(self, node: Element) -> None: self.body.append(self.starttag(node, 'span', '', CLASS='classifier')) # overwritten - def depart_classifier(self, node): - # type: (nodes.Element) -> None + def depart_classifier(self, node: Element) -> None: self.body.append('') - 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): # close `
    ` tag at the tail of classifiers self.body.append('
    ') # overwritten - def visit_term(self, node): - # type: (nodes.Element) -> None + def visit_term(self, node: Element) -> None: self.body.append(self.starttag(node, 'dt', '')) # overwritten - def depart_term(self, node): - # type: (nodes.Element) -> None - next_node = node.next_node(descend=False, siblings=True) # type: nodes.Node + def depart_term(self, node: Element) -> None: + next_node = node.next_node(descend=False, siblings=True) # type: Node if isinstance(next_node, nodes.classifier): # Leave the end tag to `self.depart_classifier()`, in case # there's a classifier. @@ -435,16 +385,14 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): self.body.append('') # overwritten - def visit_title(self, node): - # type: (nodes.Element) -> None + def visit_title(self, node: Element) -> None: super().visit_title(node) self.add_secnumber(node) self.add_fignumber(node.parent) if isinstance(node.parent, nodes.table): self.body.append('') - def depart_title(self, node): - # type: (nodes.Element) -> None + def depart_title(self, node: Element) -> None: close_tag = self.context[-1] if (self.permalink_text and self.builder.add_permalinks and node.parent.hasattr('ids') and node.parent['ids']): @@ -466,8 +414,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): super().depart_title(node) # overwritten - def visit_literal_block(self, node): - # type: (nodes.Element) -> None + def visit_literal_block(self, node: Element) -> None: if node.rawsource != node.astext(): # most probably a parsed-literal block -- don't highlight return super().visit_literal_block(node) @@ -491,8 +438,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): self.body.append(starttag + highlighted + '\n') raise nodes.SkipNode - def visit_caption(self, node): - # type: (nodes.Element) -> None + def visit_caption(self, node: Element) -> None: if isinstance(node.parent, nodes.container) and node.parent.get('literal_block'): self.body.append('
    ') else: @@ -500,8 +446,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): self.add_fignumber(node.parent) self.body.append(self.starttag(node, 'span', '', CLASS='caption-text')) - def depart_caption(self, node): - # type: (nodes.Element) -> None + def depart_caption(self, node: Element) -> None: self.body.append('') # append permalink if available @@ -517,22 +462,18 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): else: super().depart_caption(node) - def visit_doctest_block(self, node): - # type: (nodes.Element) -> None + def visit_doctest_block(self, node: Element) -> None: self.visit_literal_block(node) # overwritten to add the
    (for XHTML compliance) - def visit_block_quote(self, node): - # type: (nodes.Element) -> None + def visit_block_quote(self, node: Element) -> None: self.body.append(self.starttag(node, 'blockquote') + '
    ') - def depart_block_quote(self, node): - # type: (nodes.Element) -> None + def depart_block_quote(self, node: Element) -> None: self.body.append('
    \n') # overwritten - def visit_literal(self, node): - # type: (nodes.Element) -> None + def visit_literal(self, node: Element) -> None: if 'kbd' in node['classes']: self.body.append(self.starttag(node, 'kbd', '', CLASS='docutils literal notranslate')) @@ -541,16 +482,14 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): CLASS='docutils literal notranslate')) self.protect_literal_text += 1 - def depart_literal(self, node): - # type: (nodes.Element) -> None + def depart_literal(self, node: Element) -> None: if 'kbd' in node['classes']: self.body.append('') else: self.protect_literal_text -= 1 self.body.append('') - def visit_productionlist(self, node): - # type: (nodes.Element) -> None + def visit_productionlist(self, node: Element) -> None: self.body.append(self.starttag(node, 'pre')) names = [] productionlist = cast(Iterable[addnodes.production], node) @@ -570,30 +509,24 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): self.body.append('\n') raise nodes.SkipNode - def depart_productionlist(self, node): - # type: (nodes.Element) -> None + def depart_productionlist(self, node: Element) -> None: pass - def visit_production(self, node): - # type: (nodes.Element) -> None + def visit_production(self, node: Element) -> None: pass - def depart_production(self, node): - # type: (nodes.Element) -> None + def depart_production(self, node: Element) -> None: pass - def visit_centered(self, node): - # type: (nodes.Element) -> None + def visit_centered(self, node: Element) -> None: self.body.append(self.starttag(node, 'p', CLASS="centered") + '') - def depart_centered(self, node): - # type: (nodes.Element) -> None + def depart_centered(self, node: Element) -> None: self.body.append('

    ') # overwritten - def should_be_compact_paragraph(self, node): - # type: (nodes.Node) -> bool + def should_be_compact_paragraph(self, node: Node) -> bool: """Determine if the

    tags around paragraph can be omitted.""" if isinstance(node.parent, addnodes.desc_content): # Never compact desc_content items. @@ -603,16 +536,13 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): return False return super().should_be_compact_paragraph(node) - def visit_compact_paragraph(self, node): - # type: (nodes.Element) -> None + def visit_compact_paragraph(self, node: Element) -> None: pass - def depart_compact_paragraph(self, node): - # type: (nodes.Element) -> None + def depart_compact_paragraph(self, node: Element) -> None: pass - def visit_download_reference(self, node): - # type: (nodes.Element) -> None + def visit_download_reference(self, node: Element) -> None: atts = {'class': 'reference download', 'download': ''} @@ -631,13 +561,11 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): else: self.context.append('') - def depart_download_reference(self, node): - # type: (nodes.Element) -> None + def depart_download_reference(self, node: Element) -> None: self.body.append(self.context.pop()) # overwritten - def visit_image(self, node): - # type: (nodes.Element) -> None + def visit_image(self, node: Element) -> None: olduri = node['uri'] # rewrite the URI if the environment knows about it if olduri in self.builder.images: @@ -678,67 +606,53 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): super().visit_image(node) # overwritten - def depart_image(self, node): - # type: (nodes.Element) -> None + def depart_image(self, node: Element) -> None: if node['uri'].lower().endswith(('svg', 'svgz')): self.body.append(self.context.pop()) else: super().depart_image(node) - def visit_toctree(self, node): - # type: (nodes.Element) -> None + def visit_toctree(self, node: Element) -> None: # this only happens when formatting a toc from env.tocs -- in this # case we don't want to include the subtree raise nodes.SkipNode - def visit_index(self, node): - # type: (nodes.Element) -> None + def visit_index(self, node: Element) -> None: raise nodes.SkipNode - def visit_tabular_col_spec(self, node): - # type: (nodes.Element) -> None + def visit_tabular_col_spec(self, node: Element) -> None: raise nodes.SkipNode - def visit_glossary(self, node): - # type: (nodes.Element) -> None + def visit_glossary(self, node: Element) -> None: pass - def depart_glossary(self, node): - # type: (nodes.Element) -> None + def depart_glossary(self, node: Element) -> None: pass - def visit_acks(self, node): - # type: (nodes.Element) -> None + def visit_acks(self, node: Element) -> None: pass - def depart_acks(self, node): - # type: (nodes.Element) -> None + def depart_acks(self, node: Element) -> None: pass - def visit_hlist(self, node): - # type: (nodes.Element) -> None + def visit_hlist(self, node: Element) -> None: self.body.append('') - def depart_hlist(self, node): - # type: (nodes.Element) -> None + def depart_hlist(self, node: Element) -> None: self.body.append('
    \n') - def visit_hlistcol(self, node): - # type: (nodes.Element) -> None + def visit_hlistcol(self, node: Element) -> None: self.body.append('') - def depart_hlistcol(self, node): - # type: (nodes.Element) -> None + def depart_hlistcol(self, node: Element) -> None: self.body.append('') - def visit_option_group(self, node): - # type: (nodes.Element) -> None + def visit_option_group(self, node: Element) -> None: super().visit_option_group(node) self.context[-2] = self.context[-2].replace(' ', ' ') # overwritten - def visit_Text(self, node): - # type: (nodes.Text) -> None + def visit_Text(self, node: Text) -> None: text = node.astext() encoded = self.encode(text) if self.protect_literal_text: @@ -759,127 +673,99 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): encoded = self.cloak_email(encoded) self.body.append(encoded) - def visit_note(self, node): - # type: (nodes.Element) -> None + def visit_note(self, node: Element) -> None: self.visit_admonition(node, 'note') - def depart_note(self, node): - # type: (nodes.Element) -> None + def depart_note(self, node: Element) -> None: self.depart_admonition(node) - def visit_warning(self, node): - # type: (nodes.Element) -> None + def visit_warning(self, node: Element) -> None: self.visit_admonition(node, 'warning') - def depart_warning(self, node): - # type: (nodes.Element) -> None + def depart_warning(self, node: Element) -> None: self.depart_admonition(node) - def visit_attention(self, node): - # type: (nodes.Element) -> None + def visit_attention(self, node: Element) -> None: self.visit_admonition(node, 'attention') - def depart_attention(self, node): - # type: (nodes.Element) -> None + def depart_attention(self, node: Element) -> None: self.depart_admonition(node) - def visit_caution(self, node): - # type: (nodes.Element) -> None + def visit_caution(self, node: Element) -> None: self.visit_admonition(node, 'caution') - def depart_caution(self, node): - # type: (nodes.Element) -> None + def depart_caution(self, node: Element) -> None: self.depart_admonition(node) - def visit_danger(self, node): - # type: (nodes.Element) -> None + def visit_danger(self, node: Element) -> None: self.visit_admonition(node, 'danger') - def depart_danger(self, node): - # type: (nodes.Element) -> None + def depart_danger(self, node: Element) -> None: self.depart_admonition(node) - def visit_error(self, node): - # type: (nodes.Element) -> None + def visit_error(self, node: Element) -> None: self.visit_admonition(node, 'error') - def depart_error(self, node): - # type: (nodes.Element) -> None + def depart_error(self, node: Element) -> None: self.depart_admonition(node) - def visit_hint(self, node): - # type: (nodes.Element) -> None + def visit_hint(self, node: Element) -> None: self.visit_admonition(node, 'hint') - def depart_hint(self, node): - # type: (nodes.Element) -> None + def depart_hint(self, node: Element) -> None: self.depart_admonition(node) - def visit_important(self, node): - # type: (nodes.Element) -> None + def visit_important(self, node: Element) -> None: self.visit_admonition(node, 'important') - def depart_important(self, node): - # type: (nodes.Element) -> None + def depart_important(self, node: Element) -> None: self.depart_admonition(node) - def visit_tip(self, node): - # type: (nodes.Element) -> None + def visit_tip(self, node: Element) -> None: self.visit_admonition(node, 'tip') - def depart_tip(self, node): - # type: (nodes.Element) -> None + def depart_tip(self, node: Element) -> None: self.depart_admonition(node) - def visit_literal_emphasis(self, node): - # type: (nodes.Element) -> None + def visit_literal_emphasis(self, node: Element) -> None: return self.visit_emphasis(node) - def depart_literal_emphasis(self, node): - # type: (nodes.Element) -> None + def depart_literal_emphasis(self, node: Element) -> None: return self.depart_emphasis(node) - def visit_literal_strong(self, node): - # type: (nodes.Element) -> None + def visit_literal_strong(self, node: Element) -> None: return self.visit_strong(node) - def depart_literal_strong(self, node): - # type: (nodes.Element) -> None + def depart_literal_strong(self, node: Element) -> None: return self.depart_strong(node) - def visit_abbreviation(self, node): - # type: (nodes.Element) -> None + def visit_abbreviation(self, node: Element) -> None: attrs = {} if node.hasattr('explanation'): attrs['title'] = node['explanation'] self.body.append(self.starttag(node, 'abbr', '', **attrs)) - def depart_abbreviation(self, node): - # type: (nodes.Element) -> None + def depart_abbreviation(self, node: Element) -> None: self.body.append('') - def visit_manpage(self, node): - # type: (nodes.Element) -> None + def visit_manpage(self, node: Element) -> None: self.visit_literal_emphasis(node) if self.manpages_url: node['refuri'] = self.manpages_url.format(**node.attributes) self.visit_reference(node) - def depart_manpage(self, node): - # type: (nodes.Element) -> None + def depart_manpage(self, node: Element) -> None: if self.manpages_url: self.depart_reference(node) self.depart_literal_emphasis(node) # overwritten to add even/odd classes - def visit_table(self, node): - # type: (nodes.Element) -> None + def visit_table(self, node: Element) -> None: self._table_row_index = 0 return super().visit_table(node) - def visit_row(self, node): - # type: (nodes.Element) -> None + def visit_row(self, node: Element) -> None: self._table_row_index += 1 if self._table_row_index % 2 == 0: node['classes'].append('row-even') @@ -888,19 +774,16 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): self.body.append(self.starttag(node, 'tr', '')) node.column = 0 # type: ignore - def visit_entry(self, node): - # type: (nodes.Element) -> None + def visit_entry(self, node: Element) -> None: super().visit_entry(node) if self.body[-1] == ' ': self.body[-1] = ' ' - def visit_field_list(self, node): - # type: (nodes.Element) -> None + def visit_field_list(self, node: Element) -> None: self._fieldlist_row_index = 0 return super().visit_field_list(node) - def visit_field(self, node): - # type: (nodes.Element) -> None + def visit_field(self, node: Element) -> None: self._fieldlist_row_index += 1 if self._fieldlist_row_index % 2 == 0: node['classes'].append('field-even') @@ -908,39 +791,33 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): node['classes'].append('field-odd') self.body.append(self.starttag(node, 'tr', '', CLASS='field')) - def visit_field_name(self, node): - # type: (nodes.Element) -> None + def visit_field_name(self, node: Element) -> None: context_count = len(self.context) super().visit_field_name(node) if context_count != len(self.context): self.context[-1] = self.context[-1].replace(' ', ' ') - def visit_math(self, node, math_env=''): - # type: (nodes.Element, str) -> None + def visit_math(self, node: Element, math_env: str = '') -> None: name = self.builder.math_renderer_name visit, _ = self.builder.app.registry.html_inline_math_renderers[name] visit(self, node) - def depart_math(self, node, math_env=''): - # type: (nodes.Element, str) -> None + def depart_math(self, node: Element, math_env: str = '') -> None: name = self.builder.math_renderer_name _, depart = self.builder.app.registry.html_inline_math_renderers[name] if depart: depart(self, node) - def visit_math_block(self, node, math_env=''): - # type: (nodes.Element, str) -> None + def visit_math_block(self, node: Element, math_env: str = '') -> None: name = self.builder.math_renderer_name visit, _ = self.builder.app.registry.html_block_math_renderers[name] visit(self, node) - def depart_math_block(self, node, math_env=''): - # type: (nodes.Element, str) -> None + def depart_math_block(self, node: Element, math_env: str = '') -> None: name = self.builder.math_renderer_name _, depart = self.builder.app.registry.html_block_math_renderers[name] if depart: depart(self, node) - def unknown_visit(self, node): - # type: (nodes.Node) -> None + def unknown_visit(self, node: Node) -> None: raise NotImplementedError('Unknown node: ' + node.__class__.__name__) diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index fe57f42bf..3e67e4d1c 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -11,9 +11,11 @@ import os import posixpath import warnings -from typing import Iterable, cast +from typing import cast +from typing import Iterable from docutils import nodes +from docutils.nodes import Element, Node, Text from docutils.writers.html5_polyglot import HTMLTranslator as BaseTranslator from sphinx import addnodes @@ -26,8 +28,7 @@ from sphinx.util.images import get_image_size if False: # For type annotation - from typing import Any # NOQA - from sphinx.builders.html import StandaloneHTMLBuilder # NOQA + from sphinx.builders.html import StandaloneHTMLBuilder logger = logging.getLogger(__name__) @@ -43,8 +44,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): builder = None # type: StandaloneHTMLBuilder - def __init__(self, *args): - # type: (Any) -> None + def __init__(self, *args) -> None: if isinstance(args[0], nodes.document) and isinstance(args[1], Builder): document, builder = args else: @@ -70,26 +70,21 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): self._fieldlist_row_index = 0 self.required_params_left = 0 - def visit_start_of_file(self, node): - # type: (nodes.Element) -> None + def visit_start_of_file(self, node: Element) -> None: # only occurs in the single-file builder self.docnames.append(node['docname']) self.body.append('' % node['docname']) - def depart_start_of_file(self, node): - # type: (nodes.Element) -> None + def depart_start_of_file(self, node: Element) -> None: self.docnames.pop() - def visit_desc(self, node): - # type: (nodes.Element) -> None + def visit_desc(self, node: Element) -> None: self.body.append(self.starttag(node, 'dl', CLASS=node['objtype'])) - def depart_desc(self, node): - # type: (nodes.Element) -> None + def depart_desc(self, node: Element) -> None: self.body.append('\n\n') - def visit_desc_signature(self, node): - # type: (nodes.Element) -> None + def visit_desc_signature(self, node: Element) -> None: # the id is set automatically self.body.append(self.starttag(node, 'dt')) # anchor for per-desc interactive data @@ -97,57 +92,45 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): and node['ids'] and node['first']: self.body.append('' % node['ids'][0]) - def depart_desc_signature(self, node): - # type: (nodes.Element) -> None + def depart_desc_signature(self, node: Element) -> None: if not node.get('is_multiline'): self.add_permalink_ref(node, _('Permalink to this definition')) self.body.append('\n') - def visit_desc_signature_line(self, node): - # type: (nodes.Element) -> None + def visit_desc_signature_line(self, node: Element) -> None: pass - def depart_desc_signature_line(self, node): - # type: (nodes.Element) -> None + def depart_desc_signature_line(self, node: Element) -> None: if node.get('add_permalink'): # the permalink info is on the parent desc_signature node self.add_permalink_ref(node.parent, _('Permalink to this definition')) self.body.append('
    ') - def visit_desc_addname(self, node): - # type: (nodes.Element) -> None + def visit_desc_addname(self, node: Element) -> None: self.body.append(self.starttag(node, 'code', '', CLASS='sig-prename descclassname')) - def depart_desc_addname(self, node): - # type: (nodes.Element) -> None + def depart_desc_addname(self, node: Element) -> None: self.body.append('') - def visit_desc_type(self, node): - # type: (nodes.Element) -> None + def visit_desc_type(self, node: Element) -> None: pass - def depart_desc_type(self, node): - # type: (nodes.Element) -> None + def depart_desc_type(self, node: Element) -> None: pass - def visit_desc_returns(self, node): - # type: (nodes.Element) -> None + def visit_desc_returns(self, node: Element) -> None: self.body.append(' → ') - def depart_desc_returns(self, node): - # type: (nodes.Element) -> None + def depart_desc_returns(self, node: Element) -> None: pass - def visit_desc_name(self, node): - # type: (nodes.Element) -> None + def visit_desc_name(self, node: Element) -> None: self.body.append(self.starttag(node, 'code', '', CLASS='sig-name descname')) - def depart_desc_name(self, node): - # type: (nodes.Element) -> None + def depart_desc_name(self, node: Element) -> None: self.body.append('') - def visit_desc_parameterlist(self, node): - # type: (nodes.Element) -> None + def visit_desc_parameterlist(self, node: Element) -> None: self.body.append('(') self.first_param = 1 self.optional_param_level = 0 @@ -156,8 +139,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): for c in node.children]) self.param_separator = node.child_text_separator - def depart_desc_parameterlist(self, node): - # type: (nodes.Element) -> None + def depart_desc_parameterlist(self, node: Element) -> None: self.body.append(')') # 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]) # - def visit_desc_parameter(self, node): - # type: (nodes.Element) -> None + def visit_desc_parameter(self, node: Element) -> None: if self.first_param: self.first_param = 0 elif not self.required_params_left: @@ -177,50 +158,40 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): if not node.hasattr('noemph'): self.body.append('') - def depart_desc_parameter(self, node): - # type: (nodes.Element) -> None + def depart_desc_parameter(self, node: Element) -> None: if not node.hasattr('noemph'): self.body.append('') if self.required_params_left: self.body.append(self.param_separator) - def visit_desc_optional(self, node): - # type: (nodes.Element) -> None + def visit_desc_optional(self, node: Element) -> None: self.optional_param_level += 1 self.body.append('[') - def depart_desc_optional(self, node): - # type: (nodes.Element) -> None + def depart_desc_optional(self, node: Element) -> None: self.optional_param_level -= 1 self.body.append(']') - def visit_desc_annotation(self, node): - # type: (nodes.Element) -> None + def visit_desc_annotation(self, node: Element) -> None: self.body.append(self.starttag(node, 'em', '', CLASS='property')) - def depart_desc_annotation(self, node): - # type: (nodes.Element) -> None + def depart_desc_annotation(self, node: Element) -> None: self.body.append('') - def visit_desc_content(self, node): - # type: (nodes.Element) -> None + def visit_desc_content(self, node: Element) -> None: self.body.append(self.starttag(node, 'dd', '')) - def depart_desc_content(self, node): - # type: (nodes.Element) -> None + def depart_desc_content(self, node: Element) -> None: self.body.append('') - def visit_versionmodified(self, node): - # type: (nodes.Element) -> None + def visit_versionmodified(self, node: Element) -> None: self.body.append(self.starttag(node, 'div', CLASS=node['type'])) - def depart_versionmodified(self, node): - # type: (nodes.Element) -> None + def depart_versionmodified(self, node: Element) -> None: self.body.append('

    \n') # overwritten - def visit_reference(self, node): - # type: (nodes.Element) -> None + def visit_reference(self, node: Element) -> None: atts = {'class': 'reference'} if node.get('internal') or 'refuri' not in node: atts['class'] += ' internal' @@ -248,37 +219,30 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): self.body.append(('%s' + self.secnumber_suffix) % '.'.join(map(str, node['secnumber']))) - def visit_number_reference(self, node): - # type: (nodes.Element) -> None + def visit_number_reference(self, node: Element) -> None: self.visit_reference(node) - def depart_number_reference(self, node): - # type: (nodes.Element) -> None + def depart_number_reference(self, node: Element) -> None: self.depart_reference(node) # overwritten -- we don't want source comments to show up in the HTML - def visit_comment(self, node): # type: ignore - # type: (nodes.Element) -> None + def visit_comment(self, node: Element) -> None: # type: ignore raise nodes.SkipNode # overwritten - def visit_admonition(self, node, name=''): - # type: (nodes.Element, str) -> None + def visit_admonition(self, node: Element, name: str = '') -> None: self.body.append(self.starttag( node, 'div', CLASS=('admonition ' + name))) if name: node.insert(0, nodes.title(name, admonitionlabels[name])) - def visit_seealso(self, node): - # type: (nodes.Element) -> None + def visit_seealso(self, node: Element) -> None: self.visit_admonition(node, 'seealso') - def depart_seealso(self, node): - # type: (nodes.Element) -> None + def depart_seealso(self, node: Element) -> None: self.depart_admonition(node) - def add_secnumber(self, node): - # type: (nodes.Element) -> None + def add_secnumber(self, node: Element) -> None: if node.get('secnumber'): self.body.append('.'.join(map(str, node['secnumber'])) + self.secnumber_suffix) @@ -297,10 +261,8 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): self.body.append('.'.join(map(str, numbers)) + self.secnumber_suffix) - def add_fignumber(self, node): - # type: (nodes.Element) -> None - def append_fignumber(figtype, figure_id): - # type: (str, str) -> None + def add_fignumber(self, node: Element) -> None: + def append_fignumber(figtype: str, figure_id: str) -> None: if self.builder.name == 'singlehtml': key = "%s/%s" % (self.docnames[-1], figtype) else: @@ -325,55 +287,47 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): else: append_fignumber(figtype, node['ids'][0]) - def add_permalink_ref(self, node, title): - # type: (nodes.Element, str) -> None + def add_permalink_ref(self, node: Element, title: str) -> None: if node['ids'] and self.permalink_text and self.builder.add_permalinks: format = '%s' self.body.append(format % (node['ids'][0], title, self.permalink_text)) # overwritten - def visit_bullet_list(self, node): - # type: (nodes.Element) -> None + def visit_bullet_list(self, node: Element) -> None: if len(node) == 1 and isinstance(node[0], addnodes.toctree): # avoid emitting empty
      raise nodes.SkipNode super().visit_bullet_list(node) # overwritten - def visit_definition(self, node): - # type: (nodes.Element) -> None + def visit_definition(self, node: Element) -> None: # don't insert here. self.body.append(self.starttag(node, 'dd', '')) # overwritten - def depart_definition(self, node): - # type: (nodes.Element) -> None + def depart_definition(self, node: Element) -> None: self.body.append('\n') # overwritten - def visit_classifier(self, node): - # type: (nodes.Element) -> None + def visit_classifier(self, node: Element) -> None: self.body.append(self.starttag(node, 'span', '', CLASS='classifier')) # overwritten - def depart_classifier(self, node): - # type: (nodes.Element) -> None + def depart_classifier(self, node: Element) -> None: self.body.append('') - 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): # close `
      ` tag at the tail of classifiers self.body.append('
      ') # overwritten - def visit_term(self, node): - # type: (nodes.Element) -> None + def visit_term(self, node: Element) -> None: self.body.append(self.starttag(node, 'dt', '')) # overwritten - def depart_term(self, node): - # type: (nodes.Element) -> None - next_node = node.next_node(descend=False, siblings=True) # type: nodes.Node + def depart_term(self, node: Element) -> None: + next_node = node.next_node(descend=False, siblings=True) # type: Node if isinstance(next_node, nodes.classifier): # Leave the end tag to `self.depart_classifier()`, in case # there's a classifier. @@ -382,16 +336,14 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): self.body.append('') # overwritten - def visit_title(self, node): - # type: (nodes.Element) -> None + def visit_title(self, node: Element) -> None: super().visit_title(node) self.add_secnumber(node) self.add_fignumber(node.parent) if isinstance(node.parent, nodes.table): self.body.append('') - def depart_title(self, node): - # type: (nodes.Element) -> None + def depart_title(self, node: Element) -> None: close_tag = self.context[-1] if (self.permalink_text and self.builder.add_permalinks and node.parent.hasattr('ids') and node.parent['ids']): @@ -413,8 +365,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): super().depart_title(node) # overwritten - def visit_literal_block(self, node): - # type: (nodes.Element) -> None + def visit_literal_block(self, node: Element) -> None: if node.rawsource != node.astext(): # most probably a parsed-literal block -- don't highlight return super().visit_literal_block(node) @@ -438,8 +389,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): self.body.append(starttag + highlighted + '
      \n') raise nodes.SkipNode - def visit_caption(self, node): - # type: (nodes.Element) -> None + def visit_caption(self, node: Element) -> None: if isinstance(node.parent, nodes.container) and node.parent.get('literal_block'): self.body.append('
      ') else: @@ -447,8 +397,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): self.add_fignumber(node.parent) self.body.append(self.starttag(node, 'span', '', CLASS='caption-text')) - def depart_caption(self, node): - # type: (nodes.Element) -> None + def depart_caption(self, node: Element) -> None: self.body.append('') # append permalink if available @@ -464,22 +413,18 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): else: super().depart_caption(node) - def visit_doctest_block(self, node): - # type: (nodes.Element) -> None + def visit_doctest_block(self, node: Element) -> None: self.visit_literal_block(node) # overwritten to add the
      (for XHTML compliance) - def visit_block_quote(self, node): - # type: (nodes.Element) -> None + def visit_block_quote(self, node: Element) -> None: self.body.append(self.starttag(node, 'blockquote') + '
      ') - def depart_block_quote(self, node): - # type: (nodes.Element) -> None + def depart_block_quote(self, node: Element) -> None: self.body.append('
      \n') # overwritten - def visit_literal(self, node): - # type: (nodes.Element) -> None + def visit_literal(self, node: Element) -> None: if 'kbd' in node['classes']: self.body.append(self.starttag(node, 'kbd', '', CLASS='docutils literal notranslate')) @@ -488,16 +433,14 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): CLASS='docutils literal notranslate')) self.protect_literal_text += 1 - def depart_literal(self, node): - # type: (nodes.Element) -> None + def depart_literal(self, node: Element) -> None: if 'kbd' in node['classes']: self.body.append('') else: self.protect_literal_text -= 1 self.body.append('') - def visit_productionlist(self, node): - # type: (nodes.Element) -> None + def visit_productionlist(self, node: Element) -> None: self.body.append(self.starttag(node, 'pre')) names = [] productionlist = cast(Iterable[addnodes.production], node) @@ -517,37 +460,29 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): self.body.append('\n') raise nodes.SkipNode - def depart_productionlist(self, node): - # type: (nodes.Element) -> None + def depart_productionlist(self, node: Element) -> None: pass - def visit_production(self, node): - # type: (nodes.Element) -> None + def visit_production(self, node: Element) -> None: pass - def depart_production(self, node): - # type: (nodes.Element) -> None + def depart_production(self, node: Element) -> None: pass - def visit_centered(self, node): - # type: (nodes.Element) -> None + def visit_centered(self, node: Element) -> None: self.body.append(self.starttag(node, 'p', CLASS="centered") + '') - def depart_centered(self, node): - # type: (nodes.Element) -> None + def depart_centered(self, node: Element) -> None: self.body.append('

      ') - def visit_compact_paragraph(self, node): - # type: (nodes.Element) -> None + def visit_compact_paragraph(self, node: Element) -> None: pass - def depart_compact_paragraph(self, node): - # type: (nodes.Element) -> None + def depart_compact_paragraph(self, node: Element) -> None: pass - def visit_download_reference(self, node): - # type: (nodes.Element) -> None + def visit_download_reference(self, node: Element) -> None: atts = {'class': 'reference download', 'download': ''} @@ -566,13 +501,11 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): else: self.context.append('') - def depart_download_reference(self, node): - # type: (nodes.Element) -> None + def depart_download_reference(self, node: Element) -> None: self.body.append(self.context.pop()) # overwritten - def visit_image(self, node): - # type: (nodes.Element) -> None + def visit_image(self, node: Element) -> None: olduri = node['uri'] # rewrite the URI if the environment knows about it if olduri in self.builder.images: @@ -613,62 +546,49 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): super().visit_image(node) # overwritten - def depart_image(self, node): - # type: (nodes.Element) -> None + def depart_image(self, node: Element) -> None: if node['uri'].lower().endswith(('svg', 'svgz')): self.body.append(self.context.pop()) else: super().depart_image(node) - def visit_toctree(self, node): - # type: (nodes.Element) -> None + def visit_toctree(self, node: Element) -> None: # this only happens when formatting a toc from env.tocs -- in this # case we don't want to include the subtree raise nodes.SkipNode - def visit_index(self, node): - # type: (nodes.Element) -> None + def visit_index(self, node: Element) -> None: raise nodes.SkipNode - def visit_tabular_col_spec(self, node): - # type: (nodes.Element) -> None + def visit_tabular_col_spec(self, node: Element) -> None: raise nodes.SkipNode - def visit_glossary(self, node): - # type: (nodes.Element) -> None + def visit_glossary(self, node: Element) -> None: pass - def depart_glossary(self, node): - # type: (nodes.Element) -> None + def depart_glossary(self, node: Element) -> None: pass - def visit_acks(self, node): - # type: (nodes.Element) -> None + def visit_acks(self, node: Element) -> None: pass - def depart_acks(self, node): - # type: (nodes.Element) -> None + def depart_acks(self, node: Element) -> None: pass - def visit_hlist(self, node): - # type: (nodes.Element) -> None + def visit_hlist(self, node: Element) -> None: self.body.append('') - def depart_hlist(self, node): - # type: (nodes.Element) -> None + def depart_hlist(self, node: Element) -> None: self.body.append('
      \n') - def visit_hlistcol(self, node): - # type: (nodes.Element) -> None + def visit_hlistcol(self, node: Element) -> None: self.body.append('') - def depart_hlistcol(self, node): - # type: (nodes.Element) -> None + def depart_hlistcol(self, node: Element) -> None: self.body.append('') # overwritten - def visit_Text(self, node): - # type: (nodes.Text) -> None + def visit_Text(self, node: Text) -> None: text = node.astext() encoded = self.encode(text) if self.protect_literal_text: @@ -689,122 +609,95 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): encoded = self.cloak_email(encoded) self.body.append(encoded) - def visit_note(self, node): - # type: (nodes.Element) -> None + def visit_note(self, node: Element) -> None: self.visit_admonition(node, 'note') - def depart_note(self, node): - # type: (nodes.Element) -> None + def depart_note(self, node: Element) -> None: self.depart_admonition(node) - def visit_warning(self, node): - # type: (nodes.Element) -> None + def visit_warning(self, node: Element) -> None: self.visit_admonition(node, 'warning') - def depart_warning(self, node): - # type: (nodes.Element) -> None + def depart_warning(self, node: Element) -> None: self.depart_admonition(node) - def visit_attention(self, node): - # type: (nodes.Element) -> None + def visit_attention(self, node: Element) -> None: self.visit_admonition(node, 'attention') - def depart_attention(self, node): - # type: (nodes.Element) -> None + def depart_attention(self, node: Element) -> None: self.depart_admonition(node) - def visit_caution(self, node): - # type: (nodes.Element) -> None + def visit_caution(self, node: Element) -> None: self.visit_admonition(node, 'caution') - def depart_caution(self, node): - # type: (nodes.Element) -> None + def depart_caution(self, node: Element) -> None: self.depart_admonition(node) - def visit_danger(self, node): - # type: (nodes.Element) -> None + def visit_danger(self, node: Element) -> None: self.visit_admonition(node, 'danger') - def depart_danger(self, node): - # type: (nodes.Element) -> None + def depart_danger(self, node: Element) -> None: self.depart_admonition(node) - def visit_error(self, node): - # type: (nodes.Element) -> None + def visit_error(self, node: Element) -> None: self.visit_admonition(node, 'error') - def depart_error(self, node): - # type: (nodes.Element) -> None + def depart_error(self, node: Element) -> None: self.depart_admonition(node) - def visit_hint(self, node): - # type: (nodes.Element) -> None + def visit_hint(self, node: Element) -> None: self.visit_admonition(node, 'hint') - def depart_hint(self, node): - # type: (nodes.Element) -> None + def depart_hint(self, node: Element) -> None: self.depart_admonition(node) - def visit_important(self, node): - # type: (nodes.Element) -> None + def visit_important(self, node: Element) -> None: self.visit_admonition(node, 'important') - def depart_important(self, node): - # type: (nodes.Element) -> None + def depart_important(self, node: Element) -> None: self.depart_admonition(node) - def visit_tip(self, node): - # type: (nodes.Element) -> None + def visit_tip(self, node: Element) -> None: self.visit_admonition(node, 'tip') - def depart_tip(self, node): - # type: (nodes.Element) -> None + def depart_tip(self, node: Element) -> None: self.depart_admonition(node) - def visit_literal_emphasis(self, node): - # type: (nodes.Element) -> None + def visit_literal_emphasis(self, node: Element) -> None: return self.visit_emphasis(node) - def depart_literal_emphasis(self, node): - # type: (nodes.Element) -> None + def depart_literal_emphasis(self, node: Element) -> None: return self.depart_emphasis(node) - def visit_literal_strong(self, node): - # type: (nodes.Element) -> None + def visit_literal_strong(self, node: Element) -> None: return self.visit_strong(node) - def depart_literal_strong(self, node): - # type: (nodes.Element) -> None + def depart_literal_strong(self, node: Element) -> None: return self.depart_strong(node) - def visit_abbreviation(self, node): - # type: (nodes.Element) -> None + def visit_abbreviation(self, node: Element) -> None: attrs = {} if node.hasattr('explanation'): attrs['title'] = node['explanation'] self.body.append(self.starttag(node, 'abbr', '', **attrs)) - def depart_abbreviation(self, node): - # type: (nodes.Element) -> None + def depart_abbreviation(self, node: Element) -> None: self.body.append('') - def visit_manpage(self, node): - # type: (nodes.Element) -> None + def visit_manpage(self, node: Element) -> None: self.visit_literal_emphasis(node) if self.manpages_url: node['refuri'] = self.manpages_url.format(**node.attributes) self.visit_reference(node) - def depart_manpage(self, node): - # type: (nodes.Element) -> None + def depart_manpage(self, node: Element) -> None: if self.manpages_url: self.depart_reference(node) self.depart_literal_emphasis(node) # overwritten to add even/odd classes - def generate_targets_for_table(self, node): - # type: (nodes.Element) -> None + def generate_targets_for_table(self, node: Element) -> None: """Generate hyperlink targets for tables. Original visit_table() generates hyperlink targets inside table tags @@ -817,8 +710,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): self.body.append('' % id) node['ids'].remove(id) - def visit_table(self, node): - # type: (nodes.Element) -> None + def visit_table(self, node: Element) -> None: self.generate_targets_for_table(node) self._table_row_index = 0 @@ -830,8 +722,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): tag = self.starttag(node, 'table', CLASS=' '.join(classes)) self.body.append(tag) - def visit_row(self, node): - # type: (nodes.Element) -> None + def visit_row(self, node: Element) -> None: self._table_row_index += 1 if self._table_row_index % 2 == 0: node['classes'].append('row-even') @@ -840,45 +731,38 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): self.body.append(self.starttag(node, 'tr', '')) node.column = 0 # type: ignore - def visit_field_list(self, node): - # type: (nodes.Element) -> None + def visit_field_list(self, node: Element) -> None: self._fieldlist_row_index = 0 return super().visit_field_list(node) - def visit_field(self, node): - # type: (nodes.Element) -> None + def visit_field(self, node: Element) -> None: self._fieldlist_row_index += 1 if self._fieldlist_row_index % 2 == 0: node['classes'].append('field-even') else: node['classes'].append('field-odd') - def visit_math(self, node, math_env=''): - # type: (nodes.Element, str) -> None + def visit_math(self, node: Element, math_env: str = '') -> None: name = self.builder.math_renderer_name visit, _ = self.builder.app.registry.html_inline_math_renderers[name] visit(self, node) - def depart_math(self, node, math_env=''): - # type: (nodes.Element, str) -> None + def depart_math(self, node: Element, math_env: str = '') -> None: name = self.builder.math_renderer_name _, depart = self.builder.app.registry.html_inline_math_renderers[name] if depart: depart(self, node) - def visit_math_block(self, node, math_env=''): - # type: (nodes.Element, str) -> None + def visit_math_block(self, node: Element, math_env: str = '') -> None: name = self.builder.math_renderer_name visit, _ = self.builder.app.registry.html_block_math_renderers[name] visit(self, node) - def depart_math_block(self, node, math_env=''): - # type: (nodes.Element, str) -> None + def depart_math_block(self, node: Element, math_env: str = '') -> None: name = self.builder.math_renderer_name _, depart = self.builder.app.registry.html_block_math_renderers[name] if depart: depart(self, node) - def unknown_visit(self, node): - # type: (nodes.Node) -> None + def unknown_visit(self, node: Node) -> None: raise NotImplementedError('Unknown node: ' + node.__class__.__name__) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 452225e54..34022e421 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -15,21 +15,24 @@ import re import warnings from collections import defaultdict from os import path -from typing import Iterable, cast +from typing import Any, Dict, Iterable, Iterator, List, Tuple, Set, Union +from typing import cast from docutils import nodes, writers +from docutils.nodes import Element, Node, Text from sphinx import addnodes from sphinx import highlighting from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias +from sphinx.domains import IndexEntry from sphinx.domains.std import StandardDomain from sphinx.errors import SphinxError from sphinx.locale import admonitionlabels, _, __ -from sphinx.util import split_into, logging +from sphinx.util import split_into, logging, texescape from sphinx.util.docutils import SphinxTranslator from sphinx.util.nodes import clean_astext, get_prev_node from sphinx.util.template import LaTeXRenderer -from sphinx.util.texescape import get_escape_func, tex_replace_map +from sphinx.util.texescape import tex_replace_map try: from docutils.utils.roman import toRoman @@ -39,12 +42,8 @@ except ImportError: if False: # For type annotation - from typing import Any, Callable, Dict, Iterator, List, Pattern, Tuple, Set, Union # NOQA - from sphinx.builders.latex import LaTeXBuilder # NOQA - from sphinx.builders.latex.nodes import ( # NOQA - captioned_literal_block, footnotemark, footnotetext, math_reference, thebibliography - ) - from sphinx.domains import IndexEntry # NOQA + from sphinx.builders.latex import LaTeXBuilder + logger = logging.getLogger(__name__) @@ -218,6 +217,15 @@ ADDITIONAL_SETTINGS = { 'fncychap': '', 'geometry': '\\usepackage[dvipdfm]{geometry}', }, + 'uplatex': { + 'latex_engine': 'uplatex', + 'babel': '', + 'classoptions': ',dvipdfmx', + 'fontpkg': '\\usepackage{times}', + 'textgreek': '', + 'fncychap': '', + 'geometry': '\\usepackage[dvipdfm]{geometry}', + }, # special settings for latex_engine + language_code ('xelatex', 'fr'): { @@ -257,13 +265,11 @@ class LaTeXWriter(writers.Writer): output = None - def __init__(self, builder): - # type: (LaTeXBuilder) -> None + def __init__(self, builder: "LaTeXBuilder") -> None: super().__init__() self.builder = builder - def translate(self): - # type: () -> None + def translate(self) -> None: visitor = self.builder.create_translator(self.document, self.builder) self.document.walkabout(visitor) self.output = cast(LaTeXTranslator, visitor).astext() @@ -274,8 +280,7 @@ class LaTeXWriter(writers.Writer): class Table: """A table data""" - def __init__(self, node): - # type: (nodes.Element) -> None + def __init__(self, node: Element) -> None: self.header = [] # type: List[str] self.body = [] # type: List[str] self.align = node.get('align') @@ -299,13 +304,11 @@ class Table: # (cell = rectangular area) self.cell_id = 0 # last assigned cell_id - def is_longtable(self): - # type: () -> bool + def is_longtable(self) -> bool: """True if and only if table uses longtable environment.""" return self.row > 30 or 'longtable' in self.classes - def get_table_type(self): - # type: () -> str + def get_table_type(self) -> str: """Returns the LaTeX environment name for the table. The class currently supports: @@ -325,8 +328,7 @@ class Table: else: return 'tabulary' - def get_colspec(self): - # type: () -> str + def get_colspec(self) -> str: """Returns a column spec of table. This is what LaTeX calls the 'preamble argument' of the used table environment. @@ -349,8 +351,7 @@ class Table: else: return '{|' + ('l|' * self.colcount) + '}\n' - def add_cell(self, height, width): - # type: (int, int) -> None + def add_cell(self, height: int, width: int) -> None: """Adds a new cell to a table. It will be located at current position: (``self.row``, ``self.col``). @@ -361,8 +362,7 @@ class Table: assert self.cells[(self.row + row, self.col + col)] == 0 self.cells[(self.row + row, self.col + col)] = self.cell_id - def cell(self, row=None, col=None): - # type: (int, int) -> TableCell + def cell(self, row: int = None, col: int = None) -> "TableCell": """Returns a cell object (i.e. rectangular area) containing given position. If no option arguments: ``row`` or ``col`` are given, the current position; @@ -381,8 +381,7 @@ class Table: class TableCell: """A cell data of tables.""" - def __init__(self, table, row, col): - # type: (Table, int, int) -> None + def __init__(self, table: Table, row: int, col: int) -> None: if table.cells[(row, col)] == 0: raise IndexError @@ -398,8 +397,7 @@ class TableCell: self.col -= 1 @property - def width(self): - # type: () -> int + def width(self) -> int: """Returns the cell width.""" width = 0 while self.table.cells[(self.row, self.col + width)] == self.cell_id: @@ -407,8 +405,7 @@ class TableCell: return width @property - def height(self): - # type: () -> int + def height(self) -> int: """Returns the cell height.""" height = 0 while self.table.cells[(self.row + height, self.col)] == self.cell_id: @@ -416,14 +413,12 @@ class TableCell: return height -def escape_abbr(text): - # type: (str) -> str +def escape_abbr(text: str) -> str: """Adjust spacing after abbreviations.""" return re.sub(r'\.(?=\s|$)', r'.\@', text) -def rstdim_to_latexdim(width_str, scale = 100): - # type: (str, int) -> str +def rstdim_to_latexdim(width_str: str, scale: int = 100) -> str: """Convert `width_str` with rst length to LaTeX length.""" match = re.match(r'^(\d*\.?\d*)\s*(\S*)$', width_str) if not match: @@ -461,8 +456,7 @@ class LaTeXTranslator(SphinxTranslator): # sphinx specific document classes docclasses = ('howto', 'manual') - def __init__(self, document, builder): - # type: (nodes.document, LaTeXBuilder) -> None + def __init__(self, document: nodes.document, builder: "LaTeXBuilder") -> None: super().__init__(document, builder) self.body = [] # type: List[str] @@ -483,9 +477,6 @@ class LaTeXTranslator(SphinxTranslator): self.compact_list = 0 self.first_param = 0 - # escape helper - self.escape = get_escape_func(self.config.latex_engine) - # sort out some elements self.elements = self.builder.context.copy() @@ -648,64 +639,57 @@ class LaTeXTranslator(SphinxTranslator): self.curfilestack = [] # type: List[str] self.handled_abbrs = set() # type: Set[str] - def pushbody(self, newbody): - # type: (List[str]) -> None + def pushbody(self, newbody: List[str]) -> None: self.bodystack.append(self.body) self.body = newbody - def popbody(self): - # type: () -> List[str] + def popbody(self) -> List[str]: body = self.body self.body = self.bodystack.pop() return body - def format_docclass(self, docclass): - # type: (str) -> str + def format_docclass(self, docclass: str) -> str: """ prepends prefix to sphinx document classes """ if docclass in self.docclasses: docclass = 'sphinx' + docclass return docclass - def astext(self): - # type: () -> str + def astext(self) -> str: self.elements.update({ 'body': ''.join(self.body), 'indices': self.generate_indices() }) return self.render('latex.tex_t', self.elements) - def hypertarget(self, id, withdoc=True, anchor=True): - # type: (str, bool, bool) -> str + def hypertarget(self, id: str, withdoc: bool = True, anchor: bool = True) -> str: if withdoc: id = self.curfilestack[-1] + ':' + id return (anchor and '\\phantomsection' or '') + \ '\\label{%s}' % self.idescape(id) - def hypertarget_to(self, node, anchor=False): - # type: (nodes.Element, bool) -> str + def hypertarget_to(self, node: Element, anchor: bool = False) -> str: labels = ''.join(self.hypertarget(node_id, anchor=False) for node_id in node['ids']) if anchor: return r'\phantomsection' + labels else: return labels - def hyperlink(self, id): - # type: (str) -> str + def hyperlink(self, id: str) -> str: return '{\\hyperref[%s]{' % self.idescape(id) - def hyperpageref(self, id): - # type: (str) -> str + def hyperpageref(self, id: str) -> str: return '\\autopageref*{%s}' % self.idescape(id) - def idescape(self, id): - # type: (str) -> str + def escape(self, s: str) -> str: + return texescape.escape(s, self.config.latex_engine) + + def idescape(self, id: str) -> str: return '\\detokenize{%s}' % str(id).translate(tex_replace_map).\ encode('ascii', 'backslashreplace').decode('ascii').\ replace('\\', '_') - def babel_renewcommand(self, command, definition): - # type: (str, str) -> str + def babel_renewcommand(self, command: str, definition: str) -> str: if self.elements['multilingual']: prefix = '\\addto\\captions%s{' % self.babel.get_language() suffix = '}' @@ -715,10 +699,8 @@ class LaTeXTranslator(SphinxTranslator): return ('%s\\renewcommand{%s}{%s}%s\n' % (prefix, command, definition, suffix)) - def generate_indices(self): - # type: () -> str - def generate(content, collapsed): - # type: (List[Tuple[str, List[IndexEntry]]], bool) -> None + def generate_indices(self) -> str: + def generate(content: List[Tuple[str, List[IndexEntry]]], collapsed: bool) -> None: ret.append('\\begin{sphinxtheindex}\n') ret.append('\\let\\bigletter\\sphinxstyleindexlettergroup\n') for i, (letter, entries) in enumerate(content): @@ -757,8 +739,7 @@ class LaTeXTranslator(SphinxTranslator): return ''.join(ret) - def render(self, template_name, variables): - # type: (str, Dict) -> str + def render(self, template_name: str, variables: Dict) -> str: renderer = LaTeXRenderer(latex_engine=self.config.latex_engine) for template_dir in self.builder.config.templates_path: template = path.join(self.builder.confdir, template_dir, @@ -768,8 +749,7 @@ class LaTeXTranslator(SphinxTranslator): return renderer.render(template_name, variables) - def visit_document(self, node): - # type: (nodes.Element) -> None + def visit_document(self, node: Element) -> None: self.curfilestack.append(node.get('docname', '')) if self.first_document == 1: # the first document is all the regular content ... @@ -783,69 +763,55 @@ class LaTeXTranslator(SphinxTranslator): # "- 1" because the level is increased before the title is visited self.sectionlevel = self.top_sectionlevel - 1 - def depart_document(self, node): - # type: (nodes.Element) -> None + def depart_document(self, node: Element) -> None: pass - def visit_start_of_file(self, node): - # type: (nodes.Element) -> None + def visit_start_of_file(self, node: Element) -> None: self.curfilestack.append(node['docname']) - def depart_start_of_file(self, node): - # type: (nodes.Element) -> None + def depart_start_of_file(self, node: Element) -> None: self.curfilestack.pop() - def visit_section(self, node): - # type: (nodes.Element) -> None + def visit_section(self, node: Element) -> None: if not self.this_is_the_title: self.sectionlevel += 1 self.body.append('\n\n') - def depart_section(self, node): - # type: (nodes.Element) -> None + def depart_section(self, node: Element) -> None: self.sectionlevel = max(self.sectionlevel - 1, self.top_sectionlevel - 1) - def visit_problematic(self, node): - # type: (nodes.Element) -> None + def visit_problematic(self, node: Element) -> None: self.body.append(r'{\color{red}\bfseries{}') - def depart_problematic(self, node): - # type: (nodes.Element) -> None + def depart_problematic(self, node: Element) -> None: self.body.append('}') - def visit_topic(self, node): - # type: (nodes.Element) -> None + def visit_topic(self, node: Element) -> None: self.in_minipage = 1 self.body.append('\n\\begin{sphinxShadowBox}\n') - def depart_topic(self, node): - # type: (nodes.Element) -> None + def depart_topic(self, node: Element) -> None: self.in_minipage = 0 self.body.append('\\end{sphinxShadowBox}\n') visit_sidebar = visit_topic depart_sidebar = depart_topic - def visit_glossary(self, node): - # type: (nodes.Element) -> None + def visit_glossary(self, node: Element) -> None: pass - def depart_glossary(self, node): - # type: (nodes.Element) -> None + def depart_glossary(self, node: Element) -> None: pass - def visit_productionlist(self, node): - # type: (nodes.Element) -> None + def visit_productionlist(self, node: Element) -> None: self.body.append('\n\n\\begin{productionlist}\n') self.in_production_list = 1 - def depart_productionlist(self, node): - # type: (nodes.Element) -> None + def depart_productionlist(self, node: Element) -> None: self.body.append('\\end{productionlist}\n\n') self.in_production_list = 0 - def visit_production(self, node): - # type: (nodes.Element) -> None + def visit_production(self, node: Element) -> None: if node['tokenname']: tn = node['tokenname'] self.body.append(self.hypertarget('grammar-token-' + tn)) @@ -853,20 +819,16 @@ class LaTeXTranslator(SphinxTranslator): else: self.body.append('\\productioncont{') - def depart_production(self, node): - # type: (nodes.Element) -> None + def depart_production(self, node: Element) -> None: self.body.append('}\n') - def visit_transition(self, node): - # type: (nodes.Element) -> None + def visit_transition(self, node: Element) -> None: self.body.append(self.elements['transition']) - def depart_transition(self, node): - # type: (nodes.Element) -> None + def depart_transition(self, node: Element) -> None: pass - def visit_title(self, node): - # type: (nodes.Element) -> None + def visit_title(self, node: Element) -> None: parent = node.parent if isinstance(parent, addnodes.seealso): # the environment already handles this @@ -914,38 +876,32 @@ class LaTeXTranslator(SphinxTranslator): self.context.append('}\n') self.in_title = 1 - def depart_title(self, node): - # type: (nodes.Element) -> None + def depart_title(self, node: Element) -> None: self.in_title = 0 if isinstance(node.parent, nodes.table): self.table.caption = self.popbody() else: self.body.append(self.context.pop()) - def visit_subtitle(self, node): - # type: (nodes.Element) -> None + def visit_subtitle(self, node: Element) -> None: if isinstance(node.parent, nodes.sidebar): self.body.append('\\sphinxstylesidebarsubtitle{') self.context.append('}\n') else: self.context.append('') - def depart_subtitle(self, node): - # type: (nodes.Element) -> None + def depart_subtitle(self, node: Element) -> None: self.body.append(self.context.pop()) - def visit_desc(self, node): - # type: (nodes.Element) -> None + def visit_desc(self, node: Element) -> None: self.body.append('\n\n\\begin{fulllineitems}\n') if self.table: self.table.has_problematic = True - def depart_desc(self, node): - # type: (nodes.Element) -> None + def depart_desc(self, node: Element) -> None: self.body.append('\n\\end{fulllineitems}\n\n') - def _visit_signature_line(self, node): - # type: (nodes.Element) -> None + def _visit_signature_line(self, node: Element) -> None: for child in node: if isinstance(child, addnodes.desc_parameterlist): self.body.append(r'\pysiglinewithargsret{') @@ -953,12 +909,10 @@ class LaTeXTranslator(SphinxTranslator): else: self.body.append(r'\pysigline{') - def _depart_signature_line(self, node): - # type: (nodes.Element) -> None + def _depart_signature_line(self, node: Element) -> None: self.body.append('}') - def visit_desc_signature(self, node): - # type: (nodes.Element) -> None + def visit_desc_signature(self, node: Element) -> None: if node.parent['objtype'] != 'describe' and node['ids']: hyper = self.hypertarget(node['ids'][0]) else: @@ -969,72 +923,58 @@ class LaTeXTranslator(SphinxTranslator): else: self.body.append('%\n\\pysigstartmultiline\n') - def depart_desc_signature(self, node): - # type: (nodes.Element) -> None + def depart_desc_signature(self, node: Element) -> None: if not node.get('is_multiline'): self._depart_signature_line(node) else: self.body.append('%\n\\pysigstopmultiline') - def visit_desc_signature_line(self, node): - # type: (nodes.Element) -> None + def visit_desc_signature_line(self, node: Element) -> None: self._visit_signature_line(node) - def depart_desc_signature_line(self, node): - # type: (nodes.Element) -> None + def depart_desc_signature_line(self, node: Element) -> None: self._depart_signature_line(node) - def visit_desc_addname(self, node): - # type: (nodes.Element) -> None + def visit_desc_addname(self, node: Element) -> None: self.body.append(r'\sphinxcode{\sphinxupquote{') self.literal_whitespace += 1 - def depart_desc_addname(self, node): - # type: (nodes.Element) -> None + def depart_desc_addname(self, node: Element) -> None: self.body.append('}}') self.literal_whitespace -= 1 - def visit_desc_type(self, node): - # type: (nodes.Element) -> None + def visit_desc_type(self, node: Element) -> None: pass - def depart_desc_type(self, node): - # type: (nodes.Element) -> None + def depart_desc_type(self, node: Element) -> None: pass - def visit_desc_returns(self, node): - # type: (nodes.Element) -> None + def visit_desc_returns(self, node: Element) -> None: self.body.append(r'{ $\rightarrow$ ') - def depart_desc_returns(self, node): - # type: (nodes.Element) -> None + def depart_desc_returns(self, node: Element) -> None: self.body.append(r'}') - def visit_desc_name(self, node): - # type: (nodes.Element) -> None + def visit_desc_name(self, node: Element) -> None: self.body.append(r'\sphinxbfcode{\sphinxupquote{') self.no_contractions += 1 self.literal_whitespace += 1 - def depart_desc_name(self, node): - # type: (nodes.Element) -> None + def depart_desc_name(self, node: Element) -> None: self.body.append('}}') self.literal_whitespace -= 1 self.no_contractions -= 1 - def visit_desc_parameterlist(self, node): - # type: (nodes.Element) -> None + def visit_desc_parameterlist(self, node: Element) -> None: # close name, open parameterlist self.body.append('}{') self.first_param = 1 - def depart_desc_parameterlist(self, node): - # type: (nodes.Element) -> None + def depart_desc_parameterlist(self, node: Element) -> None: # close parameterlist, open return annotation self.body.append('}{') - def visit_desc_parameter(self, node): - # type: (nodes.Element) -> None + def visit_desc_parameter(self, node: Element) -> None: if not self.first_param: self.body.append(', ') else: @@ -1042,60 +982,48 @@ class LaTeXTranslator(SphinxTranslator): if not node.hasattr('noemph'): self.body.append(r'\emph{') - def depart_desc_parameter(self, node): - # type: (nodes.Element) -> None + def depart_desc_parameter(self, node: Element) -> None: if not node.hasattr('noemph'): self.body.append('}') - def visit_desc_optional(self, node): - # type: (nodes.Element) -> None + def visit_desc_optional(self, node: Element) -> None: self.body.append(r'\sphinxoptional{') - def depart_desc_optional(self, node): - # type: (nodes.Element) -> None + def depart_desc_optional(self, node: Element) -> None: self.body.append('}') - def visit_desc_annotation(self, node): - # type: (nodes.Element) -> None + def visit_desc_annotation(self, node: Element) -> None: self.body.append(r'\sphinxbfcode{\sphinxupquote{') - def depart_desc_annotation(self, node): - # type: (nodes.Element) -> None + def depart_desc_annotation(self, node: Element) -> None: self.body.append('}}') - def visit_desc_content(self, node): - # type: (nodes.Element) -> None + def visit_desc_content(self, node: Element) -> None: if node.children and not isinstance(node.children[0], nodes.paragraph): # avoid empty desc environment which causes a formatting bug self.body.append('~') - def depart_desc_content(self, node): - # type: (nodes.Element) -> None + def depart_desc_content(self, node: Element) -> None: pass - def visit_seealso(self, node): - # type: (nodes.Element) -> None + def visit_seealso(self, node: Element) -> None: self.body.append('\n\n\\sphinxstrong{%s:}\n\n' % admonitionlabels['seealso']) - def depart_seealso(self, node): - # type: (nodes.Element) -> None + def depart_seealso(self, node: Element) -> None: self.body.append("\n\n") - def visit_rubric(self, node): - # type: (nodes.Element) -> None + def visit_rubric(self, node: Element) -> None: if len(node) == 1 and node.astext() in ('Footnotes', _('Footnotes')): raise nodes.SkipNode self.body.append('\\subsubsection*{') self.context.append('}\n') self.in_title = 1 - def depart_rubric(self, node): - # type: (nodes.Element) -> None + def depart_rubric(self, node: Element) -> None: self.in_title = 0 self.body.append(self.context.pop()) - def visit_footnote(self, node): - # type: (nodes.Element) -> None + def visit_footnote(self, node: Element) -> None: self.in_footnote += 1 label = cast(nodes.label, node[0]) if self.in_parsed_literal: @@ -1104,25 +1032,21 @@ class LaTeXTranslator(SphinxTranslator): self.body.append('%%\n\\begin{footnote}[%s]' % label.astext()) self.body.append('\\sphinxAtStartFootnote\n') - def depart_footnote(self, node): - # type: (nodes.Element) -> None + def depart_footnote(self, node: Element) -> None: if self.in_parsed_literal: self.body.append('\\end{footnote}') else: self.body.append('%\n\\end{footnote}') self.in_footnote -= 1 - def visit_label(self, node): - # type: (nodes.Element) -> None + def visit_label(self, node: Element) -> None: raise nodes.SkipNode - def visit_tabular_col_spec(self, node): - # type: (nodes.Element) -> None + def visit_tabular_col_spec(self, node: Element) -> None: self.next_table_colspec = node['spec'] raise nodes.SkipNode - def visit_table(self, node): - # type: (nodes.Element) -> None + def visit_table(self, node: Element) -> None: if self.table: raise UnsupportedError( '%s:%s: nested tables are not yet implemented.' % @@ -1135,8 +1059,7 @@ class LaTeXTranslator(SphinxTranslator): ':widths: is ignored.'), location=node) self.next_table_colspec = None - def depart_table(self, node): - # type: (nodes.Element) -> None + def depart_table(self, node: Element) -> None: labels = self.hypertarget_to(node) table_type = self.table.get_table_type() table = self.render(table_type + '.tex_t', @@ -1147,46 +1070,37 @@ class LaTeXTranslator(SphinxTranslator): self.table = None - def visit_colspec(self, node): - # type: (nodes.Element) -> None + def visit_colspec(self, node: Element) -> None: self.table.colcount += 1 if 'colwidth' in node: self.table.colwidths.append(node['colwidth']) if 'stub' in node: self.table.stubs.append(self.table.colcount - 1) - def depart_colspec(self, node): - # type: (nodes.Element) -> None + def depart_colspec(self, node: Element) -> None: pass - def visit_tgroup(self, node): - # type: (nodes.Element) -> None + def visit_tgroup(self, node: Element) -> None: pass - def depart_tgroup(self, node): - # type: (nodes.Element) -> None + def depart_tgroup(self, node: Element) -> None: pass - def visit_thead(self, node): - # type: (nodes.Element) -> None + def visit_thead(self, node: Element) -> None: # Redirect head output until header is finished. self.pushbody(self.table.header) - def depart_thead(self, node): - # type: (nodes.Element) -> None + def depart_thead(self, node: Element) -> None: self.popbody() - def visit_tbody(self, node): - # type: (nodes.Element) -> None + def visit_tbody(self, node: Element) -> None: # Redirect body output until table is finished. self.pushbody(self.table.body) - def depart_tbody(self, node): - # type: (nodes.Element) -> None + def depart_tbody(self, node: Element) -> None: self.popbody() - def visit_row(self, node): - # type: (nodes.Element) -> None + def visit_row(self, node: Element) -> None: self.table.col = 0 # fill columns if the row starts with the bottom of multirow cell @@ -1206,8 +1120,7 @@ class LaTeXTranslator(SphinxTranslator): '{\\sphinxtablestrut{%d}}' % (cell.width, cell.cell_id)) - def depart_row(self, node): - # type: (nodes.Element) -> None + def depart_row(self, node: Element) -> None: self.body.append('\\\\\n') cells = [self.table.cell(self.table.row, i) for i in range(self.table.colcount)] underlined = [cell.row + cell.height == self.table.row + 1 for cell in cells] @@ -1224,8 +1137,7 @@ class LaTeXTranslator(SphinxTranslator): i += 1 self.table.row += 1 - def visit_entry(self, node): - # type: (nodes.Element) -> None + def visit_entry(self, node: Element) -> None: if self.table.col > 0: self.body.append('&') self.table.add_cell(node.get('morerows', 0) + 1, node.get('morecols', 0) + 1) @@ -1262,8 +1174,7 @@ class LaTeXTranslator(SphinxTranslator): self.pushbody([]) self.context.append(context) - def depart_entry(self, node): - # type: (nodes.Element) -> None + def depart_entry(self, node: Element) -> None: if self.needs_linetrimming: self.needs_linetrimming = 0 body = self.popbody() @@ -1296,8 +1207,7 @@ class LaTeXTranslator(SphinxTranslator): '{\\sphinxtablestrut{%d}}' % (nextcell.width, nextcell.cell_id)) - def visit_acks(self, node): - # type: (nodes.Element) -> None + def visit_acks(self, node: Element) -> None: # this is a list in the source, but should be rendered as a # comma-separated list here bullet_list = cast(nodes.bullet_list, node[0]) @@ -1307,22 +1217,18 @@ class LaTeXTranslator(SphinxTranslator): self.body.append('\n\n') raise nodes.SkipNode - def visit_bullet_list(self, node): - # type: (nodes.Element) -> None + def visit_bullet_list(self, node: Element) -> None: if not self.compact_list: self.body.append('\\begin{itemize}\n') if self.table: self.table.has_problematic = True - def depart_bullet_list(self, node): - # type: (nodes.Element) -> None + def depart_bullet_list(self, node: Element) -> None: if not self.compact_list: self.body.append('\\end{itemize}\n') - def visit_enumerated_list(self, node): - # type: (nodes.Element) -> None - def get_enumtype(node): - # type: (nodes.Element) -> str + def visit_enumerated_list(self, node: Element) -> None: + def get_enumtype(node: Element) -> str: enumtype = node.get('enumtype', 'arabic') if 'alpha' in enumtype and 26 < node.get('start', 0) + len(node): # fallback to arabic if alphabet counter overflows @@ -1330,8 +1236,7 @@ class LaTeXTranslator(SphinxTranslator): return enumtype - def get_nested_level(node): - # type: (nodes.Element) -> int + def get_nested_level(node: Element) -> int: if node is None: return 0 elif isinstance(node, nodes.enumerated_list): @@ -1353,40 +1258,32 @@ class LaTeXTranslator(SphinxTranslator): if self.table: self.table.has_problematic = True - def depart_enumerated_list(self, node): - # type: (nodes.Element) -> None + def depart_enumerated_list(self, node: Element) -> None: self.body.append('\\end{enumerate}\n') - def visit_list_item(self, node): - # type: (nodes.Element) -> None + def visit_list_item(self, node: Element) -> None: # Append "{}" in case the next character is "[", which would break # LaTeX's list environment (no numbering and the "[" is not printed). self.body.append(r'\item {} ') - def depart_list_item(self, node): - # type: (nodes.Element) -> None + def depart_list_item(self, node: Element) -> None: self.body.append('\n') - def visit_definition_list(self, node): - # type: (nodes.Element) -> None + def visit_definition_list(self, node: Element) -> None: self.body.append('\\begin{description}\n') if self.table: self.table.has_problematic = True - def depart_definition_list(self, node): - # type: (nodes.Element) -> None + def depart_definition_list(self, node: Element) -> None: self.body.append('\\end{description}\n') - def visit_definition_list_item(self, node): - # type: (nodes.Element) -> None + def visit_definition_list_item(self, node: Element) -> None: pass - def depart_definition_list_item(self, node): - # type: (nodes.Element) -> None + def depart_definition_list_item(self, node: Element) -> None: pass - def visit_term(self, node): - # type: (nodes.Element) -> None + def visit_term(self, node: Element) -> None: self.in_term += 1 ctx = '' if node.get('ids'): @@ -1397,43 +1294,34 @@ class LaTeXTranslator(SphinxTranslator): self.body.append('\\item[{') self.context.append(ctx) - def depart_term(self, node): - # type: (nodes.Element) -> None + def depart_term(self, node: Element) -> None: self.body.append(self.context.pop()) self.in_term -= 1 - def visit_classifier(self, node): - # type: (nodes.Element) -> None + def visit_classifier(self, node: Element) -> None: self.body.append('{[}') - def depart_classifier(self, node): - # type: (nodes.Element) -> None + def depart_classifier(self, node: Element) -> None: self.body.append('{]}') - def visit_definition(self, node): - # type: (nodes.Element) -> None + def visit_definition(self, node: Element) -> None: pass - def depart_definition(self, node): - # type: (nodes.Element) -> None + def depart_definition(self, node: Element) -> None: self.body.append('\n') - def visit_field_list(self, node): - # type: (nodes.Element) -> None + def visit_field_list(self, node: Element) -> None: self.body.append('\\begin{quote}\\begin{description}\n') if self.table: self.table.has_problematic = True - def depart_field_list(self, node): - # type: (nodes.Element) -> None + def depart_field_list(self, node: Element) -> None: self.body.append('\\end{description}\\end{quote}\n') - def visit_field(self, node): - # type: (nodes.Element) -> None + def visit_field(self, node: Element) -> None: pass - def depart_field(self, node): - # type: (nodes.Element) -> None + def depart_field(self, node: Element) -> None: pass visit_field_name = visit_term @@ -1442,8 +1330,7 @@ class LaTeXTranslator(SphinxTranslator): visit_field_body = visit_definition depart_field_body = depart_definition - def visit_paragraph(self, node): - # type: (nodes.Element) -> None + def visit_paragraph(self, node: Element) -> None: index = node.parent.index(node) if (index > 0 and isinstance(node.parent, nodes.compound) and not isinstance(node.parent[index - 1], nodes.paragraph) and @@ -1457,22 +1344,18 @@ class LaTeXTranslator(SphinxTranslator): else: self.body.append('\n') - def depart_paragraph(self, node): - # type: (nodes.Element) -> None + def depart_paragraph(self, node: Element) -> None: self.body.append('\n') - def visit_centered(self, node): - # type: (nodes.Element) -> None + def visit_centered(self, node: Element) -> None: self.body.append('\n\\begin{center}') if self.table: self.table.has_problematic = True - def depart_centered(self, node): - # type: (nodes.Element) -> None + def depart_centered(self, node: Element) -> None: self.body.append('\n\\end{center}') - def visit_hlist(self, node): - # type: (nodes.Element) -> None + def visit_hlist(self, node: Element) -> None: # for now, we don't support a more compact list format # don't add individual itemize environments, but one for all columns self.compact_list += 1 @@ -1481,34 +1364,28 @@ class LaTeXTranslator(SphinxTranslator): if self.table: self.table.has_problematic = True - def depart_hlist(self, node): - # type: (nodes.Element) -> None + def depart_hlist(self, node: Element) -> None: self.compact_list -= 1 self.body.append('\\end{itemize}\n') - def visit_hlistcol(self, node): - # type: (nodes.Element) -> None + def visit_hlistcol(self, node: Element) -> None: pass - def depart_hlistcol(self, node): - # type: (nodes.Element) -> None + def depart_hlistcol(self, node: Element) -> None: pass - def latex_image_length(self, width_str, scale = 100): - # type: (str, int) -> str + def latex_image_length(self, width_str: str, scale: int = 100) -> str: try: return rstdim_to_latexdim(width_str, scale) except ValueError: logger.warning(__('dimension unit %s is invalid. Ignored.'), width_str) return None - def is_inline(self, node): - # type: (nodes.Element) -> bool + def is_inline(self, node: Element) -> bool: """Check whether a node represents an inline element.""" return isinstance(node.parent, nodes.TextElement) - def visit_image(self, node): - # type: (nodes.Element) -> None + def visit_image(self, node: Element) -> None: attrs = node.attributes pre = [] # type: List[str] # in reverse order @@ -1590,12 +1467,10 @@ class LaTeXTranslator(SphinxTranslator): (options, base, ext)) self.body.extend(post) - def depart_image(self, node): - # type: (nodes.Element) -> None + def depart_image(self, node: Element) -> None: pass - def visit_figure(self, node): - # type: (nodes.Element) -> None + def visit_figure(self, node: Element) -> None: align = self.elements['figure_align'] if self.no_latex_floats: align = "H" @@ -1629,12 +1504,10 @@ class LaTeXTranslator(SphinxTranslator): self.body.append('\\capstart\n') self.context.append('\\end{figure}\n') - def depart_figure(self, node): - # type: (nodes.Element) -> None + def depart_figure(self, node: Element) -> None: self.body.append(self.context.pop()) - def visit_caption(self, node): - # type: (nodes.Element) -> None + def visit_caption(self, node: Element) -> None: self.in_caption += 1 if isinstance(node.parent, captioned_literal_block): self.body.append('\\sphinxSetupCaptionForVerbatim{') @@ -1645,41 +1518,34 @@ class LaTeXTranslator(SphinxTranslator): else: self.body.append('\\caption{') - def depart_caption(self, node): - # type: (nodes.Element) -> None + def depart_caption(self, node: Element) -> None: self.body.append('}') if isinstance(node.parent, nodes.figure): labels = self.hypertarget_to(node.parent) self.body.append(labels) self.in_caption -= 1 - def visit_legend(self, node): - # type: (nodes.Element) -> None + def visit_legend(self, node: Element) -> None: self.body.append('\n\\begin{sphinxlegend}') - def depart_legend(self, node): - # type: (nodes.Element) -> None + def depart_legend(self, node: Element) -> None: self.body.append('\\end{sphinxlegend}\n') - def visit_admonition(self, node): - # type: (nodes.Element) -> None + def visit_admonition(self, node: Element) -> None: self.body.append('\n\\begin{sphinxadmonition}{note}') self.no_latex_floats += 1 - def depart_admonition(self, node): - # type: (nodes.Element) -> None + def depart_admonition(self, node: Element) -> None: self.body.append('\\end{sphinxadmonition}\n') self.no_latex_floats -= 1 - def _visit_named_admonition(self, node): - # type: (nodes.Element) -> None + def _visit_named_admonition(self, node: Element) -> None: label = admonitionlabels[node.tagname] self.body.append('\n\\begin{sphinxadmonition}{%s}{%s:}' % (node.tagname, label)) self.no_latex_floats += 1 - def _depart_named_admonition(self, node): - # type: (nodes.Element) -> None + def _depart_named_admonition(self, node: Element) -> None: self.body.append('\\end{sphinxadmonition}\n') self.no_latex_floats -= 1 @@ -1702,18 +1568,14 @@ class LaTeXTranslator(SphinxTranslator): visit_warning = _visit_named_admonition depart_warning = _depart_named_admonition - def visit_versionmodified(self, node): - # type: (nodes.Element) -> None + def visit_versionmodified(self, node: Element) -> None: pass - def depart_versionmodified(self, node): - # type: (nodes.Element) -> None + def depart_versionmodified(self, node: Element) -> None: pass - def visit_target(self, node): - # type: (nodes.Element) -> None - def add_target(id): - # type: (str) -> None + def visit_target(self, node: Element) -> None: + def add_target(id: str) -> None: # indexing uses standard LaTeX index markup, so the targets # will be generated differently if id.startswith('index-'): @@ -1756,21 +1618,17 @@ class LaTeXTranslator(SphinxTranslator): for id in node['ids']: add_target(id) - def depart_target(self, node): - # type: (nodes.Element) -> None + def depart_target(self, node: Element) -> None: pass - def visit_attribution(self, node): - # type: (nodes.Element) -> None + def visit_attribution(self, node: Element) -> None: self.body.append('\n\\begin{flushright}\n') self.body.append('---') - def depart_attribution(self, node): - # type: (nodes.Element) -> None + def depart_attribution(self, node: Element) -> None: self.body.append('\n\\end{flushright}\n') - def visit_index(self, node): - # type: (nodes.Element) -> None + def visit_index(self, node: Element) -> None: def escape(value): value = self.encode(value) value = value.replace(r'\{', r'\sphinxleftcurlybrace{}') @@ -1778,6 +1636,7 @@ class LaTeXTranslator(SphinxTranslator): value = value.replace('"', '""') value = value.replace('@', '"@') value = value.replace('!', '"!') + value = value.replace('|', r'\textbar{}') return value def style(string): @@ -1835,8 +1694,7 @@ class LaTeXTranslator(SphinxTranslator): self.body.append('\\ignorespaces ') raise nodes.SkipNode - def visit_raw(self, node): - # type: (nodes.Element) -> None + def visit_raw(self, node: Element) -> None: if not self.is_inline(node): self.body.append('\n') if 'latex' in node.get('format', '').split(): @@ -1845,8 +1703,7 @@ class LaTeXTranslator(SphinxTranslator): self.body.append('\n') raise nodes.SkipNode - def visit_reference(self, node): - # type: (nodes.Element) -> None + def visit_reference(self, node: Element) -> None: if not self.in_title: for id in node.get('ids'): anchor = not self.in_caption @@ -1903,14 +1760,12 @@ class LaTeXTranslator(SphinxTranslator): self.body.append('\\sphinxhref{%s}{' % self.encode_uri(uri)) self.context.append('}') - def depart_reference(self, node): - # type: (nodes.Element) -> None + def depart_reference(self, node: Element) -> None: self.body.append(self.context.pop()) if not self.is_inline(node): self.body.append('\n') - def visit_number_reference(self, node): - # type: (nodes.Element) -> None + def visit_number_reference(self, node: Element) -> None: if node.get('refid'): id = self.curfilestack[-1] + ':' + node['refid'] else: @@ -1930,60 +1785,47 @@ class LaTeXTranslator(SphinxTranslator): raise nodes.SkipNode - def visit_download_reference(self, node): - # type: (nodes.Element) -> None + def visit_download_reference(self, node: Element) -> None: pass - def depart_download_reference(self, node): - # type: (nodes.Element) -> None + def depart_download_reference(self, node: Element) -> None: pass - def visit_pending_xref(self, node): - # type: (nodes.Element) -> None + def visit_pending_xref(self, node: Element) -> None: pass - def depart_pending_xref(self, node): - # type: (nodes.Element) -> None + def depart_pending_xref(self, node: Element) -> None: pass - def visit_emphasis(self, node): - # type: (nodes.Element) -> None + def visit_emphasis(self, node: Element) -> None: self.body.append(r'\sphinxstyleemphasis{') - def depart_emphasis(self, node): - # type: (nodes.Element) -> None + def depart_emphasis(self, node: Element) -> None: self.body.append('}') - def visit_literal_emphasis(self, node): - # type: (nodes.Element) -> None + def visit_literal_emphasis(self, node: Element) -> None: self.body.append(r'\sphinxstyleliteralemphasis{\sphinxupquote{') self.no_contractions += 1 - def depart_literal_emphasis(self, node): - # type: (nodes.Element) -> None + def depart_literal_emphasis(self, node: Element) -> None: self.body.append('}}') self.no_contractions -= 1 - def visit_strong(self, node): - # type: (nodes.Element) -> None + def visit_strong(self, node: Element) -> None: self.body.append(r'\sphinxstylestrong{') - def depart_strong(self, node): - # type: (nodes.Element) -> None + def depart_strong(self, node: Element) -> None: self.body.append('}') - def visit_literal_strong(self, node): - # type: (nodes.Element) -> None + def visit_literal_strong(self, node: Element) -> None: self.body.append(r'\sphinxstyleliteralstrong{\sphinxupquote{') self.no_contractions += 1 - def depart_literal_strong(self, node): - # type: (nodes.Element) -> None + def depart_literal_strong(self, node: Element) -> None: self.body.append('}}') self.no_contractions -= 1 - def visit_abbreviation(self, node): - # type: (nodes.Element) -> None + def visit_abbreviation(self, node: Element) -> None: abbr = node.astext() self.body.append(r'\sphinxstyleabbreviation{') # spell out the explanation once @@ -1993,28 +1835,22 @@ class LaTeXTranslator(SphinxTranslator): else: self.context.append('}') - def depart_abbreviation(self, node): - # type: (nodes.Element) -> None + def depart_abbreviation(self, node: Element) -> None: self.body.append(self.context.pop()) - def visit_manpage(self, node): - # type: (nodes.Element) -> None + def visit_manpage(self, node: Element) -> None: return self.visit_literal_emphasis(node) - def depart_manpage(self, node): - # type: (nodes.Element) -> None + def depart_manpage(self, node: Element) -> None: return self.depart_literal_emphasis(node) - def visit_title_reference(self, node): - # type: (nodes.Element) -> None + def visit_title_reference(self, node: Element) -> None: self.body.append(r'\sphinxtitleref{') - def depart_title_reference(self, node): - # type: (nodes.Element) -> None + def depart_title_reference(self, node: Element) -> None: self.body.append('}') - def visit_thebibliography(self, node): - # type: (thebibliography) -> None + def visit_thebibliography(self, node: Element) -> None: citations = cast(Iterable[nodes.citation], node) labels = (cast(nodes.label, citation[0]) for citation in citations) longest_label = max((label.astext() for label in labels), key=len) @@ -2025,78 +1861,63 @@ class LaTeXTranslator(SphinxTranslator): self.body.append('\n\\begin{sphinxthebibliography}{%s}\n' % self.encode(longest_label)) - def depart_thebibliography(self, node): - # type: (thebibliography) -> None + def depart_thebibliography(self, node: Element) -> None: self.body.append('\\end{sphinxthebibliography}\n') - def visit_citation(self, node): - # type: (nodes.Element) -> None + def visit_citation(self, node: Element) -> None: label = cast(nodes.label, node[0]) self.body.append('\\bibitem[%s]{%s:%s}' % (self.encode(label.astext()), node['docname'], node['ids'][0])) - def depart_citation(self, node): - # type: (nodes.Element) -> None + def depart_citation(self, node: Element) -> None: pass - def visit_citation_reference(self, node): - # type: (nodes.Element) -> None + def visit_citation_reference(self, node: Element) -> None: if self.in_title: pass else: self.body.append('\\sphinxcite{%s:%s}' % (node['docname'], node['refname'])) raise nodes.SkipNode - def depart_citation_reference(self, node): - # type: (nodes.Element) -> None + def depart_citation_reference(self, node: Element) -> None: pass - def visit_literal(self, node): - # type: (nodes.Element) -> None + def visit_literal(self, node: Element) -> None: self.no_contractions += 1 if self.in_title: self.body.append(r'\sphinxstyleliteralintitle{\sphinxupquote{') else: self.body.append(r'\sphinxcode{\sphinxupquote{') - def depart_literal(self, node): - # type: (nodes.Element) -> None + def depart_literal(self, node: Element) -> None: self.no_contractions -= 1 self.body.append('}}') - def visit_footnote_reference(self, node): - # type: (nodes.Element) -> None + def visit_footnote_reference(self, node: Element) -> None: raise nodes.SkipNode - def visit_footnotemark(self, node): - # type: (footnotemark) -> None + def visit_footnotemark(self, node: Element) -> None: self.body.append('\\sphinxfootnotemark[') - def depart_footnotemark(self, node): - # type: (footnotemark) -> None + def depart_footnotemark(self, node: Element) -> None: self.body.append(']') - def visit_footnotetext(self, node): - # type: (footnotetext) -> None + def visit_footnotetext(self, node: Element) -> None: label = cast(nodes.label, node[0]) self.body.append('%%\n\\begin{footnotetext}[%s]' '\\sphinxAtStartFootnote\n' % label.astext()) - def depart_footnotetext(self, node): - # type: (footnotetext) -> None + def depart_footnotetext(self, node: Element) -> None: # the \ignorespaces in particular for after table header use self.body.append('%\n\\end{footnotetext}\\ignorespaces ') - def visit_captioned_literal_block(self, node): - # type: (captioned_literal_block) -> None + def visit_captioned_literal_block(self, node: Element) -> None: pass - def depart_captioned_literal_block(self, node): - # type: (captioned_literal_block) -> None + def depart_captioned_literal_block(self, node: Element) -> None: pass - def visit_literal_block(self, node): - # type: (nodes.Element) -> None + def visit_literal_block(self, node: Element) -> None: if node.rawsource != node.astext(): # most probably a parsed-literal block -- don't highlight self.in_parsed_literal += 1 @@ -2122,8 +1943,6 @@ class LaTeXTranslator(SphinxTranslator): node.rawsource, lang, opts=opts, linenos=linenos, location=(self.curfilestack[-1], node.line), **highlight_args ) - # workaround for Unicode issue - hlcode = hlcode.replace('€', '@texteuro[]') if self.in_footnote: self.body.append('\n\\sphinxSetupCodeBlockInFootnote') hlcode = hlcode.replace('\\begin{Verbatim}', @@ -2153,23 +1972,19 @@ class LaTeXTranslator(SphinxTranslator): self.body.append('\\sphinxresetverbatimhllines\n') raise nodes.SkipNode - def depart_literal_block(self, node): - # type: (nodes.Element) -> None + def depart_literal_block(self, node: Element) -> None: self.body.append('\n\\end{sphinxalltt}\n') self.in_parsed_literal -= 1 visit_doctest_block = visit_literal_block depart_doctest_block = depart_literal_block - def visit_line(self, node): - # type: (nodes.Element) -> None + def visit_line(self, node: Element) -> None: self.body.append('\\item[] ') - def depart_line(self, node): - # type: (nodes.Element) -> None + def depart_line(self, node: Element) -> None: self.body.append('\n') - def visit_line_block(self, node): - # type: (nodes.Element) -> None + def visit_line_block(self, node: Element) -> None: if isinstance(node.parent, nodes.line_block): self.body.append('\\item[]\n' '\\begin{DUlineblock}{\\DUlineblockindent}\n') @@ -2178,12 +1993,10 @@ class LaTeXTranslator(SphinxTranslator): if self.table: self.table.has_problematic = True - def depart_line_block(self, node): - # type: (nodes.Element) -> None + def depart_line_block(self, node: Element) -> None: self.body.append('\\end{DUlineblock}\n') - def visit_block_quote(self, node): - # type: (nodes.Element) -> None + def visit_block_quote(self, node: Element) -> None: # If the block quote contains a single object and that object # is a list, then generate a list not a block quote. # This lets us indent lists. @@ -2198,8 +2011,7 @@ class LaTeXTranslator(SphinxTranslator): if self.table: self.table.has_problematic = True - def depart_block_quote(self, node): - # type: (nodes.Element) -> None + def depart_block_quote(self, node: Element) -> None: done = 0 if len(node.children) == 1: child = node.children[0] @@ -2211,89 +2023,71 @@ class LaTeXTranslator(SphinxTranslator): # option node handling copied from docutils' latex writer - def visit_option(self, node): - # type: (nodes.Element) -> None + def visit_option(self, node: Element) -> None: if self.context[-1]: # this is not the first option self.body.append(', ') - def depart_option(self, node): - # type: (nodes.Element) -> None + def depart_option(self, node: Element) -> None: # flag that the first option is done. self.context[-1] += 1 - def visit_option_argument(self, node): - # type: (nodes.Element) -> None + def visit_option_argument(self, node: Element) -> None: """The delimiter betweeen an option and its argument.""" self.body.append(node.get('delimiter', ' ')) - def depart_option_argument(self, node): - # type: (nodes.Element) -> None + def depart_option_argument(self, node: Element) -> None: pass - def visit_option_group(self, node): - # type: (nodes.Element) -> None + def visit_option_group(self, node: Element) -> None: self.body.append('\\item [') # flag for first option self.context.append(0) - def depart_option_group(self, node): - # type: (nodes.Element) -> None + def depart_option_group(self, node: Element) -> None: self.context.pop() # the flag self.body.append('] ') - def visit_option_list(self, node): - # type: (nodes.Element) -> None + def visit_option_list(self, node: Element) -> None: self.body.append('\\begin{optionlist}{3cm}\n') if self.table: self.table.has_problematic = True - def depart_option_list(self, node): - # type: (nodes.Element) -> None + def depart_option_list(self, node: Element) -> None: self.body.append('\\end{optionlist}\n') - def visit_option_list_item(self, node): - # type: (nodes.Element) -> None + def visit_option_list_item(self, node: Element) -> None: pass - def depart_option_list_item(self, node): - # type: (nodes.Element) -> None + def depart_option_list_item(self, node: Element) -> None: pass - def visit_option_string(self, node): - # type: (nodes.Element) -> None + def visit_option_string(self, node: Element) -> None: ostring = node.astext() self.no_contractions += 1 self.body.append(self.encode(ostring)) self.no_contractions -= 1 raise nodes.SkipNode - def visit_description(self, node): - # type: (nodes.Element) -> None + def visit_description(self, node: Element) -> None: self.body.append(' ') - def depart_description(self, node): - # type: (nodes.Element) -> None + def depart_description(self, node: Element) -> None: pass - def visit_superscript(self, node): - # type: (nodes.Element) -> None + def visit_superscript(self, node: Element) -> None: self.body.append('$^{\\text{') - def depart_superscript(self, node): - # type: (nodes.Element) -> None + def depart_superscript(self, node: Element) -> None: self.body.append('}}$') - def visit_subscript(self, node): - # type: (nodes.Element) -> None + def visit_subscript(self, node: Element) -> None: self.body.append('$_{\\text{') - def depart_subscript(self, node): - # type: (nodes.Element) -> None + def depart_subscript(self, node: Element) -> None: self.body.append('}}$') - def visit_inline(self, node): - # type: (nodes.Element) -> None + def visit_inline(self, node: Element) -> None: classes = node.get('classes', []) if classes in [['menuselection']]: self.body.append(r'\sphinxmenuselection{') @@ -2310,60 +2104,47 @@ class LaTeXTranslator(SphinxTranslator): else: self.context.append('') - def depart_inline(self, node): - # type: (nodes.Element) -> None + def depart_inline(self, node: Element) -> None: self.body.append(self.context.pop()) - def visit_generated(self, node): - # type: (nodes.Element) -> None + def visit_generated(self, node: Element) -> None: pass - def depart_generated(self, node): - # type: (nodes.Element) -> None + def depart_generated(self, node: Element) -> None: pass - def visit_compound(self, node): - # type: (nodes.Element) -> None + def visit_compound(self, node: Element) -> None: pass - def depart_compound(self, node): - # type: (nodes.Element) -> None + def depart_compound(self, node: Element) -> None: pass - def visit_container(self, node): - # type: (nodes.Element) -> None + def visit_container(self, node: Element) -> None: pass - def depart_container(self, node): - # type: (nodes.Element) -> None + def depart_container(self, node: Element) -> None: pass - def visit_decoration(self, node): - # type: (nodes.Element) -> None + def visit_decoration(self, node: Element) -> None: pass - def depart_decoration(self, node): - # type: (nodes.Element) -> None + def depart_decoration(self, node: Element) -> None: pass # docutils-generated elements that we don't support - def visit_header(self, node): - # type: (nodes.Element) -> None + def visit_header(self, node: Element) -> None: raise nodes.SkipNode - def visit_footer(self, node): - # type: (nodes.Element) -> None + def visit_footer(self, node: Element) -> None: raise nodes.SkipNode - def visit_docinfo(self, node): - # type: (nodes.Element) -> None + def visit_docinfo(self, node: Element) -> None: raise nodes.SkipNode # text handling - def encode(self, text): - # type: (str) -> str + def encode(self, text: str) -> str: text = self.escape(text) if self.literal_whitespace: # Insert a blank before the newline, to avoid @@ -2374,47 +2155,38 @@ class LaTeXTranslator(SphinxTranslator): text = text.replace("''", "'{'}") return text - def encode_uri(self, text): - # type: (str) -> str + def encode_uri(self, text: str) -> str: # in \href, the tilde is allowed and must be represented literally return self.encode(text).replace('\\textasciitilde{}', '~') - def visit_Text(self, node): - # type: (nodes.Text) -> None + def visit_Text(self, node: Text) -> None: text = self.encode(node.astext()) self.body.append(text) - def depart_Text(self, node): - # type: (nodes.Text) -> None + def depart_Text(self, node: Text) -> None: pass - def visit_comment(self, node): - # type: (nodes.Element) -> None + def visit_comment(self, node: Element) -> None: raise nodes.SkipNode - def visit_meta(self, node): - # type: (nodes.Element) -> None + def visit_meta(self, node: Element) -> None: # only valid for HTML raise nodes.SkipNode - def visit_system_message(self, node): - # type: (nodes.Element) -> None + def visit_system_message(self, node: Element) -> None: pass - def depart_system_message(self, node): - # type: (nodes.Element) -> None + def depart_system_message(self, node: Element) -> None: self.body.append('\n') - def visit_math(self, node): - # type: (nodes.Element) -> None + def visit_math(self, node: Element) -> None: if self.in_title: self.body.append(r'\protect\(%s\protect\)' % node.astext()) else: self.body.append(r'\(%s\)' % node.astext()) raise nodes.SkipNode - def visit_math_block(self, node): - # type: (nodes.Element) -> None + def visit_math_block(self, node: Element) -> None: if node.get('label'): label = "equation:%s:%s" % (node['docname'], node['label']) else: @@ -2430,8 +2202,7 @@ class LaTeXTranslator(SphinxTranslator): self.builder.config.math_number_all)) raise nodes.SkipNode - def visit_math_reference(self, node): - # type: (math_reference) -> None + def visit_math_reference(self, node: Element) -> None: label = "equation:%s:%s" % (node['docname'], node['target']) eqref_format = self.builder.config.math_eqref_format if eqref_format: @@ -2445,20 +2216,16 @@ class LaTeXTranslator(SphinxTranslator): else: self.body.append(r'\eqref{%s}' % label) - def depart_math_reference(self, node): - # type: (math_reference) -> None + def depart_math_reference(self, node: Element) -> None: pass - def unknown_visit(self, node): - # type: (nodes.Node) -> None + def unknown_visit(self, node: Node) -> None: raise NotImplementedError('Unknown node: ' + node.__class__.__name__) # --------- METHODS FOR COMPATIBILITY -------------------------------------- - def collect_footnotes(self, node): - # type: (nodes.Element) -> Dict[str, List[Union[collected_footnote, bool]]] - def footnotes_under(n): - # type: (nodes.Element) -> Iterator[nodes.footnote] + def collect_footnotes(self, node: Element) -> Dict[str, List[Union["collected_footnote", bool]]]: # NOQA + def footnotes_under(n: Element) -> Iterator[nodes.footnote]: if isinstance(n, nodes.footnote): yield n else: @@ -2479,8 +2246,7 @@ class LaTeXTranslator(SphinxTranslator): fnotes[num] = [newnode, False] return fnotes - def babel_defmacro(self, name, definition): - # type: (str, str) -> str + def babel_defmacro(self, name: str, definition: str) -> str: warnings.warn('babel_defmacro() is deprecated.', RemovedInSphinx40Warning) @@ -2493,8 +2259,7 @@ class LaTeXTranslator(SphinxTranslator): return ('%s\\def%s{%s}%s\n' % (prefix, name, definition, suffix)) - def generate_numfig_format(self, builder): - # type: (LaTeXBuilder) -> str + def generate_numfig_format(self, builder: "LaTeXBuilder") -> str: warnings.warn('generate_numfig_format() is deprecated.', RemovedInSphinx40Warning) ret = [] # type: List[str] diff --git a/sphinx/writers/manpage.py b/sphinx/writers/manpage.py index 7811ccc5b..c9ae41609 100644 --- a/sphinx/writers/manpage.py +++ b/sphinx/writers/manpage.py @@ -9,9 +9,11 @@ """ import warnings -from typing import Iterable, cast +from typing import Any, Dict, Iterable +from typing import cast from docutils import nodes +from docutils.nodes import Element, Node, TextElement from docutils.writers.manpage import ( Writer, Translator as BaseTranslator @@ -26,21 +28,16 @@ from sphinx.util.docutils import SphinxTranslator from sphinx.util.i18n import format_date from sphinx.util.nodes import NodeMatcher -if False: - # For type annotation - from typing import Any, Dict # NOQA logger = logging.getLogger(__name__) class ManualPageWriter(Writer): - def __init__(self, builder): - # type: (Builder) -> None + def __init__(self, builder: Builder) -> None: super().__init__() self.builder = builder - def translate(self): - # type: () -> None + def translate(self) -> None: transform = NestedInlineTransform(self.document) transform.apply() visitor = self.builder.create_translator(self.document, self.builder) @@ -60,14 +57,12 @@ class NestedInlineTransform: foo=var &bar=2 """ - def __init__(self, document): - # type: (nodes.document) -> None + def __init__(self, document: nodes.document) -> None: self.document = document - def apply(self, **kwargs): - # type: (Any) -> None + def apply(self, **kwargs) -> None: 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): pos = node.parent.index(node) for subnode in reversed(node[1:]): @@ -86,8 +81,7 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator): _docinfo = {} # type: Dict[str, Any] - def __init__(self, *args): - # type: (Any) -> None + def __init__(self, *args) -> None: if isinstance(args[0], nodes.document) and isinstance(args[1], Builder): document, builder = args else: @@ -126,153 +120,120 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator): self.language.labels[label] = self.deunicode(translation) # type: ignore # overwritten -- added quotes around all .TH arguments - def header(self): - # type: () -> str + def header(self) -> str: tmpl = (".TH \"%(title_upper)s\" \"%(manual_section)s\"" " \"%(date)s\" \"%(version)s\" \"%(manual_group)s\"\n" ".SH NAME\n" "%(title)s \\- %(subtitle)s\n") return tmpl % self._docinfo - def visit_start_of_file(self, node): - # type: (nodes.Element) -> None + def visit_start_of_file(self, node: Element) -> None: pass - def depart_start_of_file(self, node): - # type: (nodes.Element) -> None + def depart_start_of_file(self, node: Element) -> None: pass - def visit_desc(self, node): - # type: (nodes.Element) -> None + def visit_desc(self, node: Element) -> None: self.visit_definition_list(node) - def depart_desc(self, node): - # type: (nodes.Element) -> None + def depart_desc(self, node: Element) -> None: self.depart_definition_list(node) - def visit_desc_signature(self, node): - # type: (nodes.Element) -> None + def visit_desc_signature(self, node: Element) -> None: self.visit_definition_list_item(node) self.visit_term(node) - def depart_desc_signature(self, node): - # type: (nodes.Element) -> None + def depart_desc_signature(self, node: Element) -> None: self.depart_term(node) - def visit_desc_signature_line(self, node): - # type: (nodes.Element) -> None + def visit_desc_signature_line(self, node: Element) -> None: pass - def depart_desc_signature_line(self, node): - # type: (nodes.Element) -> None + def depart_desc_signature_line(self, node: Element) -> None: self.body.append(' ') - def visit_desc_addname(self, node): - # type: (nodes.Element) -> None + def visit_desc_addname(self, node: Element) -> None: pass - def depart_desc_addname(self, node): - # type: (nodes.Element) -> None + def depart_desc_addname(self, node: Element) -> None: pass - def visit_desc_type(self, node): - # type: (nodes.Element) -> None + def visit_desc_type(self, node: Element) -> None: pass - def depart_desc_type(self, node): - # type: (nodes.Element) -> None + def depart_desc_type(self, node: Element) -> None: pass - def visit_desc_returns(self, node): - # type: (nodes.Element) -> None + def visit_desc_returns(self, node: Element) -> None: self.body.append(' -> ') - def depart_desc_returns(self, node): - # type: (nodes.Element) -> None + def depart_desc_returns(self, node: Element) -> None: pass - def visit_desc_name(self, node): - # type: (nodes.Element) -> None + def visit_desc_name(self, node: Element) -> None: pass - def depart_desc_name(self, node): - # type: (nodes.Element) -> None + def depart_desc_name(self, node: Element) -> None: pass - def visit_desc_parameterlist(self, node): - # type: (nodes.Element) -> None + def visit_desc_parameterlist(self, node: Element) -> None: self.body.append('(') self.first_param = 1 - def depart_desc_parameterlist(self, node): - # type: (nodes.Element) -> None + def depart_desc_parameterlist(self, node: Element) -> None: self.body.append(')') - def visit_desc_parameter(self, node): - # type: (nodes.Element) -> None + def visit_desc_parameter(self, node: Element) -> None: if not self.first_param: self.body.append(', ') else: self.first_param = 0 - def depart_desc_parameter(self, node): - # type: (nodes.Element) -> None + def depart_desc_parameter(self, node: Element) -> None: pass - def visit_desc_optional(self, node): - # type: (nodes.Element) -> None + def visit_desc_optional(self, node: Element) -> None: self.body.append('[') - def depart_desc_optional(self, node): - # type: (nodes.Element) -> None + def depart_desc_optional(self, node: Element) -> None: self.body.append(']') - def visit_desc_annotation(self, node): - # type: (nodes.Element) -> None + def visit_desc_annotation(self, node: Element) -> None: pass - def depart_desc_annotation(self, node): - # type: (nodes.Element) -> None + def depart_desc_annotation(self, node: Element) -> None: pass - def visit_desc_content(self, node): - # type: (nodes.Element) -> None + def visit_desc_content(self, node: Element) -> None: self.visit_definition(node) - def depart_desc_content(self, node): - # type: (nodes.Element) -> None + def depart_desc_content(self, node: Element) -> None: self.depart_definition(node) - def visit_versionmodified(self, node): - # type: (nodes.Element) -> None + def visit_versionmodified(self, node: Element) -> None: self.visit_paragraph(node) - def depart_versionmodified(self, node): - # type: (nodes.Element) -> None + def depart_versionmodified(self, node: Element) -> None: self.depart_paragraph(node) # overwritten -- don't make whole of term bold if it includes strong node - def visit_term(self, node): - # type: (nodes.Element) -> None + def visit_term(self, node: Element) -> None: if node.traverse(nodes.strong): self.body.append('\n') else: super().visit_term(node) # overwritten -- we don't want source comments to show up - def visit_comment(self, node): # type: ignore - # type: (nodes.Element) -> None + def visit_comment(self, node: Element) -> None: # type: ignore raise nodes.SkipNode # overwritten -- added ensure_eol() - def visit_footnote(self, node): - # type: (nodes.Element) -> None + def visit_footnote(self, node: Element) -> None: self.ensure_eol() super().visit_footnote(node) # overwritten -- handle footnotes rubric - def visit_rubric(self, node): - # type: (nodes.Element) -> None + def visit_rubric(self, node: Element) -> None: self.ensure_eol() if len(node) == 1 and node.astext() in ('Footnotes', _('Footnotes')): self.body.append('.SH ' + self.deunicode(node.astext()).upper() + '\n') @@ -280,20 +241,16 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator): else: self.body.append('.sp\n') - def depart_rubric(self, node): - # type: (nodes.Element) -> None + def depart_rubric(self, node: Element) -> None: self.body.append('\n') - def visit_seealso(self, node): - # type: (nodes.Element) -> None + def visit_seealso(self, node: Element) -> None: self.visit_admonition(node, 'seealso') - def depart_seealso(self, node): - # type: (nodes.Element) -> None + def depart_seealso(self, node: Element) -> None: self.depart_admonition(node) - def visit_productionlist(self, node): - # type: (nodes.Element) -> None + def visit_productionlist(self, node: Element) -> None: self.ensure_eol() names = [] self.in_productionlist += 1 @@ -318,25 +275,21 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator): self.in_productionlist -= 1 raise nodes.SkipNode - def visit_production(self, node): - # type: (nodes.Element) -> None + def visit_production(self, node: Element) -> None: pass - def depart_production(self, node): - # type: (nodes.Element) -> None + def depart_production(self, node: Element) -> None: pass # overwritten -- don't emit a warning for images - def visit_image(self, node): - # type: (nodes.Element) -> None + def visit_image(self, node: Element) -> None: if 'alt' in node.attributes: self.body.append(_('[image: %s]') % node['alt'] + '\n') self.body.append(_('[image]') + '\n') raise nodes.SkipNode # overwritten -- don't visit inner marked up nodes - def visit_reference(self, node): - # type: (nodes.Element) -> None + def visit_reference(self, node: Element) -> None: self.body.append(self.defs['reference'][0]) # avoid repeating escaping code... fine since # visit_Text calls astext() and only works on that afterwards @@ -357,59 +310,46 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator): '>']) raise nodes.SkipNode - def visit_number_reference(self, node): - # type: (nodes.Element) -> None + def visit_number_reference(self, node: Element) -> None: text = nodes.Text(node.get('title', '#')) self.visit_Text(text) raise nodes.SkipNode - def visit_centered(self, node): - # type: (nodes.Element) -> None + def visit_centered(self, node: Element) -> None: self.ensure_eol() self.body.append('.sp\n.ce\n') - def depart_centered(self, node): - # type: (nodes.Element) -> None + def depart_centered(self, node: Element) -> None: self.body.append('\n.ce 0\n') - def visit_compact_paragraph(self, node): - # type: (nodes.Element) -> None + def visit_compact_paragraph(self, node: Element) -> None: pass - def depart_compact_paragraph(self, node): - # type: (nodes.Element) -> None + def depart_compact_paragraph(self, node: Element) -> None: pass - def visit_download_reference(self, node): - # type: (nodes.Element) -> None + def visit_download_reference(self, node: Element) -> None: pass - def depart_download_reference(self, node): - # type: (nodes.Element) -> None + def depart_download_reference(self, node: Element) -> None: pass - def visit_toctree(self, node): - # type: (nodes.Element) -> None + def visit_toctree(self, node: Element) -> None: raise nodes.SkipNode - def visit_index(self, node): - # type: (nodes.Element) -> None + def visit_index(self, node: Element) -> None: raise nodes.SkipNode - def visit_tabular_col_spec(self, node): - # type: (nodes.Element) -> None + def visit_tabular_col_spec(self, node: Element) -> None: raise nodes.SkipNode - def visit_glossary(self, node): - # type: (nodes.Element) -> None + def visit_glossary(self, node: Element) -> None: pass - def depart_glossary(self, node): - # type: (nodes.Element) -> None + def depart_glossary(self, node: Element) -> None: pass - def visit_acks(self, node): - # type: (nodes.Element) -> None + def visit_acks(self, node: Element) -> None: bullet_list = cast(nodes.bullet_list, node[0]) list_items = cast(Iterable[nodes.list_item], bullet_list) self.ensure_eol() @@ -419,72 +359,57 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator): self.body.append('\n') raise nodes.SkipNode - def visit_hlist(self, node): - # type: (nodes.Element) -> None + def visit_hlist(self, node: Element) -> None: self.visit_bullet_list(node) - def depart_hlist(self, node): - # type: (nodes.Element) -> None + def depart_hlist(self, node: Element) -> None: self.depart_bullet_list(node) - def visit_hlistcol(self, node): - # type: (nodes.Element) -> None + def visit_hlistcol(self, node: Element) -> None: pass - def depart_hlistcol(self, node): - # type: (nodes.Element) -> None + def depart_hlistcol(self, node: Element) -> None: pass - def visit_literal_emphasis(self, node): - # type: (nodes.Element) -> None + def visit_literal_emphasis(self, node: Element) -> None: return self.visit_emphasis(node) - def depart_literal_emphasis(self, node): - # type: (nodes.Element) -> None + def depart_literal_emphasis(self, node: Element) -> None: return self.depart_emphasis(node) - def visit_literal_strong(self, node): - # type: (nodes.Element) -> None + def visit_literal_strong(self, node: Element) -> None: return self.visit_strong(node) - def depart_literal_strong(self, node): - # type: (nodes.Element) -> None + def depart_literal_strong(self, node: Element) -> None: return self.depart_strong(node) - def visit_abbreviation(self, node): - # type: (nodes.Element) -> None + def visit_abbreviation(self, node: Element) -> None: pass - def depart_abbreviation(self, node): - # type: (nodes.Element) -> None + def depart_abbreviation(self, node: Element) -> None: pass - def visit_manpage(self, node): - # type: (nodes.Element) -> None + def visit_manpage(self, node: Element) -> None: return self.visit_strong(node) - def depart_manpage(self, node): - # type: (nodes.Element) -> None + def depart_manpage(self, node: Element) -> None: return self.depart_strong(node) # overwritten: handle section titles better than in 0.6 release - def visit_caption(self, node): - # type: (nodes.Element) -> None + def visit_caption(self, node: Element) -> None: if isinstance(node.parent, nodes.container) and node.parent.get('literal_block'): self.body.append('.sp\n') else: super().visit_caption(node) - def depart_caption(self, node): - # type: (nodes.Element) -> None + def depart_caption(self, node: Element) -> None: if isinstance(node.parent, nodes.container) and node.parent.get('literal_block'): self.body.append('\n') else: super().depart_caption(node) # overwritten: handle section titles better than in 0.6 release - def visit_title(self, node): - # type: (nodes.Element) -> None + def visit_title(self, node: Element) -> None: if isinstance(node.parent, addnodes.seealso): self.body.append('.IP "') return @@ -498,47 +423,37 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator): raise nodes.SkipNode return super().visit_title(node) - def depart_title(self, node): - # type: (nodes.Element) -> None + def depart_title(self, node: Element) -> None: if isinstance(node.parent, addnodes.seealso): self.body.append('"\n') return return super().depart_title(node) - def visit_raw(self, node): - # type: (nodes.Element) -> None + def visit_raw(self, node: Element) -> None: if 'manpage' in node.get('format', '').split(): self.body.append(node.astext()) raise nodes.SkipNode - def visit_meta(self, node): - # type: (nodes.Element) -> None + def visit_meta(self, node: Element) -> None: raise nodes.SkipNode - def visit_inline(self, node): - # type: (nodes.Element) -> None + def visit_inline(self, node: Element) -> None: pass - def depart_inline(self, node): - # type: (nodes.Element) -> None + def depart_inline(self, node: Element) -> None: pass - def visit_math(self, node): - # type: (nodes.Element) -> None + def visit_math(self, node: Element) -> None: pass - def depart_math(self, node): - # type: (nodes.Element) -> None + def depart_math(self, node: Element) -> None: pass - def visit_math_block(self, node): - # type: (nodes.Element) -> None + def visit_math_block(self, node: Element) -> None: self.visit_centered(node) - def depart_math_block(self, node): - # type: (nodes.Element) -> None + def depart_math_block(self, node: Element) -> None: self.depart_centered(node) - def unknown_visit(self, node): - # type: (nodes.Node) -> None + def unknown_visit(self, node: Node) -> None: raise NotImplementedError('Unknown node: ' + node.__class__.__name__) diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py index 15e7fd8d2..fd33c9fe4 100644 --- a/sphinx/writers/texinfo.py +++ b/sphinx/writers/texinfo.py @@ -11,11 +11,14 @@ import re import textwrap from os import path -from typing import Iterable, cast +from typing import Any, Dict, Iterable, Iterator, List, Pattern, Set, Tuple, Union +from typing import cast from docutils import nodes, writers +from docutils.nodes import Element, Node, Text from sphinx import addnodes, __display_version__ +from sphinx.domains import IndexEntry from sphinx.errors import ExtensionError from sphinx.locale import admonitionlabels, _, __ from sphinx.util import logging @@ -25,9 +28,8 @@ from sphinx.writers.latex import collected_footnote if False: # For type annotation - from typing import Any, Callable, Dict, Iterator, List, Pattern, Set, Tuple, Union # NOQA - from sphinx.builders.texinfo import TexinfoBuilder # NOQA - from sphinx.domains import IndexEntry # NOQA + from sphinx.builders.texinfo import TexinfoBuilder + logger = logging.getLogger(__name__) @@ -87,8 +89,7 @@ TEMPLATE = """\ """ -def find_subsections(section): - # type: (nodes.Element) -> List[nodes.section] +def find_subsections(section: Element) -> List[nodes.section]: """Return a list of subsections for the given ``section``.""" result = [] for child in section: @@ -100,8 +101,7 @@ def find_subsections(section): return result -def smart_capwords(s, sep=None): - # type: (str, str) -> str +def smart_capwords(s: str, sep: str = None) -> str: """Like string.capwords() but does not capitalize words that already contain a capital letter.""" words = s.split(sep) @@ -129,13 +129,11 @@ class TexinfoWriter(writers.Writer): visitor_attributes = ('output', 'fragment') - def __init__(self, builder): - # type: (TexinfoBuilder) -> None + def __init__(self, builder: "TexinfoBuilder") -> None: super().__init__() self.builder = builder - def translate(self): - # type: () -> None + def translate(self) -> None: visitor = self.builder.create_translator(self.document, self.builder) self.visitor = cast(TexinfoTranslator, visitor) self.document.walkabout(visitor) @@ -164,8 +162,7 @@ class TexinfoTranslator(SphinxTranslator): 'title': '', } - def __init__(self, document, builder): - # type: (nodes.document, TexinfoBuilder) -> None + def __init__(self, document: nodes.document, builder: "TexinfoBuilder") -> None: super().__init__(document, builder) self.init_settings() @@ -203,8 +200,7 @@ class TexinfoTranslator(SphinxTranslator): self.handled_abbrs = set() # type: Set[str] self.colwidths = None # type: List[int] - def finish(self): - # type: () -> None + def finish(self) -> None: if self.previous_section is None: self.add_menu('Top') for index in self.indices: @@ -225,8 +221,7 @@ class TexinfoTranslator(SphinxTranslator): # -- Helper routines - def init_settings(self): - # type: () -> None + def init_settings(self) -> None: elements = self.elements = self.default_elements.copy() elements.update({ # if empty, the title is set to the first section title @@ -268,14 +263,12 @@ class TexinfoTranslator(SphinxTranslator): # allow the user to override them all elements.update(self.settings.texinfo_elements) - def collect_node_names(self): - # type: () -> None + def collect_node_names(self) -> None: """Generates a unique id for each section. Assigns the attribute ``node_name`` to each section.""" - def add_node_name(name): - # type: (str) -> str + def add_node_name(name: str) -> str: node_id = self.escape_id(name) nth, suffix = 1, '' while node_id + suffix in self.written_ids or \ @@ -300,11 +293,10 @@ class TexinfoTranslator(SphinxTranslator): name = (title and title.astext()) or '' section['node_name'] = add_node_name(name) - def collect_node_menus(self): - # type: () -> None + def collect_node_menus(self) -> None: """Collect the menu entries for each "node" section.""" node_menus = self.node_menus - targets = [self.document] # type: List[nodes.Element] + targets = [self.document] # type: List[Element] targets.extend(self.document.traverse(nodes.section)) for node in targets: assert 'node_name' in node and node['node_name'] @@ -326,8 +318,7 @@ class TexinfoTranslator(SphinxTranslator): node_menus[name] = [] node_menus['Top'].append(name) - def collect_rellinks(self): - # type: () -> None + def collect_rellinks(self) -> None: """Collect the relative links (next, previous, up) for each "node".""" rellinks = self.rellinks node_menus = self.node_menus @@ -360,8 +351,7 @@ class TexinfoTranslator(SphinxTranslator): # namely menus and node names, it's not possible to escape certain # characters. - def escape(self, s): - # type: (str) -> str + def escape(self, s: str) -> str: """Return a string with Texinfo command characters escaped.""" s = s.replace('@', '@@') s = s.replace('{', '@{') @@ -371,8 +361,7 @@ class TexinfoTranslator(SphinxTranslator): s = s.replace("''", "'@w{'}") return s - def escape_arg(self, s): - # type: (str) -> str + def escape_arg(self, s: str) -> str: """Return an escaped string suitable for use as an argument to a Texinfo command.""" s = self.escape(s) @@ -382,8 +371,7 @@ class TexinfoTranslator(SphinxTranslator): s = ' '.join(s.split()).strip() return s - def escape_id(self, s): - # type: (str) -> str + def escape_id(self, s: str) -> str: """Return an escaped string suitable for node names and anchors.""" bad_chars = ',:()' for bc in bad_chars: @@ -394,22 +382,19 @@ class TexinfoTranslator(SphinxTranslator): s = ' '.join(s.split()).strip() return self.escape(s) - def escape_menu(self, s): - # type: (str) -> str + def escape_menu(self, s: str) -> str: """Return an escaped string suitable for menu entries.""" s = self.escape_arg(s) s = s.replace(':', ';') s = ' '.join(s.split()).strip() return s - def ensure_eol(self): - # type: () -> None + def ensure_eol(self) -> None: """Ensure the last line in body is terminated by new line.""" if self.body and self.body[-1][-1:] != '\n': self.body.append('\n') - def format_menu_entry(self, name, node_name, desc): - # type: (str, str, str) -> str + def format_menu_entry(self, name: str, node_name: str, desc: str) -> str: if name == node_name: s = '* %s:: ' % (name,) else: @@ -419,8 +404,8 @@ class TexinfoTranslator(SphinxTranslator): textwrap.wrap(desc, width=78 - offset)) return s + wdesc.strip() + '\n' - def add_menu_entries(self, entries, reg=re.compile(r'\s+---?\s+')): - # type: (List[str], Pattern) -> None + def add_menu_entries(self, entries: List[str], reg: Pattern = re.compile(r'\s+---?\s+') + ) -> None: for entry in entries: name = self.node_names[entry] # special formatting for entries that are divided by an em-dash @@ -437,8 +422,7 @@ class TexinfoTranslator(SphinxTranslator): desc = self.escape(desc) self.body.append(self.format_menu_entry(name, entry, desc)) - def add_menu(self, node_name): - # type: (str) -> None + def add_menu(self, node_name: str) -> None: entries = self.node_menus[node_name] if not entries: return @@ -450,8 +434,7 @@ class TexinfoTranslator(SphinxTranslator): self.body.append('\n@end menu\n') return - def _add_detailed_menu(name): - # type: (str) -> None + def _add_detailed_menu(name: str) -> None: entries = self.node_menus[name] if not entries: return @@ -467,8 +450,7 @@ class TexinfoTranslator(SphinxTranslator): self.body.append('\n@end detailmenu\n' '@end menu\n') - def tex_image_length(self, width_str): - # type: (str) -> str + def tex_image_length(self, width_str: str) -> str: match = re.match(r'(\d*\.?\d*)\s*(\S*)', width_str) if not match: # fallback @@ -483,10 +465,8 @@ class TexinfoTranslator(SphinxTranslator): res = "%d.0pt" % (float(amount) * 4.1825368) return res - def collect_indices(self): - # type: () -> None - def generate(content, collapsed): - # type: (List[Tuple[str, List[IndexEntry]]], bool) -> str + def collect_indices(self) -> None: + def generate(content: List[Tuple[str, List[IndexEntry]]], collapsed: bool) -> str: ret = ['\n@menu\n'] for letter, entries in content: for entry in entries: @@ -523,10 +503,8 @@ class TexinfoTranslator(SphinxTranslator): # this is copied from the latex writer # TODO: move this to sphinx.util - def collect_footnotes(self, node): - # type: (nodes.Element) -> Dict[str, List[Union[collected_footnote, bool]]] - def footnotes_under(n): - # type: (nodes.Element) -> Iterator[nodes.footnote] + def collect_footnotes(self, node: Element) -> Dict[str, List[Union[collected_footnote, bool]]]: # NOQA + def footnotes_under(n: Element) -> Iterator[nodes.footnote]: if isinstance(n, nodes.footnote): yield n else: @@ -544,8 +522,7 @@ class TexinfoTranslator(SphinxTranslator): # -- xref handling - def get_short_id(self, id): - # type: (str) -> str + def get_short_id(self, id: str) -> str: """Return a shorter 'id' associated with ``id``.""" # Shorter ids improve paragraph filling in places # that the id is hidden by Emacs. @@ -556,8 +533,7 @@ class TexinfoTranslator(SphinxTranslator): self.short_ids[id] = sid return sid - def add_anchor(self, id, node): - # type: (str, nodes.Node) -> None + def add_anchor(self, id: str, node: Node) -> None: if id.startswith('index-'): return id = self.curfilestack[-1] + ':' + id @@ -568,8 +544,7 @@ class TexinfoTranslator(SphinxTranslator): self.body.append('@anchor{%s}' % id) self.written_ids.add(id) - def add_xref(self, id, name, node): - # type: (str, str, nodes.Node) -> None + def add_xref(self, id: str, name: str, node: Node) -> None: name = self.escape_menu(name) sid = self.get_short_id(id) self.body.append('@ref{%s,,%s}' % (sid, name)) @@ -578,20 +553,17 @@ class TexinfoTranslator(SphinxTranslator): # -- Visiting - def visit_document(self, node): - # type: (nodes.Element) -> None + def visit_document(self, node: Element) -> None: self.footnotestack.append(self.collect_footnotes(node)) self.curfilestack.append(node.get('docname', '')) if 'docname' in node: self.add_anchor(':doc', node) - def depart_document(self, node): - # type: (nodes.Element) -> None + def depart_document(self, node: Element) -> None: self.footnotestack.pop() self.curfilestack.pop() - def visit_Text(self, node): - # type: (nodes.Text) -> None + def visit_Text(self, node: Text) -> None: s = self.escape(node.astext()) if self.escape_newlines: s = s.replace('\n', ' ') @@ -600,12 +572,10 @@ class TexinfoTranslator(SphinxTranslator): s = s.replace('-', '@w{-}') self.body.append(s) - def depart_Text(self, node): - # type: (nodes.Text) -> None + def depart_Text(self, node: Text) -> None: pass - def visit_section(self, node): - # type: (nodes.Element) -> None + def visit_section(self, node: Element) -> None: self.next_section_ids.update(node.get('ids', [])) if not self.seen_title: return @@ -624,8 +594,7 @@ class TexinfoTranslator(SphinxTranslator): self.previous_section = cast(nodes.section, node) self.section_level += 1 - def depart_section(self, node): - # type: (nodes.Element) -> None + def depart_section(self, node: Element) -> None: self.section_level -= 1 headings = ( @@ -642,8 +611,7 @@ class TexinfoTranslator(SphinxTranslator): '@subsubheading', ) - def visit_title(self, node): - # type: (nodes.Element) -> None + def visit_title(self, node: Element) -> None: if not self.seen_title: self.seen_title = True raise nodes.SkipNode @@ -664,12 +632,10 @@ class TexinfoTranslator(SphinxTranslator): heading = self.headings[-1] self.body.append('\n%s ' % heading) - def depart_title(self, node): - # type: (nodes.Element) -> None + def depart_title(self, node: Element) -> None: self.body.append('\n\n') - def visit_rubric(self, node): - # type: (nodes.Element) -> None + def visit_rubric(self, node: Element) -> None: if len(node) == 1 and node.astext() in ('Footnotes', _('Footnotes')): raise nodes.SkipNode try: @@ -679,23 +645,19 @@ class TexinfoTranslator(SphinxTranslator): self.body.append('\n%s ' % rubric) self.escape_newlines += 1 - def depart_rubric(self, node): - # type: (nodes.Element) -> None + def depart_rubric(self, node: Element) -> None: self.escape_newlines -= 1 self.body.append('\n\n') - def visit_subtitle(self, node): - # type: (nodes.Element) -> None + def visit_subtitle(self, node: Element) -> None: self.body.append('\n\n@noindent\n') - def depart_subtitle(self, node): - # type: (nodes.Element) -> None + def depart_subtitle(self, node: Element) -> None: self.body.append('\n\n') # -- References - def visit_target(self, node): - # type: (nodes.Element) -> None + def visit_target(self, node: Element) -> None: # postpone the labels until after the sectioning command parindex = node.parent.index(node) try: @@ -719,12 +681,10 @@ class TexinfoTranslator(SphinxTranslator): for id in node['ids']: self.add_anchor(id, node) - def depart_target(self, node): - # type: (nodes.Element) -> None + def depart_target(self, node: Element) -> None: pass - def visit_reference(self, node): - # type: (nodes.Element) -> None + def visit_reference(self, node: Element) -> None: # an xref's target is displayed in Info so we ignore a few # cases for the sake of appearance if isinstance(node.parent, (nodes.title, addnodes.desc_type)): @@ -787,134 +747,107 @@ class TexinfoTranslator(SphinxTranslator): self.body.append('%s@footnote{%s}' % (name, uri)) raise nodes.SkipNode - def depart_reference(self, node): - # type: (nodes.Element) -> None + def depart_reference(self, node: Element) -> None: pass - def visit_number_reference(self, node): - # type: (nodes.Element) -> None + def visit_number_reference(self, node: Element) -> None: text = nodes.Text(node.get('title', '#')) self.visit_Text(text) raise nodes.SkipNode - def visit_title_reference(self, node): - # type: (nodes.Element) -> None + def visit_title_reference(self, node: Element) -> None: text = node.astext() self.body.append('@cite{%s}' % self.escape_arg(text)) raise nodes.SkipNode # -- Blocks - def visit_paragraph(self, node): - # type: (nodes.Element) -> None + def visit_paragraph(self, node: Element) -> None: self.body.append('\n') - def depart_paragraph(self, node): - # type: (nodes.Element) -> None + def depart_paragraph(self, node: Element) -> None: self.body.append('\n') - def visit_block_quote(self, node): - # type: (nodes.Element) -> None + def visit_block_quote(self, node: Element) -> None: self.body.append('\n@quotation\n') - def depart_block_quote(self, node): - # type: (nodes.Element) -> None + def depart_block_quote(self, node: Element) -> None: self.ensure_eol() self.body.append('@end quotation\n') - def visit_literal_block(self, node): - # type: (nodes.Element) -> None + def visit_literal_block(self, node: Element) -> None: self.body.append('\n@example\n') - def depart_literal_block(self, node): - # type: (nodes.Element) -> None + def depart_literal_block(self, node: Element) -> None: self.ensure_eol() self.body.append('@end example\n') visit_doctest_block = visit_literal_block depart_doctest_block = depart_literal_block - def visit_line_block(self, node): - # type: (nodes.Element) -> None + def visit_line_block(self, node: Element) -> None: if not isinstance(node.parent, nodes.line_block): self.body.append('\n\n') self.body.append('@display\n') - def depart_line_block(self, node): - # type: (nodes.Element) -> None + def depart_line_block(self, node: Element) -> None: self.body.append('@end display\n') if not isinstance(node.parent, nodes.line_block): self.body.append('\n\n') - def visit_line(self, node): - # type: (nodes.Element) -> None + def visit_line(self, node: Element) -> None: self.escape_newlines += 1 - def depart_line(self, node): - # type: (nodes.Element) -> None + def depart_line(self, node: Element) -> None: self.body.append('@w{ }\n') self.escape_newlines -= 1 # -- Inline - def visit_strong(self, node): - # type: (nodes.Element) -> None + def visit_strong(self, node: Element) -> None: self.body.append('@strong{') - def depart_strong(self, node): - # type: (nodes.Element) -> None + def depart_strong(self, node: Element) -> None: self.body.append('}') - def visit_emphasis(self, node): - # type: (nodes.Element) -> None + def visit_emphasis(self, node: Element) -> None: self.body.append('@emph{') - def depart_emphasis(self, node): - # type: (nodes.Element) -> None + def depart_emphasis(self, node: Element) -> None: self.body.append('}') - def visit_literal(self, node): - # type: (nodes.Element) -> None + def visit_literal(self, node: Element) -> None: self.body.append('@code{') - def depart_literal(self, node): - # type: (nodes.Element) -> None + def depart_literal(self, node: Element) -> None: self.body.append('}') - def visit_superscript(self, node): - # type: (nodes.Element) -> None + def visit_superscript(self, node: Element) -> None: self.body.append('@w{^') - def depart_superscript(self, node): - # type: (nodes.Element) -> None + def depart_superscript(self, node: Element) -> None: self.body.append('}') - def visit_subscript(self, node): - # type: (nodes.Element) -> None + def visit_subscript(self, node: Element) -> None: self.body.append('@w{[') - def depart_subscript(self, node): - # type: (nodes.Element) -> None + def depart_subscript(self, node: Element) -> None: self.body.append(']}') # -- Footnotes - def visit_footnote(self, node): - # type: (nodes.Element) -> None + def visit_footnote(self, node: Element) -> None: raise nodes.SkipNode - def visit_collected_footnote(self, node): - # type: (nodes.Element) -> None + def visit_collected_footnote(self, node: Element) -> None: self.in_footnote += 1 self.body.append('@footnote{') - def depart_collected_footnote(self, node): - # type: (nodes.Element) -> None + def depart_collected_footnote(self, node: Element) -> None: self.body.append('}') self.in_footnote -= 1 - def visit_footnote_reference(self, node): - # type: (nodes.Element) -> None + def visit_footnote_reference(self, node: Element) -> None: num = node.astext().strip() try: footnode, used = self.footnotestack[-1][num] @@ -924,39 +857,32 @@ class TexinfoTranslator(SphinxTranslator): footnode.walkabout(self) # type: ignore raise nodes.SkipChildren - def visit_citation(self, node): - # type: (nodes.Element) -> None + def visit_citation(self, node: Element) -> None: self.body.append('\n') for id in node.get('ids'): self.add_anchor(id, node) self.escape_newlines += 1 - def depart_citation(self, node): - # type: (nodes.Element) -> None + def depart_citation(self, node: Element) -> None: self.escape_newlines -= 1 - def visit_citation_reference(self, node): - # type: (nodes.Element) -> None + def visit_citation_reference(self, node: Element) -> None: self.body.append('@w{[') - def depart_citation_reference(self, node): - # type: (nodes.Element) -> None + def depart_citation_reference(self, node: Element) -> None: self.body.append(']}') # -- Lists - def visit_bullet_list(self, node): - # type: (nodes.Element) -> None + def visit_bullet_list(self, node: Element) -> None: bullet = node.get('bullet', '*') self.body.append('\n\n@itemize %s\n' % bullet) - def depart_bullet_list(self, node): - # type: (nodes.Element) -> None + def depart_bullet_list(self, node: Element) -> None: self.ensure_eol() self.body.append('@end itemize\n') - def visit_enumerated_list(self, node): - # type: (nodes.Element) -> None + def visit_enumerated_list(self, node: Element) -> None: # doesn't support Roman numerals enum = node.get('enumtype', 'arabic') starters = {'arabic': '', @@ -965,101 +891,79 @@ class TexinfoTranslator(SphinxTranslator): start = node.get('start', starters.get(enum, '')) self.body.append('\n\n@enumerate %s\n' % start) - def depart_enumerated_list(self, node): - # type: (nodes.Element) -> None + def depart_enumerated_list(self, node: Element) -> None: self.ensure_eol() self.body.append('@end enumerate\n') - def visit_list_item(self, node): - # type: (nodes.Element) -> None + def visit_list_item(self, node: Element) -> None: self.body.append('\n@item ') - def depart_list_item(self, node): - # type: (nodes.Element) -> None + def depart_list_item(self, node: Element) -> None: pass # -- Option List - def visit_option_list(self, node): - # type: (nodes.Element) -> None + def visit_option_list(self, node: Element) -> None: self.body.append('\n\n@table @option\n') - def depart_option_list(self, node): - # type: (nodes.Element) -> None + def depart_option_list(self, node: Element) -> None: self.ensure_eol() self.body.append('@end table\n') - def visit_option_list_item(self, node): - # type: (nodes.Element) -> None + def visit_option_list_item(self, node: Element) -> None: pass - def depart_option_list_item(self, node): - # type: (nodes.Element) -> None + def depart_option_list_item(self, node: Element) -> None: pass - def visit_option_group(self, node): - # type: (nodes.Element) -> None + def visit_option_group(self, node: Element) -> None: self.at_item_x = '@item' - def depart_option_group(self, node): - # type: (nodes.Element) -> None + def depart_option_group(self, node: Element) -> None: pass - def visit_option(self, node): - # type: (nodes.Element) -> None + def visit_option(self, node: Element) -> None: self.escape_hyphens += 1 self.body.append('\n%s ' % self.at_item_x) self.at_item_x = '@itemx' - def depart_option(self, node): - # type: (nodes.Element) -> None + def depart_option(self, node: Element) -> None: self.escape_hyphens -= 1 - def visit_option_string(self, node): - # type: (nodes.Element) -> None + def visit_option_string(self, node: Element) -> None: pass - def depart_option_string(self, node): - # type: (nodes.Element) -> None + def depart_option_string(self, node: Element) -> None: pass - def visit_option_argument(self, node): - # type: (nodes.Element) -> None + def visit_option_argument(self, node: Element) -> None: self.body.append(node.get('delimiter', ' ')) - def depart_option_argument(self, node): - # type: (nodes.Element) -> None + def depart_option_argument(self, node: Element) -> None: pass - def visit_description(self, node): - # type: (nodes.Element) -> None + def visit_description(self, node: Element) -> None: self.body.append('\n') - def depart_description(self, node): - # type: (nodes.Element) -> None + def depart_description(self, node: Element) -> None: pass # -- Definitions - def visit_definition_list(self, node): - # type: (nodes.Element) -> None + def visit_definition_list(self, node: Element) -> None: self.body.append('\n\n@table @asis\n') - def depart_definition_list(self, node): - # type: (nodes.Element) -> None + def depart_definition_list(self, node: Element) -> None: self.ensure_eol() self.body.append('@end table\n') - def visit_definition_list_item(self, node): - # type: (nodes.Element) -> None + def visit_definition_list_item(self, node: Element) -> None: self.at_item_x = '@item' - def depart_definition_list_item(self, node): - # type: (nodes.Element) -> None + def depart_definition_list_item(self, node: Element) -> None: pass - def visit_term(self, node): - # type: (nodes.Element) -> None + def visit_term(self, node: Element) -> None: for id in node.get('ids'): self.add_anchor(id, node) # anchors and indexes need to go in front @@ -1070,46 +974,36 @@ class TexinfoTranslator(SphinxTranslator): self.body.append('\n%s ' % self.at_item_x) self.at_item_x = '@itemx' - def depart_term(self, node): - # type: (nodes.Element) -> None + def depart_term(self, node: Element) -> None: pass - def visit_classifier(self, node): - # type: (nodes.Element) -> None + def visit_classifier(self, node: Element) -> None: self.body.append(' : ') - def depart_classifier(self, node): - # type: (nodes.Element) -> None + def depart_classifier(self, node: Element) -> None: pass - def visit_definition(self, node): - # type: (nodes.Element) -> None + def visit_definition(self, node: Element) -> None: self.body.append('\n') - def depart_definition(self, node): - # type: (nodes.Element) -> None + def depart_definition(self, node: Element) -> None: pass # -- Tables - def visit_table(self, node): - # type: (nodes.Element) -> None + def visit_table(self, node: Element) -> None: self.entry_sep = '@item' - def depart_table(self, node): - # type: (nodes.Element) -> None + def depart_table(self, node: Element) -> None: self.body.append('\n@end multitable\n\n') - def visit_tabular_col_spec(self, node): - # type: (nodes.Element) -> None + def visit_tabular_col_spec(self, node: Element) -> None: pass - def depart_tabular_col_spec(self, node): - # type: (nodes.Element) -> None + def depart_tabular_col_spec(self, node: Element) -> None: pass - def visit_colspec(self, node): - # type: (nodes.Element) -> None + def visit_colspec(self, node: Element) -> None: self.colwidths.append(node['colwidth']) if len(self.colwidths) != self.n_cols: return @@ -1117,104 +1011,82 @@ class TexinfoTranslator(SphinxTranslator): for i, n in enumerate(self.colwidths): self.body.append('{%s} ' % ('x' * (n + 2))) - def depart_colspec(self, node): - # type: (nodes.Element) -> None + def depart_colspec(self, node: Element) -> None: pass - def visit_tgroup(self, node): - # type: (nodes.Element) -> None + def visit_tgroup(self, node: Element) -> None: self.colwidths = [] self.n_cols = node['cols'] - def depart_tgroup(self, node): - # type: (nodes.Element) -> None + def depart_tgroup(self, node: Element) -> None: pass - def visit_thead(self, node): - # type: (nodes.Element) -> None + def visit_thead(self, node: Element) -> None: self.entry_sep = '@headitem' - def depart_thead(self, node): - # type: (nodes.Element) -> None + def depart_thead(self, node: Element) -> None: pass - def visit_tbody(self, node): - # type: (nodes.Element) -> None + def visit_tbody(self, node: Element) -> None: pass - def depart_tbody(self, node): - # type: (nodes.Element) -> None + def depart_tbody(self, node: Element) -> None: pass - def visit_row(self, node): - # type: (nodes.Element) -> None + def visit_row(self, node: Element) -> None: pass - def depart_row(self, node): - # type: (nodes.Element) -> None + def depart_row(self, node: Element) -> None: self.entry_sep = '@item' - def visit_entry(self, node): - # type: (nodes.Element) -> None + def visit_entry(self, node: Element) -> None: self.body.append('\n%s\n' % self.entry_sep) self.entry_sep = '@tab' - def depart_entry(self, node): - # type: (nodes.Element) -> None + def depart_entry(self, node: Element) -> None: for i in range(node.get('morecols', 0)): self.body.append('\n@tab\n') # -- Field Lists - def visit_field_list(self, node): - # type: (nodes.Element) -> None + def visit_field_list(self, node: Element) -> None: pass - def depart_field_list(self, node): - # type: (nodes.Element) -> None + def depart_field_list(self, node: Element) -> None: pass - def visit_field(self, node): - # type: (nodes.Element) -> None + def visit_field(self, node: Element) -> None: self.body.append('\n') - def depart_field(self, node): - # type: (nodes.Element) -> None + def depart_field(self, node: Element) -> None: self.body.append('\n') - def visit_field_name(self, node): - # type: (nodes.Element) -> None + def visit_field_name(self, node: Element) -> None: self.ensure_eol() self.body.append('@*') - def depart_field_name(self, node): - # type: (nodes.Element) -> None + def depart_field_name(self, node: Element) -> None: self.body.append(': ') - def visit_field_body(self, node): - # type: (nodes.Element) -> None + def visit_field_body(self, node: Element) -> None: pass - def depart_field_body(self, node): - # type: (nodes.Element) -> None + def depart_field_body(self, node: Element) -> None: pass # -- Admonitions - def visit_admonition(self, node, name=''): - # type: (nodes.Element, str) -> None + def visit_admonition(self, node: Element, name: str = '') -> None: if not name: title = cast(nodes.title, node[0]) name = self.escape(title.astext()) self.body.append('\n@cartouche\n@quotation %s ' % name) - def _visit_named_admonition(self, node): - # type: (nodes.Element) -> None + def _visit_named_admonition(self, node: Element) -> None: label = admonitionlabels[node.tagname] self.body.append('\n@cartouche\n@quotation %s ' % label) - def depart_admonition(self, node): - # type: (nodes.Element) -> None + def depart_admonition(self, node: Element) -> None: self.ensure_eol() self.body.append('@end quotation\n' '@end cartouche\n') @@ -1240,42 +1112,33 @@ class TexinfoTranslator(SphinxTranslator): # -- Misc - def visit_docinfo(self, node): - # type: (nodes.Element) -> None + def visit_docinfo(self, node: Element) -> None: raise nodes.SkipNode - def visit_generated(self, node): - # type: (nodes.Element) -> None + def visit_generated(self, node: Element) -> None: raise nodes.SkipNode - def visit_header(self, node): - # type: (nodes.Element) -> None + def visit_header(self, node: Element) -> None: raise nodes.SkipNode - def visit_footer(self, node): - # type: (nodes.Element) -> None + def visit_footer(self, node: Element) -> None: raise nodes.SkipNode - def visit_container(self, node): - # type: (nodes.Element) -> None + def visit_container(self, node: Element) -> None: if node.get('literal_block'): self.body.append('\n\n@float LiteralBlock\n') - def depart_container(self, node): - # type: (nodes.Element) -> None + def depart_container(self, node: Element) -> None: if node.get('literal_block'): self.body.append('\n@end float\n\n') - def visit_decoration(self, node): - # type: (nodes.Element) -> None + def visit_decoration(self, node: Element) -> None: pass - def depart_decoration(self, node): - # type: (nodes.Element) -> None + def depart_decoration(self, node: Element) -> None: pass - def visit_topic(self, node): - # type: (nodes.Element) -> None + def visit_topic(self, node: Element) -> None: # ignore TOC's since we have to have a "menu" anyway if 'contents' in node.get('classes', []): raise nodes.SkipNode @@ -1284,43 +1147,34 @@ class TexinfoTranslator(SphinxTranslator): self.body.append('%s\n' % self.escape(title.astext())) self.depart_rubric(title) - def depart_topic(self, node): - # type: (nodes.Element) -> None + def depart_topic(self, node: Element) -> None: pass - def visit_transition(self, node): - # type: (nodes.Element) -> None + def visit_transition(self, node: Element) -> None: self.body.append('\n\n%s\n\n' % ('_' * 66)) - def depart_transition(self, node): - # type: (nodes.Element) -> None + def depart_transition(self, node: Element) -> None: pass - def visit_attribution(self, node): - # type: (nodes.Element) -> None + def visit_attribution(self, node: Element) -> None: self.body.append('\n\n@center --- ') - def depart_attribution(self, node): - # type: (nodes.Element) -> None + def depart_attribution(self, node: Element) -> None: self.body.append('\n\n') - def visit_raw(self, node): - # type: (nodes.Element) -> None + def visit_raw(self, node: Element) -> None: format = node.get('format', '').split() if 'texinfo' in format or 'texi' in format: self.body.append(node.astext()) raise nodes.SkipNode - def visit_figure(self, node): - # type: (nodes.Element) -> None + def visit_figure(self, node: Element) -> None: self.body.append('\n\n@float Figure\n') - def depart_figure(self, node): - # type: (nodes.Element) -> None + def depart_figure(self, node: Element) -> None: self.body.append('\n@end float\n\n') - def visit_caption(self, node): - # type: (nodes.Element) -> None + def visit_caption(self, node: Element) -> None: if (isinstance(node.parent, nodes.figure) or (isinstance(node.parent, nodes.container) and node.parent.get('literal_block'))): @@ -1329,15 +1183,13 @@ class TexinfoTranslator(SphinxTranslator): logger.warning(__('caption not inside a figure.'), location=(self.curfilestack[-1], node.line)) - def depart_caption(self, node): - # type: (nodes.Element) -> None + def depart_caption(self, node: Element) -> None: if (isinstance(node.parent, nodes.figure) or (isinstance(node.parent, nodes.container) and node.parent.get('literal_block'))): self.body.append('}\n') - def visit_image(self, node): - # type: (nodes.Element) -> None + def visit_image(self, node: Element) -> None: if node['uri'] in self.builder.images: uri = self.builder.images[node['uri']] else: @@ -1358,82 +1210,65 @@ class TexinfoTranslator(SphinxTranslator): self.body.append('\n@image{%s,%s,%s,%s,%s}\n' % (filename, width, height, alt, ext[1:])) - def depart_image(self, node): - # type: (nodes.Element) -> None + def depart_image(self, node: Element) -> None: pass - def visit_compound(self, node): - # type: (nodes.Element) -> None + def visit_compound(self, node: Element) -> None: pass - def depart_compound(self, node): - # type: (nodes.Element) -> None + def depart_compound(self, node: Element) -> None: pass - def visit_sidebar(self, node): - # type: (nodes.Element) -> None + def visit_sidebar(self, node: Element) -> None: self.visit_topic(node) - def depart_sidebar(self, node): - # type: (nodes.Element) -> None + def depart_sidebar(self, node: Element) -> None: self.depart_topic(node) - def visit_label(self, node): - # type: (nodes.Element) -> None + def visit_label(self, node: Element) -> None: self.body.append('@w{(') - def depart_label(self, node): - # type: (nodes.Element) -> None + def depart_label(self, node: Element) -> None: self.body.append(')} ') - def visit_legend(self, node): - # type: (nodes.Element) -> None + def visit_legend(self, node: Element) -> None: pass - def depart_legend(self, node): - # type: (nodes.Element) -> None + def depart_legend(self, node: Element) -> None: pass - def visit_system_message(self, node): - # type: (nodes.Element) -> None + def visit_system_message(self, node: Element) -> None: self.body.append('\n@verbatim\n' '\n' '@end verbatim\n' % node.astext()) raise nodes.SkipNode - def visit_comment(self, node): - # type: (nodes.Element) -> None + def visit_comment(self, node: Element) -> None: self.body.append('\n') for line in node.astext().splitlines(): self.body.append('@c %s\n' % line) raise nodes.SkipNode - def visit_problematic(self, node): - # type: (nodes.Element) -> None + def visit_problematic(self, node: Element) -> None: self.body.append('>>') - def depart_problematic(self, node): - # type: (nodes.Element) -> None + def depart_problematic(self, node: Element) -> None: self.body.append('<<') - def unimplemented_visit(self, node): - # type: (nodes.Element) -> None + def unimplemented_visit(self, node: Element) -> None: logger.warning(__("unimplemented node type: %r"), node, location=(self.curfilestack[-1], node.line)) - def unknown_visit(self, node): - # type: (nodes.Node) -> None + def unknown_visit(self, node: Node) -> None: logger.warning(__("unknown node type: %r"), node, location=(self.curfilestack[-1], node.line)) - def unknown_departure(self, node): - # type: (nodes.Node) -> None + def unknown_departure(self, node: Node) -> None: pass # -- Sphinx specific - def visit_productionlist(self, node): - # type: (nodes.Element) -> None + def visit_productionlist(self, node: Element) -> None: self.visit_literal_block(None) names = [] productionlist = cast(Iterable[addnodes.production], node) @@ -1452,32 +1287,25 @@ class TexinfoTranslator(SphinxTranslator): self.depart_literal_block(None) raise nodes.SkipNode - def visit_production(self, node): - # type: (nodes.Element) -> None + def visit_production(self, node: Element) -> None: pass - def depart_production(self, node): - # type: (nodes.Element) -> None + def depart_production(self, node: Element) -> None: pass - def visit_literal_emphasis(self, node): - # type: (nodes.Element) -> None + def visit_literal_emphasis(self, node: Element) -> None: self.body.append('@code{') - def depart_literal_emphasis(self, node): - # type: (nodes.Element) -> None + def depart_literal_emphasis(self, node: Element) -> None: self.body.append('}') - def visit_literal_strong(self, node): - # type: (nodes.Element) -> None + def visit_literal_strong(self, node: Element) -> None: self.body.append('@code{') - def depart_literal_strong(self, node): - # type: (nodes.Element) -> None + def depart_literal_strong(self, node: Element) -> None: self.body.append('}') - def visit_index(self, node): - # type: (nodes.Element) -> None + def visit_index(self, node: Element) -> None: # terminate the line but don't prevent paragraph breaks if isinstance(node.parent, nodes.paragraph): self.ensure_eol() @@ -1488,55 +1316,44 @@ class TexinfoTranslator(SphinxTranslator): text = self.escape_menu(text) self.body.append('@geindex %s\n' % text) - def visit_versionmodified(self, node): - # type: (nodes.Element) -> None + def visit_versionmodified(self, node: Element) -> None: self.body.append('\n') - def depart_versionmodified(self, node): - # type: (nodes.Element) -> None + def depart_versionmodified(self, node: Element) -> None: self.body.append('\n') - def visit_start_of_file(self, node): - # type: (nodes.Element) -> None + def visit_start_of_file(self, node: Element) -> None: # add a document target self.next_section_ids.add(':doc') self.curfilestack.append(node['docname']) self.footnotestack.append(self.collect_footnotes(node)) - def depart_start_of_file(self, node): - # type: (nodes.Element) -> None + def depart_start_of_file(self, node: Element) -> None: self.curfilestack.pop() self.footnotestack.pop() - def visit_centered(self, node): - # type: (nodes.Element) -> None + def visit_centered(self, node: Element) -> None: txt = self.escape_arg(node.astext()) self.body.append('\n\n@center %s\n\n' % txt) raise nodes.SkipNode - def visit_seealso(self, node): - # type: (nodes.Element) -> None + def visit_seealso(self, node: Element) -> None: self.body.append('\n\n@subsubheading %s\n\n' % admonitionlabels['seealso']) - def depart_seealso(self, node): - # type: (nodes.Element) -> None + def depart_seealso(self, node: Element) -> None: self.body.append('\n') - def visit_meta(self, node): - # type: (nodes.Element) -> None + def visit_meta(self, node: Element) -> None: raise nodes.SkipNode - def visit_glossary(self, node): - # type: (nodes.Element) -> None + def visit_glossary(self, node: Element) -> None: pass - def depart_glossary(self, node): - # type: (nodes.Element) -> None + def depart_glossary(self, node: Element) -> None: pass - def visit_acks(self, node): - # type: (nodes.Element) -> None + def visit_acks(self, node: Element) -> None: bullet_list = cast(nodes.bullet_list, node[0]) list_items = cast(Iterable[nodes.list_item], bullet_list) self.body.append('\n\n') @@ -1546,19 +1363,16 @@ class TexinfoTranslator(SphinxTranslator): # -- Desc - def visit_desc(self, node): - # type: (nodes.Element) -> None + def visit_desc(self, node: Element) -> None: self.desc = node self.at_deffnx = '@deffn' - def depart_desc(self, node): - # type: (nodes.Element) -> None + def depart_desc(self, node: Element) -> None: self.desc = None self.ensure_eol() self.body.append('@end deffn\n') - def visit_desc_signature(self, node): - # type: (nodes.Element) -> None + def visit_desc_signature(self, node: Element) -> None: self.escape_hyphens += 1 objtype = node.parent['objtype'] if objtype != 'describe': @@ -1578,55 +1392,43 @@ class TexinfoTranslator(SphinxTranslator): self.at_deffnx = '@deffnx' self.desc_type_name = name - def depart_desc_signature(self, node): - # type: (nodes.Element) -> None + def depart_desc_signature(self, node: Element) -> None: self.body.append("\n") self.escape_hyphens -= 1 self.desc_type_name = None - def visit_desc_name(self, node): - # type: (nodes.Element) -> None + def visit_desc_name(self, node: Element) -> None: pass - def depart_desc_name(self, node): - # type: (nodes.Element) -> None + def depart_desc_name(self, node: Element) -> None: pass - def visit_desc_addname(self, node): - # type: (nodes.Element) -> None + def visit_desc_addname(self, node: Element) -> None: pass - def depart_desc_addname(self, node): - # type: (nodes.Element) -> None + def depart_desc_addname(self, node: Element) -> None: pass - def visit_desc_type(self, node): - # type: (nodes.Element) -> None + def visit_desc_type(self, node: Element) -> None: pass - def depart_desc_type(self, node): - # type: (nodes.Element) -> None + def depart_desc_type(self, node: Element) -> None: pass - def visit_desc_returns(self, node): - # type: (nodes.Element) -> None + def visit_desc_returns(self, node: Element) -> None: self.body.append(' -> ') - def depart_desc_returns(self, node): - # type: (nodes.Element) -> None + def depart_desc_returns(self, node: Element) -> None: pass - def visit_desc_parameterlist(self, node): - # type: (nodes.Element) -> None + def visit_desc_parameterlist(self, node: Element) -> None: self.body.append(' (') self.first_param = 1 - def depart_desc_parameterlist(self, node): - # type: (nodes.Element) -> None + def depart_desc_parameterlist(self, node: Element) -> None: self.body.append(')') - def visit_desc_parameter(self, node): - # type: (nodes.Element) -> None + def visit_desc_parameter(self, node: Element) -> None: if not self.first_param: self.body.append(', ') else: @@ -1637,16 +1439,13 @@ class TexinfoTranslator(SphinxTranslator): self.body.append(text) raise nodes.SkipNode - def visit_desc_optional(self, node): - # type: (nodes.Element) -> None + def visit_desc_optional(self, node: Element) -> None: self.body.append('[') - def depart_desc_optional(self, node): - # type: (nodes.Element) -> None + def depart_desc_optional(self, node: Element) -> None: self.body.append(']') - def visit_desc_annotation(self, node): - # type: (nodes.Element) -> None + def visit_desc_annotation(self, node: Element) -> None: # Try to avoid duplicating info already displayed by the deffn category. # e.g. # @deffn {Class} Foo @@ -1658,28 +1457,22 @@ class TexinfoTranslator(SphinxTranslator): txt in self.desc_type_name.split(): raise nodes.SkipNode - def depart_desc_annotation(self, node): - # type: (nodes.Element) -> None + def depart_desc_annotation(self, node: Element) -> None: pass - def visit_desc_content(self, node): - # type: (nodes.Element) -> None + def visit_desc_content(self, node: Element) -> None: pass - def depart_desc_content(self, node): - # type: (nodes.Element) -> None + def depart_desc_content(self, node: Element) -> None: pass - def visit_inline(self, node): - # type: (nodes.Element) -> None + def visit_inline(self, node: Element) -> None: pass - def depart_inline(self, node): - # type: (nodes.Element) -> None + def depart_inline(self, node: Element) -> None: pass - def visit_abbreviation(self, node): - # type: (nodes.Element) -> None + def visit_abbreviation(self, node: Element) -> None: abbr = node.astext() self.body.append('@abbr{') if node.hasattr('explanation') and abbr not in self.handled_abbrs: @@ -1688,57 +1481,44 @@ class TexinfoTranslator(SphinxTranslator): else: self.context.append('}') - def depart_abbreviation(self, node): - # type: (nodes.Element) -> None + def depart_abbreviation(self, node: Element) -> None: self.body.append(self.context.pop()) - def visit_manpage(self, node): - # type: (nodes.Element) -> None + def visit_manpage(self, node: Element) -> None: return self.visit_literal_emphasis(node) - def depart_manpage(self, node): - # type: (nodes.Element) -> None + def depart_manpage(self, node: Element) -> None: return self.depart_literal_emphasis(node) - def visit_download_reference(self, node): - # type: (nodes.Element) -> None + def visit_download_reference(self, node: Element) -> None: pass - def depart_download_reference(self, node): - # type: (nodes.Element) -> None + def depart_download_reference(self, node: Element) -> None: pass - def visit_hlist(self, node): - # type: (nodes.Element) -> None + def visit_hlist(self, node: Element) -> None: self.visit_bullet_list(node) - def depart_hlist(self, node): - # type: (nodes.Element) -> None + def depart_hlist(self, node: Element) -> None: self.depart_bullet_list(node) - def visit_hlistcol(self, node): - # type: (nodes.Element) -> None + def visit_hlistcol(self, node: Element) -> None: pass - def depart_hlistcol(self, node): - # type: (nodes.Element) -> None + def depart_hlistcol(self, node: Element) -> None: pass - def visit_pending_xref(self, node): - # type: (nodes.Element) -> None + def visit_pending_xref(self, node: Element) -> None: pass - def depart_pending_xref(self, node): - # type: (nodes.Element) -> None + def depart_pending_xref(self, node: Element) -> None: pass - def visit_math(self, node): - # type: (nodes.Element) -> None + def visit_math(self, node: Element) -> None: self.body.append('@math{' + self.escape_arg(node.astext()) + '}') raise nodes.SkipNode - def visit_math_block(self, node): - # type: (nodes.Element) -> None + def visit_math_block(self, node: Element) -> None: if node.get('label'): self.add_anchor(node['label'], node) self.body.append('\n\n@example\n%s\n@end example\n\n' % diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py index 89d999275..de17075fc 100644 --- a/sphinx/writers/text.py +++ b/sphinx/writers/text.py @@ -12,9 +12,11 @@ import os import re import textwrap from itertools import groupby, chain -from typing import Iterable, cast +from typing import Dict, List, Iterable, Optional, Set, Tuple, Union +from typing import cast from docutils import nodes, writers +from docutils.nodes import Element, Node, Text from docutils.utils import column_width from sphinx import addnodes @@ -23,8 +25,7 @@ from sphinx.util.docutils import SphinxTranslator if False: # For type annotation - from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union # NOQA - from sphinx.builders.text import TextBuilder # NOQA + from sphinx.builders.text import TextBuilder class Cell: @@ -201,8 +202,7 @@ class Table: out = [] self.rewrap() - def writesep(char="-", lineno=None): - # type: (str, Optional[int]) -> str + def writesep(char: str = "-", lineno: Optional[int] = None) -> str: """Called on the line *before* lineno. Called with no *lineno* for the last sep. """ @@ -262,8 +262,7 @@ class TextWrapper(textwrap.TextWrapper): r'[^\s\w]*\w+[a-zA-Z]-(?=\w+[a-zA-Z])|' # hyphenated words r'(?<=[\w\!\"\'\&\.\,\?])-{2,}(?=\w))') # em-dash - def _wrap_chunks(self, chunks): - # type: (List[str]) -> List[str] + def _wrap_chunks(self, chunks: List[str]) -> List[str]: """_wrap_chunks(chunks : [string]) -> [string] The original _wrap_chunks uses len() to calculate width. @@ -310,8 +309,7 @@ class TextWrapper(textwrap.TextWrapper): return lines - def _break_word(self, word, space_left): - # type: (str, int) -> Tuple[str, str] + def _break_word(self, word: str, space_left: int) -> Tuple[str, str]: """_break_word(word : string, space_left : int) -> (string, string) Break line by unicode width instead of len(word). @@ -323,15 +321,13 @@ class TextWrapper(textwrap.TextWrapper): return word[:i - 1], word[i - 1:] return word, '' - def _split(self, text): - # type: (str) -> List[str] + def _split(self, text: str) -> List[str]: """_split(text : string) -> [string] Override original method that only split by 'wordsep_re'. This '_split' split wide-characters into chunk by one character. """ - def split(t): - # type: (str) -> List[str] + def split(t: str) -> List[str]: return super(TextWrapper, self)._split(t) chunks = [] # type: List[str] for chunk in split(text): @@ -342,8 +338,8 @@ class TextWrapper(textwrap.TextWrapper): chunks.extend(list(g)) return chunks - def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width): - # type: (List[str], List[str], int, int) -> None + def _handle_long_word(self, reversed_chunks: List[str], cur_line: List[str], + cur_len: int, width: int) -> None: """_handle_long_word(chunks : [string], cur_line : [string], cur_len : int, width : int) @@ -364,8 +360,7 @@ MAXWIDTH = 70 STDINDENT = 3 -def my_wrap(text, width=MAXWIDTH, **kwargs): - # type: (str, int, Any) -> List[str] +def my_wrap(text: str, width: int = MAXWIDTH, **kwargs) -> List[str]: w = TextWrapper(width=width, **kwargs) return w.wrap(text) @@ -377,13 +372,11 @@ class TextWriter(writers.Writer): output = None # type: str - def __init__(self, builder): - # type: (TextBuilder) -> None + def __init__(self, builder: "TextBuilder") -> None: super().__init__() self.builder = builder - def translate(self): - # type: () -> None + def translate(self) -> None: visitor = self.builder.create_translator(self.document, self.builder) self.document.walkabout(visitor) self.output = cast(TextTranslator, visitor).body @@ -392,8 +385,7 @@ class TextWriter(writers.Writer): class TextTranslator(SphinxTranslator): builder = None # type: TextBuilder - def __init__(self, document, builder): - # type: (nodes.document, TextBuilder) -> None + def __init__(self, document: nodes.document, builder: "TextBuilder") -> None: super().__init__(document, builder) newlines = self.config.text_newlines @@ -413,25 +405,21 @@ class TextTranslator(SphinxTranslator): self.lineblocklevel = 0 self.table = None # type: Table - def add_text(self, text): - # type: (str) -> None + def add_text(self, text: str) -> None: self.states[-1].append((-1, text)) - def new_state(self, indent=STDINDENT): - # type: (int) -> None + def new_state(self, indent: int = STDINDENT) -> None: self.states.append([]) self.stateindent.append(indent) - def end_state(self, wrap=True, end=[''], first=None): - # type: (bool, List[str], str) -> None + def end_state(self, wrap: bool = True, end: List[str] = [''], first: str = None) -> None: content = self.states.pop() maxindent = sum(self.stateindent) indent = self.stateindent.pop() result = [] # type: List[Tuple[int, List[str]]] toformat = [] # type: List[str] - def do_format(): - # type: () -> None + def do_format() -> None: if not toformat: return if wrap: @@ -461,73 +449,59 @@ class TextTranslator(SphinxTranslator): result.extend(result_rest) self.states[-1].extend(result) - def visit_document(self, node): - # type: (nodes.Element) -> None + def visit_document(self, node: Element) -> None: self.new_state(0) - def depart_document(self, node): - # type: (nodes.Element) -> None + def depart_document(self, node: Element) -> None: self.end_state() self.body = self.nl.join(line and (' ' * indent + line) for indent, lines in self.states[0] for line in lines) # XXX header/footer? - def visit_section(self, node): - # type: (nodes.Element) -> None + def visit_section(self, node: Element) -> None: self._title_char = self.sectionchars[self.sectionlevel] self.sectionlevel += 1 - def depart_section(self, node): - # type: (nodes.Element) -> None + def depart_section(self, node: Element) -> None: self.sectionlevel -= 1 - def visit_topic(self, node): - # type: (nodes.Element) -> None + def visit_topic(self, node: Element) -> None: self.new_state(0) - def depart_topic(self, node): - # type: (nodes.Element) -> None + def depart_topic(self, node: Element) -> None: self.end_state() visit_sidebar = visit_topic depart_sidebar = depart_topic - def visit_rubric(self, node): - # type: (nodes.Element) -> None + def visit_rubric(self, node: Element) -> None: self.new_state(0) self.add_text('-[ ') - def depart_rubric(self, node): - # type: (nodes.Element) -> None + def depart_rubric(self, node: Element) -> None: self.add_text(' ]-') self.end_state() - def visit_compound(self, node): - # type: (nodes.Element) -> None + def visit_compound(self, node: Element) -> None: pass - def depart_compound(self, node): - # type: (nodes.Element) -> None + def depart_compound(self, node: Element) -> None: pass - def visit_glossary(self, node): - # type: (nodes.Element) -> None + def visit_glossary(self, node: Element) -> None: pass - def depart_glossary(self, node): - # type: (nodes.Element) -> None + def depart_glossary(self, node: Element) -> None: pass - def visit_title(self, node): - # type: (nodes.Element) -> None + def visit_title(self, node: Element) -> None: if isinstance(node.parent, nodes.Admonition): self.add_text(node.astext() + ': ') raise nodes.SkipNode self.new_state(0) - def get_section_number_string(self, node): - # type: (nodes.Element) -> str + def get_section_number_string(self, node: Element) -> str: if isinstance(node.parent, nodes.section): anchorname = '#' + node.parent['ids'][0] numbers = self.builder.secnumbers.get(anchorname) @@ -537,8 +511,7 @@ class TextTranslator(SphinxTranslator): return '.'.join(map(str, numbers)) + self.secnumber_suffix return '' - def depart_title(self, node): - # type: (nodes.Element) -> None + def depart_title(self, node: Element) -> None: if isinstance(node.parent, nodes.section): char = self._title_char else: @@ -554,90 +527,69 @@ class TextTranslator(SphinxTranslator): title.pop(0) self.states[-1].append((0, title)) - def visit_subtitle(self, node): - # type: (nodes.Element) -> None + def visit_subtitle(self, node: Element) -> None: pass - def depart_subtitle(self, node): - # type: (nodes.Element) -> None + def depart_subtitle(self, node: Element) -> None: pass - def visit_attribution(self, node): - # type: (nodes.Element) -> None + def visit_attribution(self, node: Element) -> None: self.add_text('-- ') - def depart_attribution(self, node): - # type: (nodes.Element) -> None + def depart_attribution(self, node: Element) -> None: pass - def visit_desc(self, node): - # type: (nodes.Element) -> None + def visit_desc(self, node: Element) -> None: pass - def depart_desc(self, node): - # type: (nodes.Element) -> None + def depart_desc(self, node: Element) -> None: pass - def visit_desc_signature(self, node): - # type: (nodes.Element) -> None + def visit_desc_signature(self, node: Element) -> None: self.new_state(0) - def depart_desc_signature(self, node): - # type: (nodes.Element) -> None + def depart_desc_signature(self, node: Element) -> None: # XXX: wrap signatures in a way that makes sense self.end_state(wrap=False, end=None) - def visit_desc_signature_line(self, node): - # type: (nodes.Element) -> None + def visit_desc_signature_line(self, node: Element) -> None: pass - def depart_desc_signature_line(self, node): - # type: (nodes.Element) -> None + def depart_desc_signature_line(self, node: Element) -> None: self.add_text('\n') - def visit_desc_name(self, node): - # type: (nodes.Element) -> None + def visit_desc_name(self, node: Element) -> None: pass - def depart_desc_name(self, node): - # type: (nodes.Element) -> None + def depart_desc_name(self, node: Element) -> None: pass - def visit_desc_addname(self, node): - # type: (nodes.Element) -> None + def visit_desc_addname(self, node: Element) -> None: pass - def depart_desc_addname(self, node): - # type: (nodes.Element) -> None + def depart_desc_addname(self, node: Element) -> None: pass - def visit_desc_type(self, node): - # type: (nodes.Element) -> None + def visit_desc_type(self, node: Element) -> None: pass - def depart_desc_type(self, node): - # type: (nodes.Element) -> None + def depart_desc_type(self, node: Element) -> None: pass - def visit_desc_returns(self, node): - # type: (nodes.Element) -> None + def visit_desc_returns(self, node: Element) -> None: self.add_text(' -> ') - def depart_desc_returns(self, node): - # type: (nodes.Element) -> None + def depart_desc_returns(self, node: Element) -> None: pass - def visit_desc_parameterlist(self, node): - # type: (nodes.Element) -> None + def visit_desc_parameterlist(self, node: Element) -> None: self.add_text('(') self.first_param = 1 - def depart_desc_parameterlist(self, node): - # type: (nodes.Element) -> None + def depart_desc_parameterlist(self, node: Element) -> None: self.add_text(')') - def visit_desc_parameter(self, node): - # type: (nodes.Element) -> None + def visit_desc_parameter(self, node: Element) -> None: if not self.first_param: self.add_text(', ') else: @@ -645,49 +597,38 @@ class TextTranslator(SphinxTranslator): self.add_text(node.astext()) raise nodes.SkipNode - def visit_desc_optional(self, node): - # type: (nodes.Element) -> None + def visit_desc_optional(self, node: Element) -> None: self.add_text('[') - def depart_desc_optional(self, node): - # type: (nodes.Element) -> None + def depart_desc_optional(self, node: Element) -> None: self.add_text(']') - def visit_desc_annotation(self, node): - # type: (nodes.Element) -> None + def visit_desc_annotation(self, node: Element) -> None: pass - def depart_desc_annotation(self, node): - # type: (nodes.Element) -> None + def depart_desc_annotation(self, node: Element) -> None: pass - def visit_desc_content(self, node): - # type: (nodes.Element) -> None + def visit_desc_content(self, node: Element) -> None: self.new_state() self.add_text(self.nl) - def depart_desc_content(self, node): - # type: (nodes.Element) -> None + def depart_desc_content(self, node: Element) -> None: self.end_state() - def visit_figure(self, node): - # type: (nodes.Element) -> None + def visit_figure(self, node: Element) -> None: self.new_state() - def depart_figure(self, node): - # type: (nodes.Element) -> None + def depart_figure(self, node: Element) -> None: self.end_state() - def visit_caption(self, node): - # type: (nodes.Element) -> None + def visit_caption(self, node: Element) -> None: pass - def depart_caption(self, node): - # type: (nodes.Element) -> None + def depart_caption(self, node: Element) -> None: pass - def visit_productionlist(self, node): - # type: (nodes.Element) -> None + def visit_productionlist(self, node: Element) -> None: self.new_state() names = [] productionlist = cast(Iterable[addnodes.production], node) @@ -705,173 +646,137 @@ class TextTranslator(SphinxTranslator): self.end_state(wrap=False) raise nodes.SkipNode - def visit_footnote(self, node): - # type: (nodes.Element) -> None + def visit_footnote(self, node: Element) -> None: label = cast(nodes.label, node[0]) self._footnote = label.astext().strip() self.new_state(len(self._footnote) + 3) - def depart_footnote(self, node): - # type: (nodes.Element) -> None + def depart_footnote(self, node: Element) -> None: self.end_state(first='[%s] ' % self._footnote) - def visit_citation(self, node): - # type: (nodes.Element) -> None + def visit_citation(self, node: Element) -> None: if len(node) and isinstance(node[0], nodes.label): self._citlabel = node[0].astext() else: self._citlabel = '' self.new_state(len(self._citlabel) + 3) - def depart_citation(self, node): - # type: (nodes.Element) -> None + def depart_citation(self, node: Element) -> None: self.end_state(first='[%s] ' % self._citlabel) - def visit_label(self, node): - # type: (nodes.Element) -> None + def visit_label(self, node: Element) -> None: raise nodes.SkipNode - def visit_legend(self, node): - # type: (nodes.Element) -> None + def visit_legend(self, node: Element) -> None: pass - def depart_legend(self, node): - # type: (nodes.Element) -> None + def depart_legend(self, node: Element) -> None: pass # XXX: option list could use some better styling - def visit_option_list(self, node): - # type: (nodes.Element) -> None + def visit_option_list(self, node: Element) -> None: pass - def depart_option_list(self, node): - # type: (nodes.Element) -> None + def depart_option_list(self, node: Element) -> None: pass - def visit_option_list_item(self, node): - # type: (nodes.Element) -> None + def visit_option_list_item(self, node: Element) -> None: self.new_state(0) - def depart_option_list_item(self, node): - # type: (nodes.Element) -> None + def depart_option_list_item(self, node: Element) -> None: self.end_state() - def visit_option_group(self, node): - # type: (nodes.Element) -> None + def visit_option_group(self, node: Element) -> None: self._firstoption = True - def depart_option_group(self, node): - # type: (nodes.Element) -> None + def depart_option_group(self, node: Element) -> None: self.add_text(' ') - def visit_option(self, node): - # type: (nodes.Element) -> None + def visit_option(self, node: Element) -> None: if self._firstoption: self._firstoption = False else: self.add_text(', ') - def depart_option(self, node): - # type: (nodes.Element) -> None + def depart_option(self, node: Element) -> None: pass - def visit_option_string(self, node): - # type: (nodes.Element) -> None + def visit_option_string(self, node: Element) -> None: pass - def depart_option_string(self, node): - # type: (nodes.Element) -> None + def depart_option_string(self, node: Element) -> None: pass - def visit_option_argument(self, node): - # type: (nodes.Element) -> None + def visit_option_argument(self, node: Element) -> None: self.add_text(node['delimiter']) - def depart_option_argument(self, node): - # type: (nodes.Element) -> None + def depart_option_argument(self, node: Element) -> None: pass - def visit_description(self, node): - # type: (nodes.Element) -> None + def visit_description(self, node: Element) -> None: pass - def depart_description(self, node): - # type: (nodes.Element) -> None + def depart_description(self, node: Element) -> None: pass - def visit_tabular_col_spec(self, node): - # type: (nodes.Element) -> None + def visit_tabular_col_spec(self, node: Element) -> None: raise nodes.SkipNode - def visit_colspec(self, node): - # type: (nodes.Element) -> None + def visit_colspec(self, node: Element) -> None: self.table.colwidth.append(node["colwidth"]) raise nodes.SkipNode - def visit_tgroup(self, node): - # type: (nodes.Element) -> None + def visit_tgroup(self, node: Element) -> None: pass - def depart_tgroup(self, node): - # type: (nodes.Element) -> None + def depart_tgroup(self, node: Element) -> None: pass - def visit_thead(self, node): - # type: (nodes.Element) -> None + def visit_thead(self, node: Element) -> None: pass - def depart_thead(self, node): - # type: (nodes.Element) -> None + def depart_thead(self, node: Element) -> None: pass - def visit_tbody(self, node): - # type: (nodes.Element) -> None + def visit_tbody(self, node: Element) -> None: self.table.set_separator() - def depart_tbody(self, node): - # type: (nodes.Element) -> None + def depart_tbody(self, node: Element) -> None: pass - def visit_row(self, node): - # type: (nodes.Element) -> None + def visit_row(self, node: Element) -> None: if self.table.lines: self.table.add_row() - def depart_row(self, node): - # type: (nodes.Element) -> None + def depart_row(self, node: Element) -> None: pass - def visit_entry(self, node): - # type: (nodes.Element) -> None + def visit_entry(self, node: Element) -> None: self.entry = Cell( rowspan=node.get("morerows", 0) + 1, colspan=node.get("morecols", 0) + 1 ) self.new_state(0) - def depart_entry(self, node): - # type: (nodes.Element) -> None + def depart_entry(self, node: Element) -> None: text = self.nl.join(self.nl.join(x[1]) for x in self.states.pop()) self.stateindent.pop() self.entry.text = text self.table.add_cell(self.entry) self.entry = None - def visit_table(self, node): - # type: (nodes.Element) -> None + def visit_table(self, node: Element) -> None: if self.table: raise NotImplementedError('Nested tables are not supported.') self.new_state(0) self.table = Table() - def depart_table(self, node): - # type: (nodes.Element) -> None + def depart_table(self, node: Element) -> None: self.add_text(str(self.table)) self.table = None self.end_state(wrap=False) - def visit_acks(self, node): - # type: (nodes.Element) -> None + def visit_acks(self, node: Element) -> None: bullet_list = cast(nodes.bullet_list, node[0]) list_items = cast(Iterable[nodes.list_item], bullet_list) self.new_state(0) @@ -879,47 +784,38 @@ class TextTranslator(SphinxTranslator): self.end_state() raise nodes.SkipNode - def visit_image(self, node): - # type: (nodes.Element) -> None + def visit_image(self, node: Element) -> None: if 'alt' in node.attributes: self.add_text(_('[image: %s]') % node['alt']) self.add_text(_('[image]')) raise nodes.SkipNode - def visit_transition(self, node): - # type: (nodes.Element) -> None + def visit_transition(self, node: Element) -> None: indent = sum(self.stateindent) self.new_state(0) self.add_text('=' * (MAXWIDTH - indent)) self.end_state() raise nodes.SkipNode - def visit_bullet_list(self, node): - # type: (nodes.Element) -> None + def visit_bullet_list(self, node: Element) -> None: self.list_counter.append(-1) - def depart_bullet_list(self, node): - # type: (nodes.Element) -> None + def depart_bullet_list(self, node: Element) -> None: self.list_counter.pop() - def visit_enumerated_list(self, node): - # type: (nodes.Element) -> None + def visit_enumerated_list(self, node: Element) -> None: self.list_counter.append(node.get('start', 1) - 1) - def depart_enumerated_list(self, node): - # type: (nodes.Element) -> None + def depart_enumerated_list(self, node: Element) -> None: self.list_counter.pop() - def visit_definition_list(self, node): - # type: (nodes.Element) -> None + def visit_definition_list(self, node: Element) -> None: self.list_counter.append(-2) - def depart_definition_list(self, node): - # type: (nodes.Element) -> None + def depart_definition_list(self, node: Element) -> None: self.list_counter.pop() - def visit_list_item(self, node): - # type: (nodes.Element) -> None + def visit_list_item(self, node: Element) -> None: if self.list_counter[-1] == -1: # bullet list self.new_state(2) @@ -931,8 +827,7 @@ class TextTranslator(SphinxTranslator): self.list_counter[-1] += 1 self.new_state(len(str(self.list_counter[-1])) + 2) - def depart_list_item(self, node): - # type: (nodes.Element) -> None + def depart_list_item(self, node: Element) -> None: if self.list_counter[-1] == -1: self.end_state(first='* ') elif self.list_counter[-1] == -2: @@ -940,115 +835,89 @@ class TextTranslator(SphinxTranslator): else: self.end_state(first='%s. ' % self.list_counter[-1]) - def visit_definition_list_item(self, node): - # type: (nodes.Element) -> None + def visit_definition_list_item(self, node: Element) -> None: self._classifier_count_in_li = len(node.traverse(nodes.classifier)) - def depart_definition_list_item(self, node): - # type: (nodes.Element) -> None + def depart_definition_list_item(self, node: Element) -> None: pass - def visit_term(self, node): - # type: (nodes.Element) -> None + def visit_term(self, node: Element) -> None: self.new_state(0) - def depart_term(self, node): - # type: (nodes.Element) -> None + def depart_term(self, node: Element) -> None: if not self._classifier_count_in_li: self.end_state(end=None) - def visit_classifier(self, node): - # type: (nodes.Element) -> None + def visit_classifier(self, node: Element) -> None: self.add_text(' : ') - def depart_classifier(self, node): - # type: (nodes.Element) -> None + def depart_classifier(self, node: Element) -> None: self._classifier_count_in_li -= 1 if not self._classifier_count_in_li: self.end_state(end=None) - def visit_definition(self, node): - # type: (nodes.Element) -> None + def visit_definition(self, node: Element) -> None: self.new_state() - def depart_definition(self, node): - # type: (nodes.Element) -> None + def depart_definition(self, node: Element) -> None: self.end_state() - def visit_field_list(self, node): - # type: (nodes.Element) -> None + def visit_field_list(self, node: Element) -> None: pass - def depart_field_list(self, node): - # type: (nodes.Element) -> None + def depart_field_list(self, node: Element) -> None: pass - def visit_field(self, node): - # type: (nodes.Element) -> None + def visit_field(self, node: Element) -> None: pass - def depart_field(self, node): - # type: (nodes.Element) -> None + def depart_field(self, node: Element) -> None: pass - def visit_field_name(self, node): - # type: (nodes.Element) -> None + def visit_field_name(self, node: Element) -> None: self.new_state(0) - def depart_field_name(self, node): - # type: (nodes.Element) -> None + def depart_field_name(self, node: Element) -> None: self.add_text(':') self.end_state(end=None) - def visit_field_body(self, node): - # type: (nodes.Element) -> None + def visit_field_body(self, node: Element) -> None: self.new_state() - def depart_field_body(self, node): - # type: (nodes.Element) -> None + def depart_field_body(self, node: Element) -> None: self.end_state() - def visit_centered(self, node): - # type: (nodes.Element) -> None + def visit_centered(self, node: Element) -> None: pass - def depart_centered(self, node): - # type: (nodes.Element) -> None + def depart_centered(self, node: Element) -> None: pass - def visit_hlist(self, node): - # type: (nodes.Element) -> None + def visit_hlist(self, node: Element) -> None: pass - def depart_hlist(self, node): - # type: (nodes.Element) -> None + def depart_hlist(self, node: Element) -> None: pass - def visit_hlistcol(self, node): - # type: (nodes.Element) -> None + def visit_hlistcol(self, node: Element) -> None: pass - def depart_hlistcol(self, node): - # type: (nodes.Element) -> None + def depart_hlistcol(self, node: Element) -> None: pass - def visit_admonition(self, node): - # type: (nodes.Element) -> None + def visit_admonition(self, node: Element) -> None: self.new_state(0) - def depart_admonition(self, node): - # type: (nodes.Element) -> None + def depart_admonition(self, node: Element) -> None: self.end_state() - def _visit_admonition(self, node): - # type: (nodes.Element) -> None + def _visit_admonition(self, node: Element) -> None: self.new_state(2) if isinstance(node.children[0], nodes.Sequential): self.add_text(self.nl) - def _depart_admonition(self, node): - # type: (nodes.Element) -> None + def _depart_admonition(self, node: Element) -> None: label = admonitionlabels[node.tagname] self.end_state(first=label + ': ') @@ -1073,296 +942,229 @@ class TextTranslator(SphinxTranslator): visit_seealso = _visit_admonition depart_seealso = _depart_admonition - def visit_versionmodified(self, node): - # type: (nodes.Element) -> None + def visit_versionmodified(self, node: Element) -> None: self.new_state(0) - def depart_versionmodified(self, node): - # type: (nodes.Element) -> None + def depart_versionmodified(self, node: Element) -> None: self.end_state() - def visit_literal_block(self, node): - # type: (nodes.Element) -> None + def visit_literal_block(self, node: Element) -> None: self.new_state() - def depart_literal_block(self, node): - # type: (nodes.Element) -> None + def depart_literal_block(self, node: Element) -> None: self.end_state(wrap=False) - def visit_doctest_block(self, node): - # type: (nodes.Element) -> None + def visit_doctest_block(self, node: Element) -> None: self.new_state(0) - def depart_doctest_block(self, node): - # type: (nodes.Element) -> None + def depart_doctest_block(self, node: Element) -> None: self.end_state(wrap=False) - def visit_line_block(self, node): - # type: (nodes.Element) -> None + def visit_line_block(self, node: Element) -> None: self.new_state() self.lineblocklevel += 1 - def depart_line_block(self, node): - # type: (nodes.Element) -> None + def depart_line_block(self, node: Element) -> None: self.lineblocklevel -= 1 self.end_state(wrap=False, end=None) if not self.lineblocklevel: self.add_text('\n') - def visit_line(self, node): - # type: (nodes.Element) -> None + def visit_line(self, node: Element) -> None: pass - def depart_line(self, node): - # type: (nodes.Element) -> None + def depart_line(self, node: Element) -> None: self.add_text('\n') - def visit_block_quote(self, node): - # type: (nodes.Element) -> None + def visit_block_quote(self, node: Element) -> None: self.new_state() - def depart_block_quote(self, node): - # type: (nodes.Element) -> None + def depart_block_quote(self, node: Element) -> None: self.end_state() - def visit_compact_paragraph(self, node): - # type: (nodes.Element) -> None + def visit_compact_paragraph(self, node: Element) -> None: pass - def depart_compact_paragraph(self, node): - # type: (nodes.Element) -> None + def depart_compact_paragraph(self, node: Element) -> None: pass - def visit_paragraph(self, node): - # type: (nodes.Element) -> None + def visit_paragraph(self, node: Element) -> None: if not isinstance(node.parent, nodes.Admonition) or \ isinstance(node.parent, addnodes.seealso): self.new_state(0) - def depart_paragraph(self, node): - # type: (nodes.Element) -> None + def depart_paragraph(self, node: Element) -> None: if not isinstance(node.parent, nodes.Admonition) or \ isinstance(node.parent, addnodes.seealso): self.end_state() - def visit_target(self, node): - # type: (nodes.Element) -> None + def visit_target(self, node: Element) -> None: raise nodes.SkipNode - def visit_index(self, node): - # type: (nodes.Element) -> None + def visit_index(self, node: Element) -> None: raise nodes.SkipNode - def visit_toctree(self, node): - # type: (nodes.Element) -> None + def visit_toctree(self, node: Element) -> None: raise nodes.SkipNode - def visit_pending_xref(self, node): - # type: (nodes.Element) -> None + def visit_pending_xref(self, node: Element) -> None: pass - def depart_pending_xref(self, node): - # type: (nodes.Element) -> None + def depart_pending_xref(self, node: Element) -> None: pass - def visit_reference(self, node): - # type: (nodes.Element) -> None + def visit_reference(self, node: Element) -> None: if self.add_secnumbers: numbers = node.get("secnumber") if numbers is not None: self.add_text('.'.join(map(str, numbers)) + self.secnumber_suffix) - def depart_reference(self, node): - # type: (nodes.Element) -> None + def depart_reference(self, node: Element) -> None: pass - def visit_number_reference(self, node): - # type: (nodes.Element) -> None + def visit_number_reference(self, node: Element) -> None: text = nodes.Text(node.get('title', '#')) self.visit_Text(text) raise nodes.SkipNode - def visit_download_reference(self, node): - # type: (nodes.Element) -> None + def visit_download_reference(self, node: Element) -> None: pass - def depart_download_reference(self, node): - # type: (nodes.Element) -> None + def depart_download_reference(self, node: Element) -> None: pass - def visit_emphasis(self, node): - # type: (nodes.Element) -> None + def visit_emphasis(self, node: Element) -> None: self.add_text('*') - def depart_emphasis(self, node): - # type: (nodes.Element) -> None + def depart_emphasis(self, node: Element) -> None: self.add_text('*') - def visit_literal_emphasis(self, node): - # type: (nodes.Element) -> None + def visit_literal_emphasis(self, node: Element) -> None: self.add_text('*') - def depart_literal_emphasis(self, node): - # type: (nodes.Element) -> None + def depart_literal_emphasis(self, node: Element) -> None: self.add_text('*') - def visit_strong(self, node): - # type: (nodes.Element) -> None + def visit_strong(self, node: Element) -> None: self.add_text('**') - def depart_strong(self, node): - # type: (nodes.Element) -> None + def depart_strong(self, node: Element) -> None: self.add_text('**') - def visit_literal_strong(self, node): - # type: (nodes.Element) -> None + def visit_literal_strong(self, node: Element) -> None: self.add_text('**') - def depart_literal_strong(self, node): - # type: (nodes.Element) -> None + def depart_literal_strong(self, node: Element) -> None: self.add_text('**') - def visit_abbreviation(self, node): - # type: (nodes.Element) -> None + def visit_abbreviation(self, node: Element) -> None: self.add_text('') - def depart_abbreviation(self, node): - # type: (nodes.Element) -> None + def depart_abbreviation(self, node: Element) -> None: if node.hasattr('explanation'): self.add_text(' (%s)' % node['explanation']) - def visit_manpage(self, node): - # type: (nodes.Element) -> None + def visit_manpage(self, node: Element) -> None: return self.visit_literal_emphasis(node) - def depart_manpage(self, node): - # type: (nodes.Element) -> None + def depart_manpage(self, node: Element) -> None: return self.depart_literal_emphasis(node) - def visit_title_reference(self, node): - # type: (nodes.Element) -> None + def visit_title_reference(self, node: Element) -> None: self.add_text('*') - def depart_title_reference(self, node): - # type: (nodes.Element) -> None + def depart_title_reference(self, node: Element) -> None: self.add_text('*') - def visit_literal(self, node): - # type: (nodes.Element) -> None + def visit_literal(self, node: Element) -> None: self.add_text('"') - def depart_literal(self, node): - # type: (nodes.Element) -> None + def depart_literal(self, node: Element) -> None: self.add_text('"') - def visit_subscript(self, node): - # type: (nodes.Element) -> None + def visit_subscript(self, node: Element) -> None: self.add_text('_') - def depart_subscript(self, node): - # type: (nodes.Element) -> None + def depart_subscript(self, node: Element) -> None: pass - def visit_superscript(self, node): - # type: (nodes.Element) -> None + def visit_superscript(self, node: Element) -> None: self.add_text('^') - def depart_superscript(self, node): - # type: (nodes.Element) -> None + def depart_superscript(self, node: Element) -> None: pass - def visit_footnote_reference(self, node): - # type: (nodes.Element) -> None + def visit_footnote_reference(self, node: Element) -> None: self.add_text('[%s]' % node.astext()) raise nodes.SkipNode - def visit_citation_reference(self, node): - # type: (nodes.Element) -> None + def visit_citation_reference(self, node: Element) -> None: self.add_text('[%s]' % node.astext()) raise nodes.SkipNode - def visit_Text(self, node): - # type: (nodes.Text) -> None + def visit_Text(self, node: Text) -> None: self.add_text(node.astext()) - def depart_Text(self, node): - # type: (nodes.Text) -> None + def depart_Text(self, node: Text) -> None: pass - def visit_generated(self, node): - # type: (nodes.Element) -> None + def visit_generated(self, node: Element) -> None: pass - def depart_generated(self, node): - # type: (nodes.Element) -> None + def depart_generated(self, node: Element) -> None: pass - def visit_inline(self, node): - # type: (nodes.Element) -> None + def visit_inline(self, node: Element) -> None: if 'xref' in node['classes'] or 'term' in node['classes']: self.add_text('*') - def depart_inline(self, node): - # type: (nodes.Element) -> None + def depart_inline(self, node: Element) -> None: if 'xref' in node['classes'] or 'term' in node['classes']: self.add_text('*') - def visit_container(self, node): - # type: (nodes.Element) -> None + def visit_container(self, node: Element) -> None: pass - def depart_container(self, node): - # type: (nodes.Element) -> None + def depart_container(self, node: Element) -> None: pass - def visit_problematic(self, node): - # type: (nodes.Element) -> None + def visit_problematic(self, node: Element) -> None: self.add_text('>>') - def depart_problematic(self, node): - # type: (nodes.Element) -> None + def depart_problematic(self, node: Element) -> None: self.add_text('<<') - def visit_system_message(self, node): - # type: (nodes.Element) -> None + def visit_system_message(self, node: Element) -> None: self.new_state(0) self.add_text('' % node.astext()) self.end_state() raise nodes.SkipNode - def visit_comment(self, node): - # type: (nodes.Element) -> None + def visit_comment(self, node: Element) -> None: raise nodes.SkipNode - def visit_meta(self, node): - # type: (nodes.Element) -> None + def visit_meta(self, node: Element) -> None: # only valid for HTML raise nodes.SkipNode - def visit_raw(self, node): - # type: (nodes.Element) -> None + def visit_raw(self, node: Element) -> None: if 'text' in node.get('format', '').split(): self.new_state(0) self.add_text(node.astext()) self.end_state(wrap = False) raise nodes.SkipNode - def visit_math(self, node): - # type: (nodes.Element) -> None + def visit_math(self, node: Element) -> None: pass - def depart_math(self, node): - # type: (nodes.Element) -> None + def depart_math(self, node: Element) -> None: pass - def visit_math_block(self, node): - # type: (nodes.Element) -> None + def visit_math_block(self, node: Element) -> None: self.new_state() - def depart_math_block(self, node): - # type: (nodes.Element) -> None + def depart_math_block(self, node: Element) -> None: self.end_state() - def unknown_visit(self, node): - # type: (nodes.Node) -> None + def unknown_visit(self, node: Node) -> None: raise NotImplementedError('Unknown node: ' + node.__class__.__name__) diff --git a/sphinx/writers/xml.py b/sphinx/writers/xml.py index 8f6d393e2..64da854e5 100644 --- a/sphinx/writers/xml.py +++ b/sphinx/writers/xml.py @@ -10,22 +10,16 @@ from docutils.writers.docutils_xml import Writer as BaseXMLWriter -if False: - # For type annotation - from typing import Any # NOQA - from sphinx.builders import Builder # NOQA +from sphinx.builders import Builder class XMLWriter(BaseXMLWriter): - - def __init__(self, builder): - # type: (Builder) -> None + def __init__(self, builder: Builder) -> None: super().__init__() self.builder = builder self.translator_class = self.builder.get_translator_class() - def translate(self, *args, **kwargs): - # type: (Any, Any) -> None + def translate(self, *args, **kwargs) -> None: self.document.settings.newlines = \ self.document.settings.indents = \ self.builder.env.config.xml_pretty @@ -45,16 +39,13 @@ class PseudoXMLWriter(BaseXMLWriter): output = None """Final translated form of `document`.""" - def __init__(self, builder): - # type: (Builder) -> None + def __init__(self, builder: Builder) -> None: super().__init__() self.builder = builder - def translate(self): - # type: () -> None + def translate(self) -> None: self.output = self.document.pformat() - def supports(self, format): - # type: (str) -> bool + def supports(self, format: str) -> bool: """This writer supports all format-specific elements.""" return True diff --git a/tests/roots/test-ext-autosummary-skip-member/conf.py b/tests/roots/test-ext-autosummary-skip-member/conf.py new file mode 100644 index 000000000..7c8f0e9cf --- /dev/null +++ b/tests/roots/test-ext-autosummary-skip-member/conf.py @@ -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) diff --git a/tests/roots/test-ext-autosummary-skip-member/index.rst b/tests/roots/test-ext-autosummary-skip-member/index.rst new file mode 100644 index 000000000..c376a13c6 --- /dev/null +++ b/tests/roots/test-ext-autosummary-skip-member/index.rst @@ -0,0 +1,4 @@ +.. autosummary:: + :toctree: generate + + target.Foo diff --git a/tests/roots/test-ext-autosummary-skip-member/target.py b/tests/roots/test-ext-autosummary-skip-member/target.py new file mode 100644 index 000000000..fdf557eee --- /dev/null +++ b/tests/roots/test-ext-autosummary-skip-member/target.py @@ -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 diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 0ebcd0b62..8c023b8e4 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -315,7 +315,7 @@ def test_numref_with_prefix2(app, status, warning): assert ('\\hyperref[\\detokenize{baz:table22}]' '{Table:\\ref{\\detokenize{baz:table22}}}') in result assert ('\\hyperref[\\detokenize{index:code-1}]{Code-\\ref{\\detokenize{index:code-1}} ' - '\\textbar{} }') in result + '| }') in result assert ('\\hyperref[\\detokenize{baz:code22}]' '{Code-\\ref{\\detokenize{baz:code22}}}') in result assert ('\\hyperref[\\detokenize{foo:foo}]' @@ -1414,6 +1414,7 @@ def test_default_latex_documents(): 'project': 'STASI™ Documentation', 'author': "Wolfgang Schäuble & G'Beckstein."}) config.init_values() + config.add('latex_engine', None, True, None) expected = [('index', 'stasi.tex', 'STASI™ Documentation', r"Wolfgang Schäuble \& G'Beckstein.\@{}", 'manual')] assert default_latex_documents(config) == expected diff --git a/tests/test_build_linkcheck.py b/tests/test_build_linkcheck.py index 4bf47a962..22866b27b 100644 --- a/tests/test_build_linkcheck.py +++ b/tests/test_build_linkcheck.py @@ -8,6 +8,7 @@ :license: BSD, see LICENSE for details. """ +from unittest import mock import pytest @@ -47,3 +48,25 @@ def test_anchors_ignored(app, status, warning): # expect all ok when excluding #top 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'] diff --git a/tests/test_ext_autosummary.py b/tests/test_ext_autosummary.py index 9e5654ac4..1ef2c6a9d 100644 --- a/tests/test_ext_autosummary.py +++ b/tests/test_ext_autosummary.py @@ -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(): TEST = """ () :: () @@ -335,6 +340,15 @@ def test_generate_autosummary_docs_property(app): ".. 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', confoverrides={'autosummary_generate': []}) def test_empty_autosummary_generate(app, status, warning):