#!/usr/bin/python3
import argparse
import html
import os
import pathlib
import re
import shutil
import subprocess
import sys
import tempfile
parser = argparse.ArgumentParser(prog="sphinx_to_html")
parser.add_argument("filename_rst", help="Input .rst file")
parser.add_argument("--user", help="Run sphinx as another user", type=str)
parser.add_argument("--user-work-dir", help="Do work in specified folder accessible by user", type=str)
args = parser.parse_args()
base_url = "https://projects.blender.org"
local_url = "http://localhost:3000"
placeholder_url = "https://placeholder.org"
# Gitea sets this environment variable with the URL prefix for the current file.
gitea_prefix = os.environ.get("GITEA_PREFIX_SRC", "")
if gitea_prefix.startswith(base_url):
gitea_prefix = gitea_prefix[len(base_url):]
if gitea_prefix.startswith(local_url):
gitea_prefix = gitea_prefix[len(local_url):]
if len(gitea_prefix):
org, repo, view, ref, branch = gitea_prefix.strip('/').split('/')[:5]
doc_url = f"{base_url}/{org}/{repo}/{view}/{ref}/{branch}"
image_url = f"{base_url}/{org}/{repo}/raw/{ref}/{branch}"
else:
doc_url = ""
image_url = ""
# Set up temporary directory with sphinx configuration.
with tempfile.TemporaryDirectory(dir=args.user_work_dir) as tmp_dir:
work_dir = pathlib.Path(tmp_dir) / "work"
script_dir = pathlib.Path(__file__).parent.resolve()
shutil.copytree(script_dir / "template", work_dir)
page_filepath = work_dir / "contents.rst"
shutil.copyfile(args.filename_rst, page_filepath)
page_contents = page_filepath.read_text()
# Turn links into external links since internal links are not found and stripped.
def doc_link(matchobj):
return f"`{matchobj.group(1)}<{doc_url}/{matchobj.group(2).strip('/')}.rst>`_"
def ref_link(matchobj):
return f"`{matchobj.group(1)} <{placeholder_url}>`_"
def term_link(matchobj):
return f"`{matchobj.group(1)} <{placeholder_url}>`_"
def figure_link(matchobj):
return f"figure:: {image_url}/{matchobj.group(1).strip('/')}"
def image_link(matchobj):
return f"image:: {image_url}/{matchobj.group(1).strip('/')}"
page_contents = re.sub(":doc:`(.*)<(.+)>`", doc_link, page_contents)
page_contents = re.sub(":ref:`(.+)<(.+)>`", ref_link, page_contents)
page_contents = re.sub(":ref:`(.+)`", ref_link, page_contents)
page_contents = re.sub(":term:`(.+)`", term_link, page_contents)
page_contents = re.sub("figure:: (.+)", figure_link, page_contents)
page_contents = re.sub("image:: (.+)", image_link, page_contents)
# Disable include directives and raw for security. They are already disabled
# by docutils.py, this is just to be extra careful.
def include_directive(matchobj):
return f"warning:: include directives disabled: {html.escape(matchobj.group(1))}"
def raw_directive(matchobj):
return f"warning:: raw disabled: {html.escape(matchobj.group(1))}"
page_contents = re.sub("literalinclude::.*", include_directive, page_contents)
page_contents = re.sub("include::(.*)", include_directive, page_contents)
page_contents = re.sub("raw::(.*)", raw_directive, page_contents)
page_filepath.write_text(page_contents)
# Debug processed RST
# print(html.escape(page_contents).replace('\n', '
\n'))
# sys.exit(0)
# Run sphinx-build.
out_dir = work_dir / "out"
out_filepath = out_dir / "contents.html"
sphinx_cmd = ["sphinx-build", "-b", "html", work_dir, out_dir]
if args.user:
result = subprocess.run(sphinx_cmd, capture_output=True, user=args.user)
else:
result = subprocess.run(sphinx_cmd, capture_output=True)
# Output errors.
error = result.stderr.decode("utf-8", "ignore").strip()
if len(error):
error = error.replace(str(page_filepath) + ":", "")
error = html.escape(error)
print("
{error}") print("
Note the preview is not accurate and warnings may not indicate real issues.
") # Output contents of body. if result.returncode == 0 and out_filepath.is_file(): contents = out_filepath.read_text() body = contents.split("")[1].split("")[0] body = body.replace(placeholder_url, "#") body = body.replace('href="http', 'target="_blank" href="http') print(body)