diff --git a/doc/extdev/deprecated.rst b/doc/extdev/deprecated.rst index 1a461d9e6..958f4ee4d 100644 --- a/doc/extdev/deprecated.rst +++ b/doc/extdev/deprecated.rst @@ -44,12 +44,12 @@ The following is a list of deprecated interfaces. * - ``sphinx.util.texescape.tex_escape_map`` - 2.3 - 4.0 - - ``sphinx.util.texescape.get_escape_func()`` + - ``sphinx.util.texescape.escape()`` * - ``sphinx.util.texescape.tex_hl_escape_map_new`` - 2.3 - 4.0 - - ``sphinx.util.texescape.get_hlescape_func()`` + - ``sphinx.util.texescape.hlescape()`` * - ``sphinx.domains.math.MathDomain.add_equation()`` - 2.2 diff --git a/sphinx/builders/latex/__init__.py b/sphinx/builders/latex/__init__.py index 9c34b5568..02f147e40 100644 --- a/sphinx/builders/latex/__init__.py +++ b/sphinx/builders/latex/__init__.py @@ -440,10 +440,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')] 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 0a6cf8d6e..6804b9de0 100644 --- a/sphinx/highlighting.py +++ b/sphinx/highlighting.py @@ -27,8 +27,7 @@ from sphinx.deprecation import RemovedInSphinx30Warning from sphinx.ext import doctest from sphinx.locale import __ from sphinx.pygments_styles import SphinxStyle, NoneStyle -from sphinx.util import logging -from sphinx.util.texescape import get_hlescape_func, tex_hl_escape_map_new +from sphinx.util import logging, texescape if False: # For type annotation @@ -114,7 +113,7 @@ class PygmentsBridge: # first, escape highlighting characters like Pygments does source = source.translate(escape_hl_chars) # then, escape all characters nonrepresentable in LaTeX - source = source.translate(tex_hl_escape_map_new) + source = texescape.escape(source, self.latex_engine) return '\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n' + \ source + '\\end{Verbatim}\n' @@ -194,8 +193,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/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 3b9494892..1b44532f5 100644 --- a/sphinx/util/texescape.py +++ b/sphinx/util/texescape.py @@ -9,7 +9,7 @@ """ import re -from typing import Callable, Dict +from typing import Dict from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias @@ -97,40 +97,22 @@ deprecated_alias('sphinx.util.texescape', RemovedInSphinx40Warning) -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 - - -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_hlescape_map) - - -def hlescape_for_unicode_latex_engine(s: str) -> str: - """Escape text for unicode supporting LaTeX engine.""" - return s.translate(_tex_hlescape_map_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: diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 5facdc40c..5641c08f8 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -28,11 +28,11 @@ from sphinx.deprecation import ( 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 @@ -500,9 +500,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() @@ -736,6 +733,9 @@ class LaTeXTranslator(SphinxTranslator): # type: (str) -> str return '\\autopageref*{%s}' % self.idescape(id) + def escape(self, s: str) -> str: + return texescape.escape(s, self.config.latex_engine) + def idescape(self, id): # type: (str) -> str return '\\detokenize{%s}' % str(id).translate(tex_replace_map).\ diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 0ebcd0b62..38e99f48a 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -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