diff --git a/.ruff.toml b/.ruff.toml index 351b05a37..c2b62f25b 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -415,7 +415,6 @@ exclude = [ "sphinx/domains/python/_object.py", "sphinx/domains/rst.py", "sphinx/domains/std/__init__.py", - "sphinx/ext/imgmath.py", "sphinx/ext/inheritance_diagram.py", "sphinx/ext/linkcode.py", "sphinx/ext/mathjax.py", diff --git a/sphinx/ext/imgmath.py b/sphinx/ext/imgmath.py index 2a06dd208..df7cc9e1e 100644 --- a/sphinx/ext/imgmath.py +++ b/sphinx/ext/imgmath.py @@ -29,7 +29,6 @@ from sphinx.util.png import read_png_depth, write_png_depth from sphinx.util.template import LaTeXRenderer if TYPE_CHECKING: - from docutils.nodes import Element from sphinx.application import Sphinx @@ -47,7 +46,7 @@ class MathExtError(SphinxError): category = 'Math extension error' def __init__( - self, msg: str, stderr: str | None = None, stdout: str | None = None, + self, msg: str, stderr: str | None = None, stdout: str | None = None ) -> None: if stderr: msg += '\n[stderr]\n' + stderr @@ -68,9 +67,8 @@ depthsvgcomment_re = re.compile(r'') def read_svg_depth(filename: str) -> int | None: - """Read the depth from comment at last line of SVG file - """ - with open(filename, encoding="utf-8") as f: + """Read the depth from comment at last line of SVG file""" + with open(filename, encoding='utf-8') as f: for line in f: # NoQA: B007 pass # Only last line is checked @@ -81,16 +79,17 @@ def read_svg_depth(filename: str) -> int | None: def write_svg_depth(filename: str, depth: int) -> None: - """Write the depth to SVG file as a comment at end of file - """ - with open(filename, 'a', encoding="utf-8") as f: + """Write the depth to SVG file as a comment at end of file""" + with open(filename, 'a', encoding='utf-8') as f: f.write('\n' % depth) -def generate_latex_macro(image_format: str, - math: str, - config: Config, - confdir: str | os.PathLike[str] = '') -> str: +def generate_latex_macro( + image_format: str, + math: str, + config: Config, + confdir: str | os.PathLike[str] = '', +) -> str: """Generate LaTeX macro.""" variables = { 'fontsize': config.imgmath_font_size, @@ -109,7 +108,9 @@ def generate_latex_macro(image_format: str, for template_dir in config.templates_path: for template_suffix in ('.jinja', '_t'): - template = os.path.join(confdir, template_dir, template_name + template_suffix) + template = os.path.join( + confdir, template_dir, template_name + template_suffix + ) if os.path.exists(template): return LaTeXRenderer().render(template, variables) @@ -149,16 +150,21 @@ def compile_math(latex: str, builder: Builder) -> str: command.append('math.tex') try: - subprocess.run(command, capture_output=True, cwd=tempdir, check=True, - encoding='ascii') + subprocess.run( + command, capture_output=True, cwd=tempdir, check=True, encoding='ascii' + ) if imgmath_latex_name in {'xelatex', 'tectonic'}: return os.path.join(tempdir, 'math.xdv') else: return os.path.join(tempdir, 'math.dvi') except OSError as exc: - logger.warning(__('LaTeX command %r cannot be run (needed for math ' - 'display), check the imgmath_latex setting'), - builder.config.imgmath_latex) + logger.warning( + __( + 'LaTeX command %r cannot be run (needed for math ' + 'display), check the imgmath_latex setting' + ), + builder.config.imgmath_latex, + ) raise InvokeError from exc except CalledProcessError as exc: msg = 'latex exited with error' @@ -171,12 +177,19 @@ def convert_dvi_to_image(command: list[str], name: str) -> tuple[str, str]: ret = subprocess.run(command, capture_output=True, check=True, encoding='ascii') return ret.stdout, ret.stderr except OSError as exc: - logger.warning(__('%s command %r cannot be run (needed for math ' - 'display), check the imgmath_%s setting'), - name, command[0], name) + logger.warning( + __( + '%s command %r cannot be run (needed for math ' + 'display), check the imgmath_%s setting' + ), + name, + command[0], + name, + ) raise InvokeError from exc except CalledProcessError as exc: - raise MathExtError('%s exited with error' % name, exc.stderr, exc.stdout) from exc + msg = f'{name} exited with error' + raise MathExtError(msg, exc.stderr, exc.stdout) from exc def convert_dvi_to_png(dvipath: str, builder: Builder, out_path: str) -> int | None: @@ -245,13 +258,16 @@ def render_math( unsupported_format_msg = 'imgmath_image_format must be either "png" or "svg"' raise MathExtError(unsupported_format_msg) - latex = generate_latex_macro(image_format, - math, - self.builder.config, - self.builder.confdir) + latex = generate_latex_macro( + image_format, math, self.builder.config, self.builder.confdir + ) - filename = f"{sha1(latex.encode(), usedforsecurity=False).hexdigest()}.{image_format}" - generated_path = os.path.join(self.builder.outdir, self.builder.imagedir, 'math', filename) + filename = ( + f'{sha1(latex.encode(), usedforsecurity=False).hexdigest()}.{image_format}' + ) + generated_path = os.path.join( + self.builder.outdir, self.builder.imagedir, 'math', filename + ) ensuredir(os.path.dirname(generated_path)) if os.path.isfile(generated_path): if image_format == 'png': @@ -261,8 +277,9 @@ def render_math( return generated_path, depth # if latex or dvipng (dvisvgm) has failed once, don't bother to try again - if hasattr(self.builder, '_imgmath_warned_latex') or \ - hasattr(self.builder, '_imgmath_warned_image_translator'): + latex_failed = hasattr(self.builder, '_imgmath_warned_latex') + trans_failed = hasattr(self.builder, '_imgmath_warned_image_translator') + if latex_failed or trans_failed: return None, None # .tex -> .dvi @@ -286,7 +303,7 @@ def render_math( def render_maths_to_base64(image_format: str, generated_path: str) -> str: - with open(generated_path, "rb") as f: + with open(generated_path, 'rb') as f: encoded = base64.b64encode(f.read()).decode(encoding='utf-8') if image_format == 'png': return f'data:image/png;base64,{encoded}' @@ -308,12 +325,14 @@ def clean_up_files(app: Sphinx, exc: Exception) -> None: # in embed mode, the images are still generated in the math output dir # to be shared across workers, but are not useful to the final document with contextlib.suppress(Exception): - shutil.rmtree(os.path.join(app.builder.outdir, app.builder.imagedir, 'math')) + shutil.rmtree( + os.path.join(app.builder.outdir, app.builder.imagedir, 'math') + ) def get_tooltip(self: HTML5Translator, node: Element) -> str: if self.builder.config.imgmath_add_tooltips: - return ' alt="%s"' % self.encode(node.astext()).strip() + return f' alt="{self.encode(node.astext()).strip()}"' return '' @@ -322,16 +341,18 @@ def html_visit_math(self: HTML5Translator, node: nodes.math) -> None: rendered_path, depth = render_math(self, '$' + node.astext() + '$') except MathExtError as exc: msg = str(exc) - sm = nodes.system_message(msg, type='WARNING', level=2, - backrefs=[], source=node.astext()) + sm = nodes.system_message( + msg, type='WARNING', level=2, backrefs=[], source=node.astext() + ) sm.walkabout(self) logger.warning(__('display latex %r: %s'), node.astext(), msg) raise nodes.SkipNode from exc if rendered_path is None: # something failed -- use text-only as a bad substitute - self.body.append('%s' % - self.encode(node.astext()).strip()) + self.body.append( + f'{self.encode(node.astext()).strip()}' + ) else: if self.builder.config.imgmath_embed: image_format = self.builder.config.imgmath_image_format.lower() @@ -340,10 +361,10 @@ def html_visit_math(self: HTML5Translator, node: nodes.math) -> None: bname = os.path.basename(rendered_path) relative_path = os.path.join(self.builder.imgpath, 'math', bname) img_src = relative_path.replace(os.path.sep, '/') - c = f'') + align = f' style="vertical-align: {-depth:d}px"' if depth is not None else '' + self.body.append( + f'' + ) raise nodes.SkipNode @@ -356,8 +377,9 @@ def html_visit_displaymath(self: HTML5Translator, node: nodes.math_block) -> Non rendered_path, depth = render_math(self, latex) except MathExtError as exc: msg = str(exc) - sm = nodes.system_message(msg, type='WARNING', level=2, - backrefs=[], source=node.astext()) + sm = nodes.system_message( + msg, type='WARNING', level=2, backrefs=[], source=node.astext() + ) sm.walkabout(self) logger.warning(__('inline latex %r: %s'), node.astext(), msg) raise nodes.SkipNode from exc @@ -371,8 +393,9 @@ def html_visit_displaymath(self: HTML5Translator, node: nodes.math_block) -> Non if rendered_path is None: # something failed -- use text-only as a bad substitute - self.body.append('%s

\n' % - self.encode(node.astext()).strip()) + self.body.append( + f'{self.encode(node.astext()).strip()}

\n' + ) else: if self.builder.config.imgmath_embed: image_format = self.builder.config.imgmath_image_format.lower() @@ -381,24 +404,27 @@ def html_visit_displaymath(self: HTML5Translator, node: nodes.math_block) -> Non bname = os.path.basename(rendered_path) relative_path = os.path.join(self.builder.imgpath, 'math', bname) img_src = relative_path.replace(os.path.sep, '/') - self.body.append(f'

\n') + self.body.append(f'

\n') raise nodes.SkipNode def setup(app: Sphinx) -> ExtensionMetadata: - app.add_html_math_renderer('imgmath', - (html_visit_math, None), - (html_visit_displaymath, None)) + app.add_html_math_renderer( + 'imgmath', + inline_renderers=(html_visit_math, None), + block_renderers=(html_visit_displaymath, None), + ) app.add_config_value('imgmath_image_format', 'png', 'html') app.add_config_value('imgmath_dvipng', 'dvipng', 'html') app.add_config_value('imgmath_dvisvgm', 'dvisvgm', 'html') app.add_config_value('imgmath_latex', 'latex', 'html') app.add_config_value('imgmath_use_preview', False, 'html') - app.add_config_value('imgmath_dvipng_args', - ['-gamma', '1.5', '-D', '110', '-bg', 'Transparent'], - 'html') + app.add_config_value( + 'imgmath_dvipng_args', + ['-gamma', '1.5', '-D', '110', '-bg', 'Transparent'], + 'html', + ) app.add_config_value('imgmath_dvisvgm_args', ['--no-fonts'], 'html') app.add_config_value('imgmath_latex_args', [], 'html') app.add_config_value('imgmath_latex_preamble', '', 'html')