Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0897b59

Browse files
committed
Ensure user-supplied files are always copied
1 parent 2f9a79c commit 0897b59

2 files changed

Lines changed: 64 additions & 27 deletions

File tree

sphinx/builders/html/__init__.py

Lines changed: 62 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import types
1414
import warnings
1515
from os import path
16+
from pathlib import Path
1617
from typing import IO, TYPE_CHECKING, Any
1718
from urllib.parse import quote
1819

@@ -756,35 +757,38 @@ def write_domain_indices(self) -> None:
756757
def copy_image_files(self) -> None:
757758
if self.images:
758759
stringify_func = ImageAdapter(self.app.env).get_original_image_uri
759-
ensuredir(path.join(self.outdir, self.imagedir))
760+
ensuredir(self.outdir / self.imagedir)
760761
for src in status_iterator(self.images, __('copying images... '), "brown",
761762
len(self.images), self.app.verbosity,
762763
stringify_func=stringify_func):
763764
dest = self.images[src]
764765
try:
765-
copyfile(path.join(self.srcdir, src),
766-
path.join(self.outdir, self.imagedir, dest))
766+
copyfile(
767+
self.srcdir / src,
768+
self.outdir / self.imagedir / dest,
769+
force=True,
770+
)
767771
except Exception as err:
768772
logger.warning(__('cannot copy image file %r: %s'),
769-
path.join(self.srcdir, src), err)
773+
self.srcdir / src, err)
770774

771775
def copy_download_files(self) -> None:
772776
def to_relpath(f: str) -> str:
773777
return relative_path(self.srcdir, f)
774778

775779
# copy downloadable files
776780
if self.env.dlfiles:
777-
ensuredir(path.join(self.outdir, '_downloads'))
781+
ensuredir(self.outdir / '_downloads')
778782
for src in status_iterator(self.env.dlfiles, __('copying downloadable files... '),
779783
"brown", len(self.env.dlfiles), self.app.verbosity,
780784
stringify_func=to_relpath):
781785
try:
782-
dest = path.join(self.outdir, '_downloads', self.env.dlfiles[src][1])
783-
ensuredir(path.dirname(dest))
784-
copyfile(path.join(self.srcdir, src), dest)
786+
dest = self.outdir / '_downloads' / self.env.dlfiles[src][1]
787+
ensuredir(dest.parent)
788+
copyfile(self.srcdir / src, dest, force=True)
785789
except OSError as err:
786790
logger.warning(__('cannot copy downloadable file %r: %s'),
787-
path.join(self.srcdir, src), err)
791+
self.srcdir / src, err)
788792

789793
def create_pygments_style_file(self) -> None:
790794
"""Create a style file for pygments."""
@@ -801,18 +805,30 @@ def copy_translation_js(self) -> None:
801805
"""Copy a JavaScript file for translations."""
802806
jsfile = self._get_translations_js()
803807
if jsfile:
804-
copyfile(jsfile, path.join(self.outdir, '_static', 'translations.js'))
808+
copyfile(
809+
jsfile,
810+
self.outdir / '_static' / 'translations.js',
811+
force=True,
812+
)
805813

806814
def copy_stemmer_js(self) -> None:
807815
"""Copy a JavaScript file for stemmer."""
808816
if self.indexer is not None:
809817
if hasattr(self.indexer, 'get_js_stemmer_rawcodes'):
810818
for jsfile in self.indexer.get_js_stemmer_rawcodes():
811-
copyfile(jsfile, path.join(self.outdir, '_static', path.basename(jsfile)))
819+
js_path = Path(jsfile)
820+
copyfile(
821+
js_path,
822+
self.outdir / '_static' / js_path.name,
823+
force=True,
824+
)
812825
else:
813826
if js_stemmer_rawcode := self.indexer.get_js_stemmer_rawcode():
814-
copyfile(js_stemmer_rawcode,
815-
path.join(self.outdir, '_static', '_stemmer.js'))
827+
copyfile(
828+
js_stemmer_rawcode,
829+
self.outdir / '_static' / '_stemmer.js',
830+
force=True,
831+
)
816832

