init
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
"""Viewer directive module."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst import Directive
|
||||
from docutils.utils import relative_path # pragma: no cover
|
||||
from sphinx.util import logging
|
||||
from trame_vtk.tools.vtksz2html import HTML_VIEWER_PATH
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def is_path_relative_to(path, other):
|
||||
"""Path.is_relative_to was introduced in Python 3.9 [1].
|
||||
|
||||
Provide a replacement that works for all supported versions
|
||||
|
||||
[1] https://docs.python.org/3/library/pathlib.html#pathlib.PurePath.is_relative_to.
|
||||
"""
|
||||
return path.is_relative_to(other)
|
||||
|
||||
|
||||
class OfflineViewerDirective(Directive):
|
||||
required_arguments = 1
|
||||
optional_arguments = 0
|
||||
final_argument_whitespace = True
|
||||
has_content = True
|
||||
|
||||
def run(self): # pragma: no cover
|
||||
source_dir = Path(self.state.document.settings.env.app.srcdir)
|
||||
output_dir = Path(self.state.document.settings.env.app.outdir)
|
||||
# _build directory
|
||||
build_dir = Path(self.state.document.settings.env.app.outdir).parent
|
||||
|
||||
# this is the path passed to 'offlineviewer:: <path>` directive
|
||||
source_file = str(Path(self.state.document.current_source).parent / self.arguments[0])
|
||||
source_file = Path(source_file).absolute().resolve()
|
||||
if not Path(source_file).is_file():
|
||||
logger.warning(f'Source file {source_file} does not exist.')
|
||||
return []
|
||||
|
||||
# copy viewer HTML to _static
|
||||
static_path = Path(output_dir) / '_static'
|
||||
static_path.mkdir(exist_ok=True)
|
||||
if not Path(static_path, Path(HTML_VIEWER_PATH).name).exists():
|
||||
shutil.copy(HTML_VIEWER_PATH, static_path)
|
||||
|
||||
# calculate the scene asset path relative to the build directory and
|
||||
# recreate the directory structure under output_dir/_images. This
|
||||
# avoids overriding files with the same name e.g. index-x_yy_zz.vtksz will
|
||||
# be generated by any index.rst file and we have a number of them.
|
||||
# Example:
|
||||
# source_file ${HOME}/pyvista/pyvista/doc/_build/plot_directive/getting-started/index-2_00_00.vtksz # noqa:E501
|
||||
# dest_partial_path: plot_directive/getting-started
|
||||
# dest_path: ${HOME}/pyvista/pyvista/doc/_build/html/_images/plot_directive/getting-started/index-2_00_00.vtksz # noqa: E501
|
||||
|
||||
if is_path_relative_to(source_file, build_dir):
|
||||
dest_partial_path = Path(source_file.parent).relative_to(build_dir)
|
||||
elif is_path_relative_to(source_file, source_dir):
|
||||
dest_partial_path = Path(source_file.parent).relative_to(source_dir)
|
||||
else:
|
||||
logger.warning(
|
||||
f'Source file {source_file} is not a subpath of either the build directory of the '
|
||||
f'source directory. Cannot extract base path',
|
||||
)
|
||||
return []
|
||||
|
||||
dest_path = Path(output_dir).joinpath('_images').joinpath(dest_partial_path)
|
||||
dest_path.mkdir(parents=True, exist_ok=True)
|
||||
dest_file = dest_path.joinpath(source_file.name).resolve()
|
||||
if source_file != dest_file:
|
||||
try:
|
||||
shutil.copy(source_file, dest_file)
|
||||
except Exception as e: # noqa: BLE001
|
||||
logger.warning(f'Failed to copy file from {source_file} to {dest_file}: {e}')
|
||||
|
||||
# Compute the relative path of the current source to the source directory,
|
||||
# which is the same as the relative path of the '_static' directory to the
|
||||
# generated HTML file.
|
||||
relpath_to_source_root = relative_path(self.state.document.current_source, source_dir)
|
||||
rel_viewer_path = (
|
||||
Path() / relpath_to_source_root / '_static' / Path(HTML_VIEWER_PATH).name
|
||||
).as_posix()
|
||||
rel_asset_path = Path(os.path.relpath(dest_file, static_path)).as_posix()
|
||||
html = (
|
||||
f"<iframe src='{rel_viewer_path}?fileURL={rel_asset_path}' "
|
||||
"width='100%%' height='400px' frameborder='0'></iframe>"
|
||||
)
|
||||
|
||||
raw_node = nodes.raw('', html, format='html')
|
||||
|
||||
return [raw_node]
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_directive('offlineviewer', OfflineViewerDirective)
|
||||
|
||||
return {
|
||||
'version': '0.1',
|
||||
'parallel_read_safe': True,
|
||||
'parallel_write_safe': True,
|
||||
}
|
||||
Reference in New Issue
Block a user