Source code for sphinx_runpython.process_rst

from contextlib import redirect_stderr
import io
import os
import sys
import tempfile
from typing import List, Optional, Tuple
from docutils.core import publish_parts
from .runpython import run_cmd

_dummy_extensions = [
    "sphinx.ext.autodoc",
    "sphinx.ext.coverage",
    "sphinx.ext.githubpages",
    "sphinx.ext.graphviz",
    "sphinx.ext.ifconfig",
    "sphinx.ext.mathjax",
    "sphinx.ext.todo",
    "sphinx.ext.viewcode",
    "matplotlib.sphinxext.plot_directive",
    "sphinx_issues",
    "sphinx_runpython.blocdefs.sphinx_blocref_extension",
    "sphinx_runpython.blocdefs.sphinx_exref_extension",
    "sphinx_runpython.blocdefs.sphinx_faqref_extension",
    "sphinx_runpython.blocdefs.sphinx_mathdef_extension",
    "sphinx_runpython.collapse",
    "sphinx_runpython.docassert",
    "sphinx_runpython.epkg",
    "sphinx_runpython.runpython",
    "sphinx_runpython.quote",
    "sphinx_runpython.sphinx_rst_builder",
]

_dummy_epkg_dictionary = {
    "dot": "https://en.wikipedia.org/wiki/DOT_(graph_description_language)",
    "DOT": "https://en.wikipedia.org/wiki/DOT_(graph_description_language)",
    "JIT": "https://en.wikipedia.org/wiki/Just-in-time_compilation",
    "git": "https://git-scm.com/",
    "Graphviz": "https://graphviz.org/",
    "HTML": "https://simple.wikipedia.org/wiki/HTML",
    "nested_parse_with_titles": "http://sphinx-doc.org/extdev/markupapi.html?highlight=nested_parse_with_titles",
    "numpy": (
        "https://www.numpy.org/",
        ("https://docs.scipy.org/doc/numpy/reference/generated/numpy.{0}.html", 1),
        ("https://docs.scipy.org/doc/numpy/reference/generated/numpy.{0}.{1}.html", 2),
    ),
    "pandas": (
        "https://pandas.pydata.org/pandas-docs/stable/",
        ("https://pandas.pydata.org/pandas-docs/stable/generated/pandas.{0}.html", 1),
        (
            "https://pandas.pydata.org/pandas-docs/stable/generated/pandas.{0}.{1}.html",
            2,
        ),
    ),
    "pandoc": "https://johnmacfarlane.net/pandoc/",
    "Pandoc": "https://johnmacfarlane.net/pandoc/",
    "PNG": "https://en.wikipedia.org/wiki/PNG",
    "pypandoc": "https://github.com/JessicaTegner/pypandoc",
    "python": "https://www.python.org/",
    "RST": "https://fr.wikipedia.org/wiki/ReStructuredText",
    "sphinx": "https://www.sphinx-doc.org/en/master/",
    "sphinx-gallery": "https://github.com/sphinx-gallery/sphinx-gallery",
    "SVG": "https://en.wikipedia.org/wiki/SVG",
    "viz.js": "https://visjs.org/",
    "*py": (
        "https://docs.python.org/3/",
        ("https://docs.python.org/3/library/{0}.html", 1),
        ("https://docs.python.org/3/library/{0}.html#{0}.{1}", 2),
        ("https://docs.python.org/3/library/{0}.html#{0}.{1}.{2}", 3),
    ),
    "*pyf": (("https://docs.python.org/3/library/functions.html#{0}", 1),),
}


_dummy_conf = """
import os
import sys
from sphinx_runpython import __version__

source_suffix = ".rst"
master_doc = "index"
project = "rst2html"
pygments_style = "sphinx"
todo_include_todos = True

html_theme = "alabaster"
html_theme_options = {}
"""


def _rst2html_sphinx(
    rst: str,
    writer_name: str = "html",
    report_level: int = 0,
    new_extensions: Optional[List[str]] = None,
    **kwargs,
) -> Tuple[str, str]:
    if "writer" in kwargs:
        raise ValueError("'writer' is not a valid argument, please use 'writer_name'.")
    with tempfile.TemporaryDirectory() as folder:
        index = os.path.join(folder, "index.rst")
        with open(index, "w", encoding="utf-8") as f:
            f.write(rst)
        conf = os.path.join(folder, "conf.py")
        with open(conf, "w", encoding="utf-8") as f:
            f.write(_dummy_conf)
            if new_extensions is None:
                f.write(f"\nextensions = {_dummy_extensions}\n")
            else:
                f.write(f"\nextensions = {_dummy_extensions + new_extensions}\n")
            f.write(f"\nepkg_dictionary = {_dummy_epkg_dictionary}\n")
        fout = os.path.join(folder, "output")

        rep = " -v" * report_level
        cmd = f"{sys.executable} -m sphinx -b {writer_name} {folder} {fout}{rep}"
        out, err = run_cmd(cmd, wait=True)

        html = os.path.join(fout, f"index.{writer_name}")
        if not os.path.exists(html):
            raise RuntimeError(
                f"Unable to find output {html!r}\n--STDOUT--\n{out}\n--STDERR--\n{err}"
            )
        with open(html, "r", encoding="utf-8") as f:
            content = f.read()
        return content, err


[docs]def rst2html( rst: str, writer_name: str = "html", report_level: int = 0, return_warnings: bool = False, use_sphinx: bool = True, new_extensions: Optional[List[str]] = None, **kwargs, ) -> Tuple[str, str]: """ Converts a RST string into HTML or RST. :param rst: RST string :param report_level: filter output, 0 means everything :param writer_name: writer name :param kwargs: additional values to add to the configuration :param return_warnings: return the warnings as well :param use_sphinx: run sphinx from the command line and returns the results, for that configuration :param new_extensions: new extensions to add :return: output and warnings """ if use_sphinx: content, err = _rst2html_sphinx( rst, writer_name=writer_name, report_level=report_level, new_extensions=new_extensions, **kwargs, ) if return_warnings: return content, err return content docutils_kwargs = { "writer_name": writer_name, "settings_overrides": { "_disable_config": True, "report_level": report_level, }, } target = io.StringIO() with redirect_stderr(target): parts = publish_parts(rst, **docutils_kwargs) html = parts["html_body"] warning = target.getvalue().strip() if "System Message: ERROR" in html: raise RuntimeError( f"rst2html failed, warnings:\n{warning}\n-----\nOUTPUT\n{html}" ) if return_warnings: return html, warning return html