diff options
Diffstat (limited to 'sphinx/writers/latex.py')
| -rw-r--r-- | sphinx/writers/latex.py | 1121 |
1 files changed, 550 insertions, 571 deletions
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 3ada12c4d..ec5fcde09 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ sphinx.writers.latex ~~~~~~~~~~~~~~~~~~~~ @@ -17,18 +16,20 @@ import sys import warnings from collections import defaultdict from os import path +from typing import Iterable, cast from docutils import nodes, writers -from docutils.writers.latex2e import Babel -from six import itervalues, text_type from sphinx import addnodes from sphinx import highlighting -from sphinx.deprecation import RemovedInSphinx30Warning +from sphinx.deprecation import ( + RemovedInSphinx30Warning, RemovedInSphinx40Warning, deprecated_alias +) +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.i18n import format_date +from sphinx.util.docutils import SphinxTranslator from sphinx.util.nodes import clean_astext from sphinx.util.template import LaTeXRenderer from sphinx.util.texescape import tex_escape_map, tex_replace_map @@ -37,12 +38,16 @@ try: from docutils.utils.roman import toRoman except ImportError: # In Debain/Ubuntu, roman package is provided as roman, not as docutils.utils.roman - from roman import toRoman + from roman import toRoman # type: ignore if False: # For type annotation from typing import Any, Callable, Dict, Iterator, List, Pattern, Tuple, Set, Union # NOQA - from sphinx.builder import Builder # 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 logger = logging.getLogger(__name__) @@ -63,7 +68,55 @@ ENUMERATE_LIST_STYLE = defaultdict(lambda: r'\arabic', 'upperalpha': r'\Alph', 'lowerroman': r'\roman', 'upperroman': r'\Roman', - }) # type: Dict[unicode, unicode] + }) +PDFLATEX_DEFAULT_FONTPKG = r''' +\usepackage{times} +\expandafter\ifx\csname T@LGR\endcsname\relax +\else +% LGR was declared as font encoding + \substitutefont{LGR}{\rmdefault}{cmr} + \substitutefont{LGR}{\sfdefault}{cmss} + \substitutefont{LGR}{\ttdefault}{cmtt} +\fi +\expandafter\ifx\csname T@X2\endcsname\relax + \expandafter\ifx\csname T@T2A\endcsname\relax + \else + % T2A was declared as font encoding + \substitutefont{T2A}{\rmdefault}{cmr} + \substitutefont{T2A}{\sfdefault}{cmss} + \substitutefont{T2A}{\ttdefault}{cmtt} + \fi +\else +% X2 was declared as font encoding + \substitutefont{X2}{\rmdefault}{cmr} + \substitutefont{X2}{\sfdefault}{cmss} + \substitutefont{X2}{\ttdefault}{cmtt} +\fi +''' +XELATEX_DEFAULT_FONTPKG = r''' +\setmainfont{FreeSerif}[ + Extension = .otf, + UprightFont = *, + ItalicFont = *Italic, + BoldFont = *Bold, + BoldItalicFont = *BoldItalic +] +\setsansfont{FreeSans}[ + Extension = .otf, + UprightFont = *, + ItalicFont = *Oblique, + BoldFont = *Bold, + BoldItalicFont = *BoldOblique, +] +\setmonofont{FreeMono}[ + Extension = .otf, + UprightFont = *, + ItalicFont = *Oblique, + BoldFont = *Bold, + BoldItalicFont = *BoldOblique, +] +''' +LUALATEX_DEFAULT_FONTPKG = XELATEX_DEFAULT_FONTPKG DEFAULT_SETTINGS = { 'latex_engine': 'pdflatex', @@ -86,7 +139,10 @@ DEFAULT_SETTINGS = { 'multilingual': '', 'babel': '\\usepackage{babel}', 'polyglossia': '', - 'fontpkg': '\\usepackage{times}', + 'fontpkg': PDFLATEX_DEFAULT_FONTPKG, + 'substitutefont': '', + 'textcyrillic': '', + 'textgreek': '\\usepackage{textalpha}', 'fncychap': '\\usepackage[Bjarne]{fncychap}', 'hyperref': ('% Include hyperref last.\n' '\\usepackage{hyperref}\n' @@ -94,15 +150,11 @@ DEFAULT_SETTINGS = { '\\usepackage{hypcap}% it must be loaded after hyperref.\n' '% Set up styles of URL: it should be placed after hyperref.\n' '\\urlstyle{same}'), - 'usepackages': '', - 'numfig_format': '', 'contentsname': '', 'preamble': '', 'title': '', - 'date': '', 'release': '', 'author': '', - 'logo': '\\vbox{}', 'releasename': '', 'makeindex': '\\makeindex', 'shorthandoff': '', @@ -114,30 +166,24 @@ DEFAULT_SETTINGS = { 'figure_align': 'htbp', 'tocdepth': '', 'secnumdepth': '', - 'pageautorefname': '', - 'translatablestrings': '', -} # type: Dict[unicode, unicode] +} # type: Dict[str, Any] ADDITIONAL_SETTINGS = { 'pdflatex': { 'inputenc': '\\usepackage[utf8]{inputenc}', 'utf8extra': ('\\ifdefined\\DeclareUnicodeCharacter\n' '% support both utf8 and utf8x syntaxes\n' - '\\edef\\sphinxdqmaybe{' - '\\ifdefined\\DeclareUnicodeCharacterAsOptional' - '\\string"\\fi}\n' - ' \\DeclareUnicodeCharacter{\\sphinxdqmaybe00A0}' - '{\\nobreakspace}\n' - ' \\DeclareUnicodeCharacter{\\sphinxdqmaybe2500}' - '{\\sphinxunichar{2500}}\n' - ' \\DeclareUnicodeCharacter{\\sphinxdqmaybe2502}' - '{\\sphinxunichar{2502}}\n' - ' \\DeclareUnicodeCharacter{\\sphinxdqmaybe2514}' - '{\\sphinxunichar{2514}}\n' - ' \\DeclareUnicodeCharacter{\\sphinxdqmaybe251C}' - '{\\sphinxunichar{251C}}\n' - ' \\DeclareUnicodeCharacter{\\sphinxdqmaybe2572}' - '{\\textbackslash}\n' + ' \\ifdefined\\DeclareUnicodeCharacterAsOptional\n' + ' \\def\\sphinxDUC#1{\\DeclareUnicodeCharacter{"#1}}\n' + ' \\else\n' + ' \\let\\sphinxDUC\\DeclareUnicodeCharacter\n' + ' \\fi\n' + ' \\sphinxDUC{00A0}{\\nobreakspace}\n' + ' \\sphinxDUC{2500}{\\sphinxunichar{2500}}\n' + ' \\sphinxDUC{2502}{\\sphinxunichar{2502}}\n' + ' \\sphinxDUC{2514}{\\sphinxunichar{2514}}\n' + ' \\sphinxDUC{251C}{\\sphinxunichar{251C}}\n' + ' \\sphinxDUC{2572}{\\textbackslash}\n' '\\fi'), }, 'xelatex': { @@ -145,29 +191,32 @@ ADDITIONAL_SETTINGS = { 'polyglossia': '\\usepackage{polyglossia}', 'babel': '', 'fontenc': '\\usepackage{fontspec}', - 'fontpkg': '', + 'fontpkg': XELATEX_DEFAULT_FONTPKG, + 'textgreek': '', 'utf8extra': ('\\catcode`^^^^00a0\\active\\protected\\def^^^^00a0' '{\\leavevmode\\nobreak\\ }'), - 'fvset': '\\fvset{fontsize=auto}', }, 'lualatex': { 'latex_engine': 'lualatex', 'polyglossia': '\\usepackage{polyglossia}', 'babel': '', - 'fontenc': '\\usepackage{fontspec}', - 'fontpkg': '', + 'fontenc': ('\\usepackage{fontspec}\n' + '\\defaultfontfeatures[\\rmfamily,\\sffamily]{}'), + 'fontpkg': LUALATEX_DEFAULT_FONTPKG, + 'textgreek': '', 'utf8extra': ('\\catcode`^^^^00a0\\active\\protected\\def^^^^00a0' '{\\leavevmode\\nobreak\\ }'), - 'fvset': '\\fvset{fontsize=auto}', }, 'platex': { 'latex_engine': 'platex', 'babel': '', 'classoptions': ',dvipdfmx', + 'fontpkg': '\\usepackage{times}', + 'textgreek': '', 'fncychap': '', 'geometry': '\\usepackage[dvipdfm]{geometry}', }, -} # type: Dict[unicode, Dict[unicode, unicode]] +} # type: Dict[str, Dict[str, Any]] EXTRA_RE = re.compile(r'^(.*\S)\s+\(([^()]*)\)\s*$') @@ -194,86 +243,34 @@ class LaTeXWriter(writers.Writer): output = None def __init__(self, builder): - # type: (Builder) -> None - writers.Writer.__init__(self) + # type: (LaTeXBuilder) -> None + super().__init__() self.builder = builder def translate(self): # type: () -> None visitor = self.builder.create_translator(self.document, self.builder) self.document.walkabout(visitor) - self.output = visitor.astext() + self.output = cast(LaTeXTranslator, visitor).astext() # Helper classes -class ExtBabel(Babel): - cyrillic_languages = ('bulgarian', 'kazakh', 'mongolian', 'russian', 'ukrainian') - - def __init__(self, language_code, use_polyglossia=False): - # type: (unicode, bool) -> None - self.language_code = language_code - self.use_polyglossia = use_polyglossia - self.supported = True - super(ExtBabel, self).__init__(language_code or '') - - def get_shorthandoff(self): - # type: () -> unicode - warnings.warn('ExtBabel.get_shorthandoff() is deprecated.', - RemovedInSphinx30Warning, stacklevel=2) - return SHORTHANDOFF - - def uses_cyrillic(self): - # type: () -> bool - return self.language in self.cyrillic_languages - - def is_supported_language(self): - # type: () -> bool - return self.supported - - def language_name(self, language_code): - # type: (unicode) -> unicode - language = super(ExtBabel, self).language_name(language_code) - if language == 'ngerman' and self.use_polyglossia: - # polyglossia calls new orthography (Neue Rechtschreibung) as - # german (with new spelling option). - return 'german' - elif not language: - self.supported = False - return 'english' # fallback to english - else: - return language - - def get_mainlanguage_options(self): - # type: () -> unicode - """Return options for polyglossia's ``\\setmainlanguage``.""" - if self.use_polyglossia is False: - return None - elif self.language == 'german': - language = super(ExtBabel, self).language_name(self.language_code) - if language == 'ngerman': - return 'spelling=new' - else: - return 'spelling=old' - else: - return None - - -class Table(object): +class Table: """A table data""" def __init__(self, node): - # type: (nodes.table) -> None - self.header = [] # type: List[unicode] - self.body = [] # type: List[unicode] + # type: (nodes.Element) -> None + self.header = [] # type: List[str] + self.body = [] # type: List[str] self.align = node.get('align') self.colcount = 0 - self.colspec = None # type: unicode + self.colspec = None # type: str self.colwidths = [] # type: List[int] self.has_problematic = False self.has_oldproblematic = False self.has_verbatim = False - self.caption = None # type: List[unicode] + self.caption = None # type: List[str] self.stubs = [] # type: List[int] # current position @@ -281,7 +278,7 @@ class Table(object): self.row = 0 # for internal use - self.classes = node.get('classes', []) # type: List[unicode] + self.classes = node.get('classes', []) # type: List[str] self.cells = defaultdict(int) # type: Dict[Tuple[int, int], int] # it maps table location to cell_id # (cell = rectangular area) @@ -289,14 +286,14 @@ class Table(object): @property def caption_footnotetexts(self): - # type: () -> List[unicode] + # type: () -> List[str] warnings.warn('table.caption_footnotetexts is deprecated.', RemovedInSphinx30Warning, stacklevel=2) return [] @property def header_footnotetexts(self): - # type: () -> List[unicode] + # type: () -> List[str] warnings.warn('table.header_footnotetexts is deprecated.', RemovedInSphinx30Warning, stacklevel=2) return [] @@ -307,7 +304,7 @@ class Table(object): return self.row > 30 or 'longtable' in self.classes def get_table_type(self): - # type: () -> unicode + # type: () -> str """Returns the LaTeX environment name for the table. The class currently supports: @@ -328,7 +325,7 @@ class Table(object): return 'tabulary' def get_colspec(self): - # type: () -> unicode + # type: () -> str """Returns a column spec of table. This is what LaTeX calls the 'preamble argument' of the used table environment. @@ -380,7 +377,7 @@ class Table(object): return None -class TableCell(object): +class TableCell: """A cell data of tables.""" def __init__(self, table, row, col): @@ -419,13 +416,13 @@ class TableCell(object): def escape_abbr(text): - # type: (unicode) -> unicode + # type: (str) -> str """Adjust spacing after abbreviations.""" return re.sub(r'\.(?=\s|$)', r'.\@', text) def rstdim_to_latexdim(width_str, scale = 100): - # type: (unicode, int) -> unicode + # type: (str, int) -> str """Convert `width_str` with rst length to LaTeX length.""" match = re.match(r'^(\d*\.?\d*)\s*(\S*)$', width_str) if not match: @@ -453,7 +450,8 @@ def rstdim_to_latexdim(width_str, scale = 100): return res -class LaTeXTranslator(nodes.NodeVisitor): +class LaTeXTranslator(SphinxTranslator): + builder = None # type: LaTeXBuilder secnumdepth = 2 # legacy sphinxhowto.cls uses this, whereas article.cls # default is originally 3. For book/report, 2 is already LaTeX default. @@ -463,10 +461,9 @@ class LaTeXTranslator(nodes.NodeVisitor): docclasses = ('howto', 'manual') def __init__(self, document, builder): - # type: (nodes.Node, Builder) -> None - nodes.NodeVisitor.__init__(self, document) - self.builder = builder - self.body = [] # type: List[unicode] + # type: (nodes.document, LaTeXBuilder) -> None + super().__init__(document, builder) + self.body = [] # type: List[str] # flags self.in_title = 0 @@ -485,65 +482,36 @@ class LaTeXTranslator(nodes.NodeVisitor): self.first_param = 0 # sort out some elements - self.elements = DEFAULT_SETTINGS.copy() - self.elements.update(ADDITIONAL_SETTINGS.get(builder.config.latex_engine, {})) - # for xelatex+French, don't use polyglossia - if self.elements['latex_engine'] == 'xelatex': - if builder.config.language: - if builder.config.language[:2] == 'fr': - self.elements.update({ - 'polyglossia': '', - 'babel': '\\usepackage{babel}', - }) - # allow the user to override them all - self.elements.update(builder.config.latex_elements) + self.elements = self.builder.context.copy() # but some have other interface in config file - self.elements.update({ - 'wrapperclass': self.format_docclass(document.settings.docclass), - # if empty, the title is set to the first section title - 'title': document.settings.title, # treat as a raw LaTeX code - 'release': self.encode(builder.config.release), - 'author': document.settings.author, # treat as a raw LaTeX code - 'indexname': _('Index'), - 'use_xindy': builder.config.latex_use_xindy, - }) - if not self.elements['releasename'] and self.elements['release']: - self.elements.update({ - 'releasename': _('Release'), - }) + self.elements['wrapperclass'] = self.format_docclass(self.settings.docclass) # we assume LaTeX class provides \chapter command except in case # of non-Japanese 'howto' case self.sectionnames = LATEXSECTIONNAMES[:] - if document.settings.docclass == 'howto': - docclass = builder.config.latex_docclass.get('howto', 'article') + if self.settings.docclass == 'howto': + docclass = self.config.latex_docclass.get('howto', 'article') if docclass[0] == 'j': # Japanese class... pass else: self.sectionnames.remove('chapter') else: - docclass = builder.config.latex_docclass.get('manual', 'report') + docclass = self.config.latex_docclass.get('manual', 'report') self.elements['docclass'] = docclass # determine top section level self.top_sectionlevel = 1 - if builder.config.latex_toplevel_sectioning: + if self.config.latex_toplevel_sectioning: try: self.top_sectionlevel = \ - self.sectionnames.index(builder.config.latex_toplevel_sectioning) + self.sectionnames.index(self.config.latex_toplevel_sectioning) except ValueError: logger.warning(__('unknown %r toplevel_sectioning for class %r') % - (builder.config.latex_toplevel_sectioning, docclass)) - - if builder.config.today: - self.elements['date'] = builder.config.today - else: - self.elements['date'] = format_date(builder.config.today_fmt or _('%b %d, %Y'), - language=builder.config.language) + (self.config.latex_toplevel_sectioning, docclass)) - if builder.config.numfig: - self.numfig_secnum_depth = builder.config.numfig_secnum_depth + if self.config.numfig: + self.numfig_secnum_depth = self.config.numfig_secnum_depth if self.numfig_secnum_depth > 0: # default is 1 # numfig_secnum_depth as passed to sphinx.sty indices same names as in # LATEXSECTIONNAMES but with -1 for part, 0 for chapter, 1 for section... @@ -561,44 +529,51 @@ class LaTeXTranslator(nodes.NodeVisitor): else: self.elements['sphinxpkgoptions'] += ',nonumfigreset' try: - if builder.config.math_numfig: + if self.config.math_numfig: self.elements['sphinxpkgoptions'] += ',mathnumfig' except AttributeError: pass - if builder.config.latex_logo: - # no need for \\noindent here, used in flushright - self.elements['logo'] = '\\sphinxincludegraphics{%s}\\par' % \ - path.basename(builder.config.latex_logo) - - if (builder.config.language and builder.config.language != 'ja' and - 'fncychap' not in builder.config.latex_elements): - # use Sonny style if any language specified + if (self.config.language not in {None, 'en', 'ja'} and + 'fncychap' not in self.config.latex_elements): + # use Sonny style if any language specified (except English) self.elements['fncychap'] = ('\\usepackage[Sonny]{fncychap}\n' '\\ChNameVar{\\Large\\normalfont' '\\sffamily}\n\\ChTitleVar{\\Large' '\\normalfont\\sffamily}') - self.babel = ExtBabel(builder.config.language, - not self.elements['babel']) - if builder.config.language and not self.babel.is_supported_language(): + self.babel = self.builder.babel + if self.config.language and not self.babel.is_supported_language(): # emit warning if specified language is invalid # (only emitting, nothing changed to processing) logger.warning(__('no Babel option known for language %r'), - builder.config.language) + self.config.language) # set up multilingual module... + if self.elements['latex_engine'] == 'pdflatex': + if not self.babel.uses_cyrillic(): + if 'X2' in self.elements['fontenc']: + self.elements['substitutefont'] = '\\usepackage{substitutefont}' + self.elements['textcyrillic'] = ('\\usepackage[Xtwo]' + '{sphinxcyrillic}') + elif 'T2A' in self.elements['fontenc']: + self.elements['substitutefont'] = '\\usepackage{substitutefont}' + self.elements['textcyrillic'] = ('\\usepackage[TtwoA]' + '{sphinxcyrillic}') + if 'LGR' in self.elements['fontenc']: + self.elements['substitutefont'] = '\\usepackage{substitutefont}' + else: + self.elements['textgreek'] = '' # 'babel' key is public and user setting must be obeyed if self.elements['babel']: self.elements['classoptions'] += ',' + self.babel.get_language() # this branch is not taken for xelatex/lualatex if default settings self.elements['multilingual'] = self.elements['babel'] - if builder.config.language: + if self.config.language: self.elements['shorthandoff'] = SHORTHANDOFF # Times fonts don't work with Cyrillic languages - if self.babel.uses_cyrillic() \ - and 'fontpkg' not in builder.config.latex_elements: + if self.babel.uses_cyrillic() and 'fontpkg' not in self.config.latex_elements: self.elements['fontpkg'] = '' elif self.elements['polyglossia']: self.elements['classoptions'] += ',' + self.babel.get_language() @@ -612,25 +587,15 @@ class LaTeXTranslator(nodes.NodeVisitor): self.elements['multilingual'] = '%s\n%s' % (self.elements['polyglossia'], mainlanguage) - if getattr(builder, 'usepackages', None): - def declare_package(packagename, options=None): - # type:(unicode, unicode) -> unicode - if options: - return '\\usepackage[%s]{%s}' % (options, packagename) - else: - return '\\usepackage{%s}' % (packagename,) - usepackages = (declare_package(*p) for p in builder.usepackages) - self.elements['usepackages'] += "\n".join(usepackages) - minsecnumdepth = self.secnumdepth # 2 from legacy sphinx manual/howto - if document.get('tocdepth'): + if self.document.get('tocdepth'): # reduce tocdepth if `part` or `chapter` is used for top_sectionlevel # tocdepth = -1: show only parts # tocdepth = 0: show parts and chapters # tocdepth = 1: show parts, chapters and sections # tocdepth = 2: show parts, chapters, sections and subsections # ... - tocdepth = document['tocdepth'] + self.top_sectionlevel - 2 + tocdepth = self.document['tocdepth'] + self.top_sectionlevel - 2 if len(self.sectionnames) < len(LATEXSECTIONNAMES) and \ self.top_sectionlevel > 0: tocdepth += 1 # because top_sectionlevel is shifted by -1 @@ -641,16 +606,17 @@ class LaTeXTranslator(nodes.NodeVisitor): self.elements['tocdepth'] = '\\setcounter{tocdepth}{%d}' % tocdepth minsecnumdepth = max(minsecnumdepth, tocdepth) - if builder.config.numfig and (builder.config.numfig_secnum_depth > 0): + if self.config.numfig and (self.config.numfig_secnum_depth > 0): minsecnumdepth = max(minsecnumdepth, self.numfig_secnum_depth - 1) if minsecnumdepth > self.secnumdepth: self.elements['secnumdepth'] = '\\setcounter{secnumdepth}{%d}' %\ minsecnumdepth - if getattr(document.settings, 'contentsname', None): - self.elements['contentsname'] = \ - self.babel_renewcommand('\\contentsname', document.settings.contentsname) + contentsname = self.settings.contentsname + if contentsname: + self.elements['contentsname'] = self.babel_renewcommand('\\contentsname', + contentsname) if self.elements['maxlistdepth']: self.elements['sphinxpkgoptions'] += (',maxlistdepth=%s' % @@ -664,72 +630,52 @@ class LaTeXTranslator(nodes.NodeVisitor): if self.elements['extraclassoptions']: self.elements['classoptions'] += ',' + \ self.elements['extraclassoptions'] - self.elements['translatablestrings'] = ( - self.babel_renewcommand( - '\\literalblockcontinuedname', self.encode(_('continued from previous page')) - ) + - self.babel_renewcommand( - '\\literalblockcontinuesname', self.encode(_('continues on next page')) - ) + - self.babel_renewcommand( - '\\sphinxnonalphabeticalgroupname', self.encode(_('Non-alphabetical')) - ) + - self.babel_renewcommand( - '\\sphinxsymbolsname', self.encode(_('Symbols')) - ) + - self.babel_renewcommand( - '\\sphinxnumbersname', self.encode(_('Numbers')) - ) - ) - self.elements['pageautorefname'] = \ - self.babel_defmacro('\\pageautorefname', self.encode(_('page'))) - self.elements['numfig_format'] = self.generate_numfig_format(builder) - - self.highlighter = highlighting.PygmentsBridge('latex', builder.config.pygments_style) - self.context = [] # type: List[Any] - self.descstack = [] # type: List[unicode] - self.table = None # type: Table - self.next_table_colspec = None # type: unicode - self.bodystack = [] # type: List[List[unicode]] - self.footnote_restricted = False - self.pending_footnotes = [] # type: List[nodes.footnote_reference] - self.curfilestack = [] # type: List[unicode] - self.handled_abbrs = set() # type: Set[unicode] + + self.highlighter = highlighting.PygmentsBridge('latex', self.config.pygments_style) + self.context = [] # type: List[Any] + self.descstack = [] # type: List[str] + self.table = None # type: Table + self.next_table_colspec = None # type: str + self.bodystack = [] # type: List[List[str]] + self.footnote_restricted = None # type: nodes.Element + self.pending_footnotes = [] # type: List[nodes.footnote_reference] + self.curfilestack = [] # type: List[str] + self.handled_abbrs = set() # type: Set[str] def pushbody(self, newbody): - # type: (List[unicode]) -> None + # type: (List[str]) -> None self.bodystack.append(self.body) self.body = newbody def popbody(self): - # type: () -> List[unicode] + # type: () -> List[str] body = self.body self.body = self.bodystack.pop() return body def restrict_footnote(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None warnings.warn('LaTeXWriter.restrict_footnote() is deprecated.', RemovedInSphinx30Warning, stacklevel=2) - if self.footnote_restricted is False: + if self.footnote_restricted is None: self.footnote_restricted = node self.pending_footnotes = [] def unrestrict_footnote(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None warnings.warn('LaTeXWriter.unrestrict_footnote() is deprecated.', RemovedInSphinx30Warning, stacklevel=2) if self.footnote_restricted == node: - self.footnote_restricted = False + self.footnote_restricted = None for footnode in self.pending_footnotes: footnode['footnotetext'] = True footnode.walkabout(self) self.pending_footnotes = [] def format_docclass(self, docclass): - # type: (unicode) -> unicode + # type: (str) -> str """ prepends prefix to sphinx document classes """ if docclass in self.docclasses: @@ -737,22 +683,22 @@ class LaTeXTranslator(nodes.NodeVisitor): return docclass def astext(self): - # type: () -> unicode + # type: () -> str self.elements.update({ - 'body': u''.join(self.body), + '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: (unicode, bool, bool) -> unicode + # type: (str, bool, bool) -> 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.Node, bool) -> unicode + # type: (nodes.Element, bool) -> str labels = ''.join(self.hypertarget(node_id, anchor=False) for node_id in node['ids']) if anchor: return r'\phantomsection' + labels @@ -760,21 +706,21 @@ class LaTeXTranslator(nodes.NodeVisitor): return labels def hyperlink(self, id): - # type: (unicode) -> unicode + # type: (str) -> str return '{\\hyperref[%s]{' % self.idescape(id) def hyperpageref(self, id): - # type: (unicode) -> unicode + # type: (str) -> str return '\\autopageref*{%s}' % self.idescape(id) def idescape(self, id): - # type: (unicode) -> unicode - return '\\detokenize{%s}' % text_type(id).translate(tex_replace_map).\ + # type: (str) -> str + return '\\detokenize{%s}' % str(id).translate(tex_replace_map).\ encode('ascii', 'backslashreplace').decode('ascii').\ replace('\\', '_') def babel_renewcommand(self, command, definition): - # type: (unicode, unicode) -> unicode + # type: (str, str) -> str if self.elements['multilingual']: prefix = '\\addto\\captions%s{' % self.babel.get_language() suffix = '}' @@ -784,66 +730,17 @@ class LaTeXTranslator(nodes.NodeVisitor): return ('%s\\renewcommand{%s}{%s}%s\n' % (prefix, command, definition, suffix)) - def babel_defmacro(self, name, definition): - # type: (unicode, unicode) -> unicode - if self.elements['babel']: - prefix = '\\addto\\extras%s{' % self.babel.get_language() - suffix = '}' - else: # babel is disabled (mainly for Japanese environment) - prefix = '' - suffix = '' - - return ('%s\\def%s{%s}%s\n' % (prefix, name, definition, suffix)) - - def generate_numfig_format(self, builder): - # type: (Builder) -> unicode - ret = [] # type: List[unicode] - figure = self.builder.config.numfig_format['figure'].split('%s', 1) - if len(figure) == 1: - ret.append('\\def\\fnum@figure{%s}\n' % - text_type(figure[0]).strip().translate(tex_escape_map)) - else: - definition = escape_abbr(text_type(figure[0]).translate(tex_escape_map)) - ret.append(self.babel_renewcommand('\\figurename', definition)) - ret.append('\\makeatletter\n') - ret.append('\\def\\fnum@figure{\\figurename\\thefigure{}%s}\n' % - text_type(figure[1]).translate(tex_escape_map)) - ret.append('\\makeatother\n') - - table = self.builder.config.numfig_format['table'].split('%s', 1) - if len(table) == 1: - ret.append('\\def\\fnum@table{%s}\n' % - text_type(table[0]).strip().translate(tex_escape_map)) - else: - definition = escape_abbr(text_type(table[0]).translate(tex_escape_map)) - ret.append(self.babel_renewcommand('\\tablename', definition)) - ret.append('\\makeatletter\n') - ret.append('\\def\\fnum@table{\\tablename\\thetable{}%s}\n' % - text_type(table[1]).translate(tex_escape_map)) - ret.append('\\makeatother\n') - - codeblock = self.builder.config.numfig_format['code-block'].split('%s', 1) - if len(codeblock) == 1: - pass # FIXME - else: - definition = text_type(codeblock[0]).strip().translate(tex_escape_map) - ret.append(self.babel_renewcommand('\\literalblockname', definition)) - if codeblock[1]: - pass # FIXME - - return ''.join(ret) - def generate_indices(self): - # type: (Builder) -> unicode + # type: () -> str def generate(content, collapsed): - # type: (List[Tuple[unicode, List[Tuple[unicode, unicode, unicode, unicode, unicode]]]], bool) -> None # NOQA + # type: (List[Tuple[str, List[IndexEntry]]], bool) -> None ret.append('\\begin{sphinxtheindex}\n') ret.append('\\let\\bigletter\\sphinxstyleindexlettergroup\n') for i, (letter, entries) in enumerate(content): if i > 0: ret.append('\\indexspace\n') ret.append('\\bigletter{%s}\n' % - text_type(letter).translate(tex_escape_map)) + str(letter).translate(tex_escape_map)) for entry in entries: if not entry[3]: continue @@ -860,7 +757,7 @@ class LaTeXTranslator(nodes.NodeVisitor): # latex_domain_indices can be False/True or a list of index names indices_config = self.builder.config.latex_domain_indices if indices_config: - for domain in itervalues(self.builder.env.domains): + for domain in self.builder.env.domains.values(): for indexcls in domain.indices: indexname = '%s-%s' % (domain.name, indexcls.name) if isinstance(indices_config, list): @@ -870,14 +767,14 @@ class LaTeXTranslator(nodes.NodeVisitor): self.builder.docnames) if not content: continue - ret.append(u'\\renewcommand{\\indexname}{%s}\n' % + ret.append('\\renewcommand{\\indexname}{%s}\n' % indexcls.localname) generate(content, collapsed) return ''.join(ret) def render(self, template_name, variables): - # type: (unicode, Dict) -> unicode + # type: (str, Dict) -> str for template_dir in self.builder.config.templates_path: template = path.join(self.builder.confdir, template_dir, template_name) @@ -887,14 +784,14 @@ class LaTeXTranslator(nodes.NodeVisitor): return LaTeXRenderer().render(template_name, variables) def visit_document(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.curfilestack.append(node.get('docname', '')) if self.first_document == 1: # the first document is all the regular content ... self.first_document = 0 elif self.first_document == 0: # ... and all others are the appendices - self.body.append(u'\n\\appendix\n') + self.body.append('\n\\appendix\n') self.first_document = -1 if 'docname' in node: self.body.append(self.hypertarget(':doc')) @@ -902,88 +799,68 @@ class LaTeXTranslator(nodes.NodeVisitor): self.sectionlevel = self.top_sectionlevel - 1 def depart_document(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def visit_start_of_file(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.curfilestack.append(node['docname']) - def collect_footnotes(self, node): - # type: (nodes.Node) -> Dict[unicode, List[Union[collected_footnote, bool]]] - def footnotes_under(n): - # type: (nodes.Node) -> Iterator[nodes.Node] - if isinstance(n, nodes.footnote): - yield n - else: - for c in n.children: - if isinstance(c, addnodes.start_of_file): - continue - for k in footnotes_under(c): - yield k - - fnotes = {} # type: Dict[unicode, List[Union[collected_footnote, bool]]] - for fn in footnotes_under(node): - num = fn.children[0].astext().strip() - newnode = collected_footnote(*fn.children, number=num) - fnotes[num] = [newnode, False] - return fnotes - def depart_start_of_file(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.curfilestack.pop() def visit_section(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None if not self.this_is_the_title: self.sectionlevel += 1 self.body.append('\n\n') def depart_section(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.sectionlevel = max(self.sectionlevel - 1, self.top_sectionlevel - 1) def visit_problematic(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append(r'{\color{red}\bfseries{}') def depart_problematic(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('}') def visit_topic(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.in_minipage = 1 self.body.append('\n\\begin{sphinxShadowBox}\n') def depart_topic(self, node): - # type: (nodes.Node) -> None + # type: (nodes.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.Node) -> None + # type: (nodes.Element) -> None pass def depart_glossary(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def visit_productionlist(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('\n\n\\begin{productionlist}\n') self.in_production_list = 1 def depart_productionlist(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('\\end{productionlist}\n\n') self.in_production_list = 0 def visit_production(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None if node['tokenname']: tn = node['tokenname'] self.body.append(self.hypertarget('grammar-token-' + tn)) @@ -992,19 +869,19 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append('\\productioncont{') def depart_production(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('}\n') def visit_transition(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append(self.elements['transition']) def depart_transition(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def visit_title(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None parent = node.parent if isinstance(parent, addnodes.seealso): # the environment already handles this @@ -1025,7 +902,7 @@ class LaTeXTranslator(nodes.NodeVisitor): short = '' if node.traverse(nodes.image): short = ('[%s]' % - u' '.join(clean_astext(node).split()).translate(tex_escape_map)) + ' '.join(clean_astext(node).split()).translate(tex_escape_map)) try: self.body.append(r'\%s%s{' % (self.sectionnames[self.sectionlevel], short)) @@ -1054,7 +931,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.in_title = 1 def depart_title(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.in_title = 0 if isinstance(node.parent, nodes.table): self.table.caption = self.popbody() @@ -1062,7 +939,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append(self.context.pop()) def visit_subtitle(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None if isinstance(node.parent, nodes.sidebar): self.body.append('\\sphinxstylesidebarsubtitle{') self.context.append('}\n') @@ -1070,21 +947,21 @@ class LaTeXTranslator(nodes.NodeVisitor): self.context.append('') def depart_subtitle(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append(self.context.pop()) def visit_desc(self, node): - # type: (nodes.Node) -> None + # type: (nodes.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.Node) -> None + # type: (nodes.Element) -> None self.body.append('\n\\end{fulllineitems}\n\n') def _visit_signature_line(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None for child in node: if isinstance(child, addnodes.desc_parameterlist): self.body.append(r'\pysiglinewithargsret{') @@ -1093,11 +970,11 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append(r'\pysigline{') def _depart_signature_line(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('}') def visit_desc_signature(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None if node.parent['objtype'] != 'describe' and node['ids']: hyper = self.hypertarget(node['ids'][0]) else: @@ -1109,71 +986,71 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append('%\n\\pysigstartmultiline\n') def depart_desc_signature(self, node): - # type: (nodes.Node) -> None + # type: (nodes.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.Node) -> None + # type: (nodes.Element) -> None self._visit_signature_line(node) def depart_desc_signature_line(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self._depart_signature_line(node) def visit_desc_addname(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append(r'\sphinxcode{\sphinxupquote{') self.literal_whitespace += 1 def depart_desc_addname(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('}}') self.literal_whitespace -= 1 def visit_desc_type(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def depart_desc_type(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def visit_desc_returns(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append(r'{ $\rightarrow$ ') def depart_desc_returns(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append(r'}') def visit_desc_name(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append(r'\sphinxbfcode{\sphinxupquote{') self.no_contractions += 1 self.literal_whitespace += 1 def depart_desc_name(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('}}') self.literal_whitespace -= 1 self.no_contractions -= 1 def visit_desc_parameterlist(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None # close name, open parameterlist self.body.append('}{') self.first_param = 1 def depart_desc_parameterlist(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None # close parameterlist, open return annotation self.body.append('}{') def visit_desc_parameter(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None if not self.first_param: self.body.append(', ') else: @@ -1182,69 +1059,69 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append(r'\emph{') def depart_desc_parameter(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None if not node.hasattr('noemph'): self.body.append('}') def visit_desc_optional(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append(r'\sphinxoptional{') def depart_desc_optional(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('}') def visit_desc_annotation(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append(r'\sphinxbfcode{\sphinxupquote{') def depart_desc_annotation(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('}}') def visit_desc_content(self, node): - # type: (nodes.Node) -> None + # type: (nodes.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.Node) -> None + # type: (nodes.Element) -> None pass def visit_seealso(self, node): - # type: (nodes.Node) -> None - self.body.append(u'\n\n\\sphinxstrong{%s:}\n\n' % admonitionlabels['seealso']) + # type: (nodes.Element) -> None + self.body.append('\n\n\\sphinxstrong{%s:}\n\n' % admonitionlabels['seealso']) def depart_seealso(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append("\n\n") def visit_rubric(self, node): - # type: (nodes.Node) -> None - if len(node.children) == 1 and node.children[0].astext() in \ - ('Footnotes', _('Footnotes')): + # type: (nodes.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.Node) -> None + # type: (nodes.Element) -> None self.in_title = 0 self.body.append(self.context.pop()) def visit_footnote(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.in_footnote += 1 + label = cast(nodes.label, node[0]) if self.in_parsed_literal: - self.body.append('\\begin{footnote}[%s]' % node[0].astext()) + self.body.append('\\begin{footnote}[%s]' % label.astext()) else: - self.body.append('%%\n\\begin{footnote}[%s]' % node[0].astext()) + self.body.append('%%\n\\begin{footnote}[%s]' % label.astext()) self.body.append('\\sphinxAtStartFootnote\n') def depart_footnote(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None if self.in_parsed_literal: self.body.append('\\end{footnote}') else: @@ -1252,16 +1129,16 @@ class LaTeXTranslator(nodes.NodeVisitor): self.in_footnote -= 1 def visit_label(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None raise nodes.SkipNode def visit_tabular_col_spec(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.next_table_colspec = node['spec'] raise nodes.SkipNode def visit_table(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None if self.table: raise UnsupportedError( '%s:%s: nested tables are not yet implemented.' % @@ -1270,12 +1147,12 @@ class LaTeXTranslator(nodes.NodeVisitor): if self.next_table_colspec: self.table.colspec = '{%s}\n' % self.next_table_colspec if 'colwidths-given' in node.get('classes', []): - logger.info('both tabularcolumns and :widths: option are given. ' - ':widths: is ignored.', location=node) + logger.info(__('both tabularcolumns and :widths: option are given. ' + ':widths: is ignored.'), location=node) self.next_table_colspec = None def depart_table(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None labels = self.hypertarget_to(node) table_type = self.table.get_table_type() table = self.render(table_type + '.tex_t', @@ -1287,7 +1164,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.table = None def visit_colspec(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.table.colcount += 1 if 'colwidth' in node: self.table.colwidths.append(node['colwidth']) @@ -1295,37 +1172,37 @@ class LaTeXTranslator(nodes.NodeVisitor): self.table.stubs.append(self.table.colcount - 1) def depart_colspec(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def visit_tgroup(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def depart_tgroup(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def visit_thead(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None # Redirect head output until header is finished. self.pushbody(self.table.header) def depart_thead(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.popbody() def visit_tbody(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None # Redirect body output until table is finished. self.pushbody(self.table.body) def depart_tbody(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.popbody() def visit_row(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.table.col = 0 # fill columns if the row starts with the bottom of multirow cell @@ -1346,7 +1223,7 @@ class LaTeXTranslator(nodes.NodeVisitor): (cell.width, cell.cell_id)) def depart_row(self, node): - # type: (nodes.Node) -> None + # type: (nodes.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] @@ -1364,7 +1241,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.table.row += 1 def visit_entry(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None if self.table.col > 0: self.body.append('&') self.table.add_cell(node.get('morerows', 0) + 1, node.get('morecols', 0) + 1) @@ -1402,7 +1279,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.context.append(context) def depart_entry(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None if self.needs_linetrimming: self.needs_linetrimming = 0 body = self.popbody() @@ -1436,31 +1313,32 @@ class LaTeXTranslator(nodes.NodeVisitor): (nextcell.width, nextcell.cell_id)) def visit_acks(self, node): - # type: (nodes.Node) -> None + # type: (nodes.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]) + list_items = cast(Iterable[nodes.list_item], bullet_list) self.body.append('\n\n') - self.body.append(', '.join(n.astext() - for n in node.children[0].children) + '.') + self.body.append(', '.join(n.astext() for n in list_items) + '.') self.body.append('\n\n') raise nodes.SkipNode def visit_bullet_list(self, node): - # type: (nodes.Node) -> None + # type: (nodes.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.Node) -> None + # type: (nodes.Element) -> None if not self.compact_list: self.body.append('\\end{itemize}\n') def visit_enumerated_list(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None def get_enumtype(node): - # type: (nodes.Node) -> unicode + # type: (nodes.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 @@ -1469,7 +1347,7 @@ class LaTeXTranslator(nodes.NodeVisitor): return enumtype def get_nested_level(node): - # type: (nodes.Node) -> int + # type: (nodes.Element) -> int if node is None: return 0 elif isinstance(node, nodes.enumerated_list): @@ -1495,41 +1373,41 @@ class LaTeXTranslator(nodes.NodeVisitor): self.table.has_problematic = True def depart_enumerated_list(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('\\end{enumerate}\n') def visit_list_item(self, node): - # type: (nodes.Node) -> None + # type: (nodes.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.Node) -> None + # type: (nodes.Element) -> None self.body.append('\n') def visit_definition_list(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('\\begin{description}\n') if self.table: self.table.has_problematic = True def depart_definition_list(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('\\end{description}\n') def visit_definition_list_item(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def depart_definition_list_item(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def visit_term(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.in_term += 1 - ctx = '' # type: unicode + ctx = '' if node.get('ids'): ctx = '\\phantomsection' for node_id in node['ids']: @@ -1539,42 +1417,42 @@ class LaTeXTranslator(nodes.NodeVisitor): self.context.append(ctx) def depart_term(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append(self.context.pop()) self.in_term -= 1 def visit_classifier(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('{[}') def depart_classifier(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('{]}') def visit_definition(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def depart_definition(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('\n') def visit_field_list(self, node): - # type: (nodes.Node) -> None + # type: (nodes.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.Node) -> None + # type: (nodes.Element) -> None self.body.append('\\end{description}\\end{quote}\n') def visit_field(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def depart_field(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass visit_field_name = visit_term @@ -1584,7 +1462,7 @@ class LaTeXTranslator(nodes.NodeVisitor): depart_field_body = depart_definition def visit_paragraph(self, node): - # type: (nodes.Node) -> None + # type: (nodes.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 @@ -1599,21 +1477,21 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append('\n') def depart_paragraph(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('\n') def visit_centered(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('\n\\begin{center}') if self.table: self.table.has_problematic = True def depart_centered(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('\n\\end{center}') def visit_hlist(self, node): - # type: (nodes.Node) -> None + # type: (nodes.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 @@ -1623,20 +1501,20 @@ class LaTeXTranslator(nodes.NodeVisitor): self.table.has_problematic = True def depart_hlist(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.compact_list -= 1 self.body.append('\\end{itemize}\n') def visit_hlistcol(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def depart_hlistcol(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def latex_image_length(self, width_str, scale = 100): - # type: (nodes.Node, int) -> unicode + # type: (str, int) -> str try: return rstdim_to_latexdim(width_str, scale) except ValueError: @@ -1644,16 +1522,16 @@ class LaTeXTranslator(nodes.NodeVisitor): return None def is_inline(self, node): - # type: (nodes.Node) -> bool + # type: (nodes.Element) -> bool """Check whether a node represents an inline element.""" return isinstance(node.parent, nodes.TextElement) def visit_image(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None attrs = node.attributes - pre = [] # type: List[unicode] + pre = [] # type: List[str] # in reverse order - post = [] # type: List[unicode] + post = [] # type: List[str] include_graphics_options = [] is_inline = self.is_inline(node) if 'width' in attrs: @@ -1727,11 +1605,11 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.extend(post) def depart_image(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def visit_figure(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None if self.table: # TODO: support align option if 'width' in node: @@ -1748,7 +1626,7 @@ class LaTeXTranslator(nodes.NodeVisitor): length = None if 'width' in node: length = self.latex_image_length(node['width']) - elif 'width' in node[0]: + elif isinstance(node[0], nodes.image) and 'width' in node[0]: length = self.latex_image_length(node[0]['width']) self.body.append('\\begin{wrapfigure}{%s}{%s}\n\\centering' % (node['align'] == 'right' and 'r' or 'l', length or '0pt')) @@ -1764,11 +1642,11 @@ class LaTeXTranslator(nodes.NodeVisitor): self.context.append('\\end{figure}\n') def depart_figure(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append(self.context.pop()) def visit_caption(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.in_caption += 1 if isinstance(node.parent, captioned_literal_block): self.body.append('\\sphinxSetupCaptionForVerbatim{') @@ -1780,7 +1658,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append('\\caption{') def depart_caption(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('}') if isinstance(node.parent, nodes.figure): labels = self.hypertarget_to(node.parent) @@ -1788,64 +1666,62 @@ class LaTeXTranslator(nodes.NodeVisitor): self.in_caption -= 1 def visit_legend(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('\n\\begin{sphinxlegend}') def depart_legend(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('\\end{sphinxlegend}\n') def visit_admonition(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('\n\\begin{sphinxadmonition}{note}') def depart_admonition(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('\\end{sphinxadmonition}\n') - def _make_visit_admonition(name): - # type: (unicode) -> Callable[[LaTeXTranslator, nodes.Node], None] - def visit_admonition(self, node): - # type: (nodes.Node) -> None - self.body.append(u'\n\\begin{sphinxadmonition}{%s}{%s:}' % - (name, admonitionlabels[name])) - return visit_admonition + def _visit_named_admonition(self, node): + # type: (nodes.Element) -> None + label = admonitionlabels[node.tagname] + self.body.append('\n\\begin{sphinxadmonition}{%s}{%s:}' % + (node.tagname, label)) def _depart_named_admonition(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('\\end{sphinxadmonition}\n') - visit_attention = _make_visit_admonition('attention') + visit_attention = _visit_named_admonition depart_attention = _depart_named_admonition - visit_caution = _make_visit_admonition('caution') + visit_caution = _visit_named_admonition depart_caution = _depart_named_admonition - visit_danger = _make_visit_admonition('danger') + visit_danger = _visit_named_admonition depart_danger = _depart_named_admonition - visit_error = _make_visit_admonition('error') + visit_error = _visit_named_admonition depart_error = _depart_named_admonition - visit_hint = _make_visit_admonition('hint') + visit_hint = _visit_named_admonition depart_hint = _depart_named_admonition - visit_important = _make_visit_admonition('important') + visit_important = _visit_named_admonition depart_important = _depart_named_admonition - visit_note = _make_visit_admonition('note') + visit_note = _visit_named_admonition depart_note = _depart_named_admonition - visit_tip = _make_visit_admonition('tip') + visit_tip = _visit_named_admonition depart_tip = _depart_named_admonition - visit_warning = _make_visit_admonition('warning') + visit_warning = _visit_named_admonition depart_warning = _depart_named_admonition def visit_versionmodified(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def depart_versionmodified(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def visit_target(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None def add_target(id): - # type: (unicode) -> None + # type: (str) -> None # indexing uses standard LaTeX index markup, so the targets # will be generated differently if id.startswith('index-'): @@ -1866,11 +1742,11 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append(self.hypertarget(id, anchor=anchor)) # skip if visitor for next node supports hyperlink - next_node = node + next_node = node # type: nodes.Node while isinstance(next_node, nodes.target): next_node = next_node.next_node(ascend=True) - domain = self.builder.env.get_domain('std') + domain = cast(StandardDomain, self.builder.env.get_domain('std')) if isinstance(next_node, HYPERLINK_SUPPORT_NODES): return elif domain.get_enumerable_node_type(next_node) and domain.get_numfig_title(next_node): @@ -1884,20 +1760,20 @@ class LaTeXTranslator(nodes.NodeVisitor): add_target(id) def depart_target(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def visit_attribution(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('\n\\begin{flushright}\n') self.body.append('---') def depart_attribution(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('\n\\end{flushright}\n') def visit_index(self, node, scre = None): - # type: (nodes.Node, None) -> None + # type: (nodes.Element, Pattern) -> None def escape(value): value = self.encode(value) value = value.replace(r'\{', r'\sphinxleftcurlybrace{}') @@ -1967,7 +1843,7 @@ class LaTeXTranslator(nodes.NodeVisitor): raise nodes.SkipNode def visit_raw(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None if not self.is_inline(node): self.body.append('\n') if 'latex' in node.get('format', '').split(): @@ -1977,7 +1853,7 @@ class LaTeXTranslator(nodes.NodeVisitor): raise nodes.SkipNode def visit_reference(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None if not self.in_title: for id in node.get('ids'): anchor = not self.in_caption @@ -2007,8 +1883,9 @@ class LaTeXTranslator(nodes.NodeVisitor): # reference to a label id = uri[1:].replace('#', ':') self.body.append(self.hyperlink(id)) - if len(node) and hasattr(node[0], 'attributes') and \ - 'std-term' in node[0].get('classes', []): + if (len(node) and + isinstance(node[0], nodes.Element) and + 'std-term' in node[0].get('classes', [])): # don't add a pageref for glossary terms self.context.append('}}}') # mark up as termreference @@ -2032,18 +1909,18 @@ class LaTeXTranslator(nodes.NodeVisitor): self.context.append('}') def depart_reference(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append(self.context.pop()) def visit_number_reference(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None if node.get('refid'): id = self.curfilestack[-1] + ':' + node['refid'] else: id = node.get('refuri', '')[1:].replace('#', ':') title = node.get('title', '%s') - title = text_type(title).translate(tex_escape_map).replace('\\%s', '%s') + title = str(title).translate(tex_escape_map).replace('\\%s', '%s') if '\\{name\\}' in title or '\\{number\\}' in title: # new style format (cf. "Fig.%{number}") title = title.replace('\\{name\\}', '{name}').replace('\\{number\\}', '{number}') @@ -2058,59 +1935,59 @@ class LaTeXTranslator(nodes.NodeVisitor): raise nodes.SkipNode def visit_download_reference(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def depart_download_reference(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def visit_pending_xref(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def depart_pending_xref(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def visit_emphasis(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append(r'\sphinxstyleemphasis{') def depart_emphasis(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('}') def visit_literal_emphasis(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append(r'\sphinxstyleliteralemphasis{\sphinxupquote{') self.no_contractions += 1 def depart_literal_emphasis(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('}}') self.no_contractions -= 1 def visit_strong(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append(r'\sphinxstylestrong{') def depart_strong(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('}') def visit_literal_strong(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append(r'\sphinxstyleliteralstrong{\sphinxupquote{') self.no_contractions += 1 def depart_literal_strong(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('}}') self.no_contractions -= 1 def visit_abbreviation(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None abbr = node.astext() self.body.append(r'\sphinxstyleabbreviation{') # spell out the explanation once @@ -2121,51 +1998,53 @@ class LaTeXTranslator(nodes.NodeVisitor): self.context.append('}') def depart_abbreviation(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append(self.context.pop()) def visit_manpage(self, node): - # type: (nodes.Node) -> Any + # type: (nodes.Element) -> None return self.visit_literal_emphasis(node) def depart_manpage(self, node): - # type: (nodes.Node) -> Any + # type: (nodes.Element) -> None return self.depart_literal_emphasis(node) def visit_title_reference(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append(r'\sphinxtitleref{') def depart_title_reference(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('}') def visit_thebibliography(self, node): - # type: (nodes.Node) -> None - longest_label = max((subnode[0].astext() for subnode in node), key=len) + # type: (thebibliography) -> 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) if len(longest_label) > MAX_CITATION_LABEL_LENGTH: # adjust max width of citation labels not to break the layout longest_label = longest_label[:MAX_CITATION_LABEL_LENGTH] - self.body.append(u'\n\\begin{sphinxthebibliography}{%s}\n' % + self.body.append('\n\\begin{sphinxthebibliography}{%s}\n' % self.encode(longest_label)) def depart_thebibliography(self, node): - # type: (nodes.Node) -> None - self.body.append(u'\\end{sphinxthebibliography}\n') + # type: (thebibliography) -> None + self.body.append('\\end{sphinxthebibliography}\n') def visit_citation(self, node): - # type: (nodes.Node) -> None - label = node[0].astext() - self.body.append(u'\\bibitem[%s]{%s:%s}' % - (self.encode(label), node['docname'], node['ids'][0])) + # type: (nodes.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.Node) -> None + # type: (nodes.Element) -> None pass def visit_citation_reference(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None if self.in_title: pass else: @@ -2173,11 +2052,11 @@ class LaTeXTranslator(nodes.NodeVisitor): raise nodes.SkipNode def depart_citation_reference(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def visit_literal(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.no_contractions += 1 if self.in_title: self.body.append(r'\sphinxstyleliteralintitle{\sphinxupquote{') @@ -2185,43 +2064,43 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append(r'\sphinxcode{\sphinxupquote{') def depart_literal(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.no_contractions -= 1 self.body.append('}}') def visit_footnote_reference(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None raise nodes.SkipNode def visit_footnotemark(self, node): - # type: (nodes.Node) -> None + # type: (footnotemark) -> None self.body.append('\\sphinxfootnotemark[') def depart_footnotemark(self, node): - # type: (nodes.Node) -> None + # type: (footnotemark) -> None self.body.append(']') def visit_footnotetext(self, node): - # type: (nodes.Node) -> None - number = node[0].astext() + # type: (footnotetext) -> None + label = cast(nodes.label, node[0]) self.body.append('%%\n\\begin{footnotetext}[%s]' - '\\sphinxAtStartFootnote\n' % number) + '\\sphinxAtStartFootnote\n' % label.astext()) def depart_footnotetext(self, node): - # type: (nodes.Node) -> None + # type: (footnotetext) -> 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: (nodes.Node) -> None + # type: (captioned_literal_block) -> None pass def depart_captioned_literal_block(self, node): - # type: (nodes.Node) -> None + # type: (captioned_literal_block) -> None pass def visit_literal_block(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None if node.rawsource != node.astext(): # most probably a parsed-literal block -- don't highlight self.in_parsed_literal += 1 @@ -2248,7 +2127,7 @@ class LaTeXTranslator(nodes.NodeVisitor): location=(self.curfilestack[-1], node.line), **highlight_args ) # workaround for Unicode issue - hlcode = hlcode.replace(u'€', u'@texteuro[]') + hlcode = hlcode.replace('€', '@texteuro[]') if self.in_footnote: self.body.append('\n\\sphinxSetupCodeBlockInFootnote') hlcode = hlcode.replace('\\begin{Verbatim}', @@ -2279,22 +2158,22 @@ class LaTeXTranslator(nodes.NodeVisitor): raise nodes.SkipNode def depart_literal_block(self, node): - # type: (nodes.Node) -> None + # type: (nodes.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.Node) -> None + # type: (nodes.Element) -> None self.body.append('\\item[] ') def depart_line(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('\n') def visit_line_block(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None if isinstance(node.parent, nodes.line_block): self.body.append('\\item[]\n' '\\begin{DUlineblock}{\\DUlineblockindent}\n') @@ -2304,11 +2183,11 @@ class LaTeXTranslator(nodes.NodeVisitor): self.table.has_problematic = True def depart_line_block(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('\\end{DUlineblock}\n') def visit_block_quote(self, node): - # type: (nodes.Node) -> None + # type: (nodes.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. @@ -2324,7 +2203,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.table.has_problematic = True def depart_block_quote(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None done = 0 if len(node.children) == 1: child = node.children[0] @@ -2337,56 +2216,56 @@ class LaTeXTranslator(nodes.NodeVisitor): # option node handling copied from docutils' latex writer def visit_option(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None if self.context[-1]: # this is not the first option self.body.append(', ') def depart_option(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None # flag that the first option is done. self.context[-1] += 1 def visit_option_argument(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None """The delimiter betweeen an option and its argument.""" self.body.append(node.get('delimiter', ' ')) def depart_option_argument(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def visit_option_group(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('\\item [') # flag for first option self.context.append(0) def depart_option_group(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.context.pop() # the flag self.body.append('] ') def visit_option_list(self, node): - # type: (nodes.Node) -> None + # type: (nodes.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.Node) -> None + # type: (nodes.Element) -> None self.body.append('\\end{optionlist}\n') def visit_option_list_item(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def depart_option_list_item(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def visit_option_string(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None ostring = node.astext() self.no_contractions += 1 self.body.append(self.encode(ostring)) @@ -2394,31 +2273,31 @@ class LaTeXTranslator(nodes.NodeVisitor): raise nodes.SkipNode def visit_description(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append(' ') def depart_description(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def visit_superscript(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('$^{\\text{') def depart_superscript(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('}}$') def visit_subscript(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('$_{\\text{') def depart_subscript(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('}}$') def visit_inline(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None classes = node.get('classes', []) if classes in [['menuselection']]: self.body.append(r'\sphinxmenuselection{') @@ -2436,102 +2315,102 @@ class LaTeXTranslator(nodes.NodeVisitor): self.context.append('') def depart_inline(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append(self.context.pop()) def visit_generated(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def depart_generated(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def visit_compound(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def depart_compound(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def visit_container(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def depart_container(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def visit_decoration(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def depart_decoration(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass # docutils-generated elements that we don't support def visit_header(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None raise nodes.SkipNode def visit_footer(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None raise nodes.SkipNode def visit_docinfo(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None raise nodes.SkipNode # text handling def encode(self, text): - # type: (unicode) -> unicode - text = text_type(text).translate(tex_escape_map) + # type: (str) -> str + text = str(text).translate(tex_escape_map) if self.literal_whitespace: # Insert a blank before the newline, to avoid # ! LaTeX Error: There's no line here to end. - text = text.replace(u'\n', u'~\\\\\n').replace(u' ', u'~') + text = text.replace('\n', '~\\\\\n').replace(' ', '~') if self.no_contractions: - text = text.replace('--', u'-{-}') - text = text.replace("''", u"'{'}") + text = text.replace('--', '-{-}') + text = text.replace("''", "'{'}") return text def encode_uri(self, text): - # type: (unicode) -> unicode + # type: (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.Node) -> None + # type: (nodes.Text) -> None text = self.encode(node.astext()) self.body.append(text) def depart_Text(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Text) -> None pass def visit_comment(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None raise nodes.SkipNode def visit_meta(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None # only valid for HTML raise nodes.SkipNode def visit_system_message(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None pass def depart_system_message(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None self.body.append('\n') def visit_math(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None if self.in_title: self.body.append(r'\protect\(%s\protect\)' % node.astext()) else: @@ -2539,7 +2418,7 @@ class LaTeXTranslator(nodes.NodeVisitor): raise nodes.SkipNode def visit_math_block(self, node): - # type: (nodes.Node) -> None + # type: (nodes.Element) -> None if node.get('label'): label = "equation:%s:%s" % (node['docname'], node['label']) else: @@ -2556,7 +2435,7 @@ class LaTeXTranslator(nodes.NodeVisitor): raise nodes.SkipNode def visit_math_reference(self, node): - # type: (nodes.Node) -> None + # type: (math_reference) -> None label = "equation:%s:%s" % (node['docname'], node['target']) eqref_format = self.builder.config.math_eqref_format if eqref_format: @@ -2571,7 +2450,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append(r'\eqref{%s}' % label) def depart_math_reference(self, node): - # type: (nodes.Node) -> None + # type: (math_reference) -> None pass def unknown_visit(self, node): @@ -2580,16 +2459,40 @@ class LaTeXTranslator(nodes.NodeVisitor): # --------- 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] + if isinstance(n, nodes.footnote): + yield n + else: + for c in n.children: + if isinstance(c, addnodes.start_of_file): + continue + elif isinstance(c, nodes.Element): + yield from footnotes_under(c) + + warnings.warn('LaTeXWriter.collected_footnote() is deprecated.', + RemovedInSphinx40Warning, stacklevel=2) + + fnotes = {} # type: Dict[str, List[Union[collected_footnote, bool]]] + for fn in footnotes_under(node): + label = cast(nodes.label, fn[0]) + num = label.astext().strip() + newnode = collected_footnote('', *fn.children, number=num) + fnotes[num] = [newnode, False] + return fnotes + @property def footnotestack(self): - # type: () -> List[Dict[unicode, List[Union[collected_footnote, bool]]]] + # type: () -> List[Dict[str, List[Union[collected_footnote, bool]]]] warnings.warn('LaTeXWriter.footnotestack is deprecated.', RemovedInSphinx30Warning, stacklevel=2) return [] @property def bibitems(self): - # type: () -> List[List[unicode]] + # type: () -> List[List[str]] warnings.warn('LaTeXTranslator.bibitems() is deprecated.', RemovedInSphinx30Warning, stacklevel=2) return [] @@ -2603,7 +2506,7 @@ class LaTeXTranslator(nodes.NodeVisitor): @property def next_section_ids(self): - # type: () -> Set[unicode] + # type: () -> Set[str] warnings.warn('LaTeXTranslator.next_section_ids is deprecated.', RemovedInSphinx30Warning, stacklevel=2) return set() @@ -2616,20 +2519,20 @@ class LaTeXTranslator(nodes.NodeVisitor): return {} def push_hyperlink_ids(self, figtype, ids): - # type: (unicode, Set[unicode]) -> None + # type: (str, Set[str]) -> None warnings.warn('LaTeXTranslator.push_hyperlink_ids() is deprecated.', RemovedInSphinx30Warning, stacklevel=2) pass def pop_hyperlink_ids(self, figtype): - # type: (unicode) -> Set[unicode] + # type: (str) -> Set[str] warnings.warn('LaTeXTranslator.pop_hyperlink_ids() is deprecated.', RemovedInSphinx30Warning, stacklevel=2) return set() @property def hlsettingstack(self): - # type: () -> List[List[Union[unicode, int]]] + # type: () -> List[List[Union[str, int]]] warnings.warn('LaTeXTranslator.hlsettingstack is deprecated.', RemovedInSphinx30Warning, stacklevel=2) return [[self.builder.config.highlight_language, sys.maxsize]] @@ -2644,12 +2547,88 @@ class LaTeXTranslator(nodes.NodeVisitor): msg = __("Unknown configure key: latex_elements[%r] is ignored.") logger.warning(msg % key) + def babel_defmacro(self, name, definition): + # type: (str, str) -> str + warnings.warn('babel_defmacro() is deprecated.', + RemovedInSphinx40Warning) + + if self.elements['babel']: + prefix = '\\addto\\extras%s{' % self.babel.get_language() + suffix = '}' + else: # babel is disabled (mainly for Japanese environment) + prefix = '' + suffix = '' + + return ('%s\\def%s{%s}%s\n' % (prefix, name, definition, suffix)) + + def _make_visit_admonition(name): # type: ignore + # type: (str) -> Callable[[LaTeXTranslator, nodes.Element], None] + warnings.warn('LaTeXTranslator._make_visit_admonition() is deprecated.', + RemovedInSphinx30Warning) + + def visit_admonition(self, node): + # type: (nodes.Element) -> None + self.body.append('\n\\begin{sphinxadmonition}{%s}{%s:}' % + (name, admonitionlabels[name])) + return visit_admonition + + def generate_numfig_format(self, builder): + # type: (LaTeXBuilder) -> str + warnings.warn('generate_numfig_format() is deprecated.', + RemovedInSphinx40Warning) + ret = [] # type: List[str] + figure = self.builder.config.numfig_format['figure'].split('%s', 1) + if len(figure) == 1: + ret.append('\\def\\fnum@figure{%s}\n' % + str(figure[0]).strip().translate(tex_escape_map)) + else: + definition = escape_abbr(str(figure[0]).translate(tex_escape_map)) + ret.append(self.babel_renewcommand('\\figurename', definition)) + ret.append('\\makeatletter\n') + ret.append('\\def\\fnum@figure{\\figurename\\thefigure{}%s}\n' % + str(figure[1]).translate(tex_escape_map)) + ret.append('\\makeatother\n') + + table = self.builder.config.numfig_format['table'].split('%s', 1) + if len(table) == 1: + ret.append('\\def\\fnum@table{%s}\n' % + str(table[0]).strip().translate(tex_escape_map)) + else: + definition = escape_abbr(str(table[0]).translate(tex_escape_map)) + ret.append(self.babel_renewcommand('\\tablename', definition)) + ret.append('\\makeatletter\n') + ret.append('\\def\\fnum@table{\\tablename\\thetable{}%s}\n' % + str(table[1]).translate(tex_escape_map)) + ret.append('\\makeatother\n') + + codeblock = self.builder.config.numfig_format['code-block'].split('%s', 1) + if len(codeblock) == 1: + pass # FIXME + else: + definition = str(codeblock[0]).strip().translate(tex_escape_map) + ret.append(self.babel_renewcommand('\\literalblockname', definition)) + if codeblock[1]: + pass # FIXME + + return ''.join(ret) + # Import old modules here for compatibility -# They should be imported after `LaTeXTranslator` to avoid recursive import. -# -# refs: https://github.com/sphinx-doc/sphinx/issues/4889 from sphinx.builders.latex.transforms import URI_SCHEMES, ShowUrlsTransform # NOQA +from sphinx.builders.latex.util import ExtBabel # NOQA + + +deprecated_alias('sphinx.writers.latex', + { + 'ShowUrlsTransform': ShowUrlsTransform, + 'URI_SCHEMES': URI_SCHEMES, + }, + RemovedInSphinx30Warning) +deprecated_alias('sphinx.writers.latex', + { + 'ExtBabel': ExtBabel, + }, + RemovedInSphinx40Warning) # FIXME: Workaround to avoid circular import # refs: https://github.com/sphinx-doc/sphinx/issues/5433 |
