Ensure user-supplied files are always copied

This commit is contained in:
Adam Turner 2024-07-23 03:12:35 +01:00
parent 2f9a79c3b2
commit 0897b5948b
2 changed files with 64 additions and 27 deletions

View File

@ -13,6 +13,7 @@ import time
import types
import warnings
from os import path
from pathlib import Path
from typing import IO, TYPE_CHECKING, Any
from urllib.parse import quote
@ -756,17 +757,20 @@ class StandaloneHTMLBuilder(Builder):
def copy_image_files(self) -> None:
if self.images:
stringify_func = ImageAdapter(self.app.env).get_original_image_uri
ensuredir(path.join(self.outdir, self.imagedir))
ensuredir(self.outdir / self.imagedir)
for src in status_iterator(self.images, __('copying images... '), "brown",
len(self.images), self.app.verbosity,
stringify_func=stringify_func):
dest = self.images[src]
try:
copyfile(path.join(self.srcdir, src),
path.join(self.outdir, self.imagedir, dest))
copyfile(
self.srcdir / src,
self.outdir / self.imagedir / dest,
force=True,
)
except Exception as err:
logger.warning(__('cannot copy image file %r: %s'),
path.join(self.srcdir, src), err)
self.srcdir / src, err)
def copy_download_files(self) -> None:
def to_relpath(f: str) -> str:
@ -774,17 +778,17 @@ class StandaloneHTMLBuilder(Builder):
# copy downloadable files
if self.env.dlfiles:
ensuredir(path.join(self.outdir, '_downloads'))
ensuredir(self.outdir / '_downloads')
for src in status_iterator(self.env.dlfiles, __('copying downloadable files... '),
"brown", len(self.env.dlfiles), self.app.verbosity,
stringify_func=to_relpath):
try:
dest = path.join(self.outdir, '_downloads', self.env.dlfiles[src][1])
ensuredir(path.dirname(dest))
copyfile(path.join(self.srcdir, src), dest)
dest = self.outdir / '_downloads' / self.env.dlfiles[src][1]
ensuredir(dest.parent)
copyfile(self.srcdir / src, dest, force=True)
except OSError as err:
logger.warning(__('cannot copy downloadable file %r: %s'),
path.join(self.srcdir, src), err)
self.srcdir / src, err)
def create_pygments_style_file(self) -> None:
"""Create a style file for pygments."""
@ -801,18 +805,30 @@ class StandaloneHTMLBuilder(Builder):
"""Copy a JavaScript file for translations."""
jsfile = self._get_translations_js()
if jsfile:
copyfile(jsfile, path.join(self.outdir, '_static', 'translations.js'))
copyfile(
jsfile,
self.outdir / '_static' / 'translations.js',
force=True,
)
def copy_stemmer_js(self) -> None:
"""Copy a JavaScript file for stemmer."""
if self.indexer is not None:
if hasattr(self.indexer, 'get_js_stemmer_rawcodes'):
for jsfile in self.indexer.get_js_stemmer_rawcodes():
copyfile(jsfile, path.join(self.outdir, '_static', path.basename(jsfile)))
js_path = Path(jsfile)
copyfile(
js_path,
self.outdir / '_static' / js_path.name,
force=True,
)
else:
if js_stemmer_rawcode := self.indexer.get_js_stemmer_rawcode():
copyfile(js_stemmer_rawcode,
path.join(self.outdir, '_static', '_stemmer.js'))
copyfile(
js_stemmer_rawcode,
self.outdir / '_static' / '_stemmer.js',
force=True,
)
def copy_theme_static_files(self, context: dict[str, Any]) -> None:
def onerror(filename: str, error: Exception) -> None:
@ -821,10 +837,13 @@ class StandaloneHTMLBuilder(Builder):
if self.theme:
for entry in reversed(self.theme.get_theme_dirs()):
copy_asset(path.join(entry, 'static'),
path.join(self.outdir, '_static'),
excluded=DOTFILES, context=context,
renderer=self.templates, onerror=onerror)
copy_asset(
Path(entry) / 'static',
self.outdir / '_static',
excluded=DOTFILES, context=context,
renderer=self.templates, onerror=onerror,
force=True,
)
def copy_html_static_files(self, context: dict[str, Any]) -> None:
def onerror(filename: str, error: Exception) -> None:
@ -833,24 +852,36 @@ class StandaloneHTMLBuilder(Builder):
excluded = Matcher([*self.config.exclude_patterns, '**/.*'])
for entry in self.config.html_static_path:
copy_asset(path.join(self.confdir, entry),
path.join(self.outdir, '_static'),
excluded, context=context, renderer=self.templates, onerror=onerror)
copy_asset(
self.confdir / entry,
self.outdir / '_static',
excluded=excluded, context=context,
renderer=self.templates, onerror=onerror,
force=True,
)
def copy_html_logo(self) -> None:
if self.config.html_logo and not isurl(self.config.html_logo):
copy_asset(path.join(self.confdir, self.config.html_logo),
path.join(self.outdir, '_static'))
source_path = self.confdir / self.config.html_logo
copyfile(
source_path,
self.outdir / '_static' / source_path.name,
force=True,
)
def copy_html_favicon(self) -> None:
if self.config.html_favicon and not isurl(self.config.html_favicon):
copy_asset(path.join(self.confdir, self.config.html_favicon),
path.join(self.outdir, '_static'))
source_path = self.confdir / self.config.html_favicon
copyfile(
source_path,
self.outdir / '_static' / source_path.name,
force=True,
)
def copy_static_files(self) -> None:
try:
with progress_message(__('copying static files')):
ensuredir(path.join(self.outdir, '_static'))
ensuredir(self.outdir / '_static')
# prepare context for templates
context = self.globalcontext.copy()
@ -873,8 +904,12 @@ class StandaloneHTMLBuilder(Builder):
with progress_message(__('copying extra files')):
excluded = Matcher(self.config.exclude_patterns)
for extra_path in self.config.html_extra_path:
entry = path.join(self.confdir, extra_path)
copy_asset(entry, self.outdir, excluded)
copy_asset(
self.confdir / extra_path,
self.outdir,
excluded=excluded,
force=True,
)
except OSError as err:
logger.warning(__('cannot copy extra file %r'), err)

View File

@ -96,6 +96,8 @@ def copy_asset(source: str | os.PathLike[str], destination: str | os.PathLike[st
On copying, it expands the template variables if context argument is given and
the asset is a template file.
Use ``copy_asset_file`` instead to copy a single file.
:param source: The path to source file or directory
:param destination: The path to destination directory
:param excluded: The matcher to determine the given path should be copied or not