diff --git a/CHANGES b/CHANGES index b170ea01b4c..04eed21c5fa 100644 --- a/CHANGES +++ b/CHANGES @@ -29,9 +29,30 @@ Bugs fixed Testing -------- -Release 6.1.2 (in development) +Release 6.1.3 (in development) ============================== +Dependencies +------------ + +Incompatible changes +-------------------- + +Deprecated +---------- + +Features added +-------------- + +Bugs fixed +---------- + +Testing +-------- + +Release 6.1.2 (released Jan 07, 2023) +===================================== + Bugs fixed ---------- @@ -45,6 +66,7 @@ Bugs fixed margin since Sphinx 5.1.0 .. _contents: https://docutils.sourceforge.io/docs/ref/rst/directives.html#table-of-contents +* #11100: Fix copying images when running under parallel mode. Release 6.1.1 (released Jan 05, 2023) ===================================== diff --git a/pyproject.toml b/pyproject.toml index fa3b3bb95f5..5884b7fe329 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -162,9 +162,12 @@ ignore = [ # flake8-bandit "S101", # assert used "S105", # possible hardcoded password + "S113", # probable use of requests call without timeout + "S324", # probable use of insecure hash functions # flake8-simplify "SIM102", # nested 'if' statements "SIM105", # use contextlib.suppress + "SIM108", # use ternary operator "SIM117", # use single 'with' statement ] external = [ # Whitelist for RUF100 unkown code warnings diff --git a/sphinx/builders/_epub_base.py b/sphinx/builders/_epub_base.py index ef857e9d358..7174914bb6c 100644 --- a/sphinx/builders/_epub_base.py +++ b/sphinx/builders/_epub_base.py @@ -401,9 +401,12 @@ def copy_image_files_pil(self) -> None: the format and resizing the image if necessary/possible. """ ensuredir(path.join(self.outdir, self.imagedir)) - for src in status_iterator(self.images, __('copying images... '), "brown", - len(self.images), self.app.verbosity): - dest = self.images[src] + converted_images = {*self.env.original_image_uri.values()} + for src in status_iterator(self.env.images, __('copying images... '), "brown", + len(self.env.images), self.app.verbosity): + if src in converted_images: + continue + _docnames, dest = self.env.images[src] try: img = Image.open(path.join(self.srcdir, src)) except OSError: @@ -438,7 +441,7 @@ def copy_image_files(self) -> None: """Copy image files to destination directory. This overwritten method can use Pillow to convert image files. """ - if self.images: + if self.env.images: if self.config.epub_fix_images or self.config.epub_max_image_width: if not Image: logger.warning(__('Pillow not found - copying image files')) diff --git a/sphinx/builders/html/__init__.py b/sphinx/builders/html/__init__.py index 0fb64decdf4..063d194713d 100644 --- a/sphinx/builders/html/__init__.py +++ b/sphinx/builders/html/__init__.py @@ -764,13 +764,16 @@ def write_domain_indices(self) -> None: self.handle_page(indexname, indexcontext, 'domainindex.html') def copy_image_files(self) -> None: - if self.images: + if self.env.images: + converted_images = {*self.env.original_image_uri.values()} stringify_func = ImageAdapter(self.app.env).get_original_image_uri ensuredir(path.join(self.outdir, self.imagedir)) - for src in status_iterator(self.images, __('copying images... '), "brown", - len(self.images), self.app.verbosity, + for src in status_iterator(self.env.images, __('copying images... '), "brown", + len(self.env.images), self.app.verbosity, stringify_func=stringify_func): - dest = self.images[src] + if src in converted_images: + continue + _docnames, dest = self.env.images[src] try: copyfile(path.join(self.srcdir, src), path.join(self.outdir, self.imagedir, dest)) diff --git a/sphinx/builders/latex/__init__.py b/sphinx/builders/latex/__init__.py index edc314dc93f..0a9de19e54c 100644 --- a/sphinx/builders/latex/__init__.py +++ b/sphinx/builders/latex/__init__.py @@ -413,12 +413,15 @@ def copy_latex_additional_files(self) -> None: copy_asset_file(path.join(self.confdir, filename), self.outdir) def copy_image_files(self) -> None: - if self.images: + if self.env.images: + converted_images = {*self.env.original_image_uri.values()} stringify_func = ImageAdapter(self.app.env).get_original_image_uri - for src in status_iterator(self.images, __('copying images... '), "brown", - len(self.images), self.app.verbosity, + for src in status_iterator(self.env.images, __('copying images... '), "brown", + len(self.env.images), self.app.verbosity, stringify_func=stringify_func): - dest = self.images[src] + if src in converted_images: + continue + _docnames, dest = self.env.images[src] try: copy_asset_file(path.join(self.srcdir, src), path.join(self.outdir, dest)) diff --git a/sphinx/builders/texinfo.py b/sphinx/builders/texinfo.py index d2ae72ef7b6..19aeaa5ea8e 100644 --- a/sphinx/builders/texinfo.py +++ b/sphinx/builders/texinfo.py @@ -173,12 +173,15 @@ def finish(self) -> None: self.copy_support_files() def copy_image_files(self, targetname: str) -> None: - if self.images: + if self.env.images: + converted_images = {*self.env.original_image_uri.values()} stringify_func = ImageAdapter(self.app.env).get_original_image_uri - for src in status_iterator(self.images, __('copying images... '), "brown", - len(self.images), self.app.verbosity, + for src in status_iterator(self.env.images, __('copying images... '), "brown", + len(self.env.images), self.app.verbosity, stringify_func=stringify_func): - dest = self.images[src] + if src in converted_images: + continue + _docnames, dest = self.env.images[src] try: imagedir = path.join(self.outdir, targetname + '-figures') ensuredir(imagedir) diff --git a/tests/test_build_epub.py b/tests/test_build_epub.py index a50c51e255f..86fdae0cd68 100644 --- a/tests/test_build_epub.py +++ b/tests/test_build_epub.py @@ -2,6 +2,7 @@ import os import subprocess +from pathlib import Path from subprocess import CalledProcessError from xml.etree import ElementTree @@ -390,3 +391,22 @@ def test_xml_name_pattern_check(): assert _XML_NAME_PATTERN.match('id-pub') assert _XML_NAME_PATTERN.match('webpage') assert not _XML_NAME_PATTERN.match('1bfda21') + + +@pytest.mark.sphinx('epub', testroot='images') +def test_copy_images(app, status, warning): + app.build() + + images_dir = Path(app.outdir) / '_images' + images = {image.name for image in images_dir.rglob('*')} + assert images == { + 'img.gif', + 'img.pdf', + 'img.png', + 'python-logo.png', + 'rimg.png', + 'rimg1.png', + 'svgimg.pdf', + 'svgimg.svg', + 'testimäge.png', + } diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 5eed5d5eaf2..89350e82d0c 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -3,6 +3,7 @@ import os import re from itertools import chain, cycle +from pathlib import Path from unittest.mock import ANY, call, patch import pytest @@ -1770,3 +1771,21 @@ def test_theme_having_multiple_stylesheets(app): assert '' in content assert '' in content + + +@pytest.mark.sphinx('html', testroot='images') +def test_copy_images(app, status, warning): + app.build() + + images_dir = Path(app.outdir) / '_images' + images = {image.name for image in images_dir.rglob('*')} + assert images == { + 'img.gif', + 'img.pdf', + 'img.png', + 'rimg.png', + 'rimg1.png', + 'svgimg.pdf', + 'svgimg.svg', + 'testimäge.png', + } diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index f20fc67c585..3f1206ac8b8 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -4,6 +4,7 @@ import re import subprocess from itertools import product +from pathlib import Path from shutil import copyfile from subprocess import CalledProcessError @@ -1670,3 +1671,25 @@ def test_latex_code_role(app): common_content + '%\n}} code block') in content assert (r'\begin{sphinxVerbatim}[commandchars=\\\{\}]' + '\n' + common_content + '\n' + r'\end{sphinxVerbatim}') in content + + +@pytest.mark.sphinx('latex', testroot='images') +def test_copy_images(app, status, warning): + app.build() + + test_dir = Path(app.outdir) + images = { + image.name for image in test_dir.rglob('*') + if image.suffix in {'.gif', '.pdf', '.png', '.svg'} + } + assert images == { + 'img.gif', + 'img.pdf', + 'img.png', + 'python-logo.png', + 'rimg.png', + 'rimg1.png', + 'svgimg.pdf', + 'svgimg.svg', + 'testimäge.png', + } diff --git a/tests/test_build_texinfo.py b/tests/test_build_texinfo.py index 974cb1965f5..aae5689b709 100644 --- a/tests/test_build_texinfo.py +++ b/tests/test_build_texinfo.py @@ -3,6 +3,7 @@ import os import re import subprocess +from pathlib import Path from subprocess import CalledProcessError from unittest.mock import Mock @@ -137,3 +138,22 @@ def test_texinfo_samp_with_variable(app, status, warning): assert '@code{@var{variable_only}}' in output assert '@code{@var{variable} and text}' in output assert '@code{Show @var{variable} in the middle}' in output + + +@pytest.mark.sphinx('texinfo', testroot='images') +def test_copy_images(app, status, warning): + app.build() + + images_dir = Path(app.outdir) / 'python-figures' + images = {image.name for image in images_dir.rglob('*')} + assert images == { + 'img.gif', + 'img.pdf', + 'img.png', + 'python-logo.png', + 'rimg.png', + 'rimg1.png', + 'svgimg.pdf', + 'svgimg.svg', + 'testimäge.png', + }