Skip to content

Commit

Permalink
Verify that an asset file to be copied exists (sphinx-doc#12183)
Browse files Browse the repository at this point in the history
  • Loading branch information
picnixz authored Mar 23, 2024
1 parent b1548d0 commit d91ba11
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 2 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ Bugs fixed
may be used,
when multiple suffixes are specified in :confval:`source_suffix`.
Patch by Sutou Kouhei.
* #10786: improve the error message when a file to be copied (e.g., an asset)
is removed during Sphinx execution.
Patch by Bénédikt Tran.

Testing
-------
Expand Down
13 changes: 11 additions & 2 deletions sphinx/util/osutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
import unicodedata
from io import StringIO
from os import path
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING

from sphinx.deprecation import _deprecation_warning

if TYPE_CHECKING:
from collections.abc import Iterator
from types import TracebackType
from typing import Any

# SEP separates path elements in the canonical file names
#
Expand Down Expand Up @@ -89,8 +90,16 @@ def copytimes(source: str | os.PathLike[str], dest: str | os.PathLike[str]) -> N
def copyfile(source: str | os.PathLike[str], dest: str | os.PathLike[str]) -> None:
"""Copy a file and its modification times, if possible.
Note: ``copyfile`` skips copying if the file has not been changed
:param source: An existing source to copy.
:param dest: The destination path.
:raise FileNotFoundError: The *source* does not exist.
.. note:: :func:`copyfile` is a no-op if *source* and *dest* are identical.
"""
if not path.exists(source):
msg = f'{os.fsdecode(source)} does not exist'
raise FileNotFoundError(msg)

if not path.exists(dest) or not filecmp.cmp(source, dest):
shutil.copyfile(source, dest)
with contextlib.suppress(OSError):
Expand Down
24 changes: 24 additions & 0 deletions tests/test_builders/test_build_html.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Test the HTML builder and check output against XPath."""

import os
import posixpath
import re

Expand All @@ -8,6 +9,7 @@
from sphinx.builders.html import validate_html_extra_path, validate_html_static_path
from sphinx.deprecation import RemovedInSphinx80Warning
from sphinx.errors import ConfigError
from sphinx.util.console import strip_colors
from sphinx.util.inventory import InventoryFile

FIGURE_CAPTION = ".//figure/figcaption/p"
Expand Down Expand Up @@ -387,3 +389,25 @@ def test_html_signaturereturn_icon(app):
content = (app.outdir / 'index.html').read_text(encoding='utf8')

assert ('<span class="sig-return-icon">&#x2192;</span>' in content)


@pytest.mark.sphinx('html', testroot='root', srcdir=os.urandom(4).hex())
def test_html_remove_sources_before_write_gh_issue_10786(app, warning):
# see: https://github.com/sphinx-doc/sphinx/issues/10786
target = app.srcdir / 'img.png'

def handler(app):
assert target.exists()
target.unlink()
return []

app.connect('html-collect-pages', handler)
assert target.exists()
app.build()
assert not target.exists()

ws = strip_colors(warning.getvalue()).splitlines()
assert len(ws) >= 1

file = os.fsdecode(target)
assert f'WARNING: cannot copy image file {file!r}: {file!s} does not exist' == ws[-1]

0 comments on commit d91ba11

Please sign in to comment.