817833
def copy_theme_static_files(self, context: dict[str, Any]) -> None:
818834
def onerror(filename: str, error: Exception) -> None:
@@ -821,10 +837,13 @@ def onerror(filename: str, error: Exception) -> None:
821837

822838
if self.theme:
823839
for entry in reversed(self.theme.get_theme_dirs()):
824-
copy_asset(path.join(entry, 'static'),
825-
path.join(self.outdir, '_static'),
826-
excluded=DOTFILES, context=context,
827-
renderer=self.templates, onerror=onerror)
840+
copy_asset(
841+
Path(entry) / 'static',
842+
self.outdir / '_static',
843+
excluded=DOTFILES, context=context,
844+
renderer=self.templates, onerror=onerror,
845+
force=True,
846+
)
828847

829848
def copy_html_static_files(self, context: dict[str, Any]) -> None:
830849
def onerror(filename: str, error: Exception) -> None:
@@ -833,24 +852,36 @@ def onerror(filename: str, error: Exception) -> None:
833852

834853
excluded = Matcher([*self.config.exclude_patterns, '**/.*'])
835854
for entry in self.config.html_static_path:
836-
copy_asset(path.join(self.confdir, entry),
837-
path.join(self.outdir, '_static'),
838-
excluded, context=context, renderer=self.templates, onerror=onerror)
855+
copy_asset(
856+
self.confdir / entry,
857+
self.outdir / '_static',
858+
excluded=excluded, context=context,
859+
renderer=self.templates, onerror=onerror,
860+
force=True,
861+
)
839862

840863
def copy_html_logo(self) -> None:
841864
if self.config.html_logo and not isurl(self.config.html_logo):
842-
copy_asset(path.join(self.confdir, self.config.html_logo),
843-
path.join(self.outdir, '_static'))
865+
source_path = self.confdir / self.config.html_logo
866+
copyfile(
867+
source_path,
868+
self.outdir / '_static' / source_path.name,
869+
force=True,
870+
)
844871

845872
def copy_html_favicon(self) -> None:
846873
if self.config.html_favicon and not isurl(self.config.html_favicon):
847-
copy_asset(path.join(self.confdir, self.config.html_favicon),
848-
path.join(self.outdir, '_static'))
874+
source_path = self.confdir / self.config.html_favicon
875+
copyfile(
876+
source_path,
877+
self.outdir / '_static' / source_path.name,
878+
force=True,
879+
)
849880

850881
def copy_static_files(self) -> None:
851882
try:
852883
with progress_message(__('copying static files')):
853-
ensuredir(path.join(self.outdir, '_static'))
884+
ensuredir(self.outdir / '_static')
854885

855886
# prepare context for templates
856887
context = self.globalcontext.copy()
@@ -873,8 +904,12 @@ def copy_extra_files(self) -> None:
873904
with progress_message(__('copying extra files')):
874905
excluded = Matcher(self.config.exclude_patterns)
875906
for extra_path in self.config.html_extra_path:
876-
entry = path.join(self.confdir, extra_path)
877-
copy_asset(entry, self.outdir, excluded)
907+
copy_asset(
908+
self.confdir / extra_path,
909+
self.outdir,
910+
excluded=excluded,
911+
force=True,
912+
)
878913
except OSError as err:
879914
logger.warning(__('cannot copy extra file %r'), err)
880915

sphinx/util/fileutil.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ def copy_asset(source: str | os.PathLike[str], destination: str | os.PathLike[st
9696
On copying, it expands the template variables if context argument is given and
9797
the asset is a template file.
9898
99+
Use ``copy_asset_file`` instead to copy a single file.
100+
99101
:param source: The path to source file or directory
100102
:param destination: The path to destination directory
101103
:param excluded: The matcher to determine the given path should be copied or not

0 commit comments

Comments
 (0)