Source code for ipypublish.postprocessors.sphinx

import webbrowser
import shutil
import os
from subprocess import Popen, PIPE, STDOUT
from distutils.spawn import find_executable

from six import u
from traitlets import TraitError, validate, Bool, Dict, Unicode

from ipypublish import __version__
from ipypublish.postprocessors.base import IPyPostProcessor
from ipypublish.sphinx.utils import import_sphinx
from ipypublish.sphinx.create_setup import make_conf, make_index


# NOTE Interesting note about adding a directive to actually run python code
# https://stackoverflow.com/questions/7250659/how-to-use-python-to-programmatically-generate-part-of-sphinx-documentation


[docs]class RunSphinx(IPyPostProcessor): """ run sphinx to create an html output """ @property def allowed_mimetypes(self): return ("text/restructuredtext",) @property def requires_path(self): return True @property def logger_name(self): return "run-sphinx" open_in_browser = Bool( True, help="launch a html page containing a pdf browser" ).tag(config=True) numbered = Bool( True, help="set :numbered: in toc, which numbers sections, etc" ).tag(config=True) show_prompts = Bool(True, help="whether to include cell prompts").tag(config=True) prompt_style = Unicode("[{count}]:", help="the style of cell prompts").tag( config=True ) @validate("prompt_style") def _valid_prompt_style(self, proposal): try: proposal.format(count=1) except TypeError: raise TraitError( "prompt_style should be formatable by " "`prompt_style.format(count=1)`" ) return proposal["value"] conf_kwargs = Dict( help=( "additional key-word arguments to be included in the conf.py " "as <key> = <val>" ) ).tag(config=True) override_defaults = Bool( True, help="if True, conf_kwargs override default values" ).tag(config=True) nitpick = Bool(False, help="nit-picky mode, warn about all missing references")
[docs] def run_postprocess(self, stream, mimetype, filepath, resources): # check sphinx is available and the correct version try: import_sphinx() except ImportError as err: self.handle_error(err.msg, ImportError) self.logger.info("Creating Sphinx files") titlepage = {} if "titlepage" in resources.get("ipub", {}): titlepage = resources["ipub"]["titlepage"] # includes ['author', 'email', 'supervisors', 'title', 'subtitle', # 'tagline', 'institution', 'logo'] # create a conf.py kwargs = {} if not self.conf_kwargs else self.conf_kwargs kwargs["ipysphinx_show_prompts"] = self.show_prompts kwargs["ipysphinx_input_prompt"] = self.prompt_style kwargs["ipysphinx_output_prompt"] = self.prompt_style if "author" in titlepage: kwargs["author"] = titlepage["author"] if "tagline" in titlepage: kwargs["description"] = titlepage["tagline"] if "email" in titlepage: kwargs["email"] = titlepage["email"] conf_str = make_conf(overwrite=self.override_defaults, **kwargs) conf_path = filepath.parent.joinpath("conf.py") with conf_path.open("w", encoding="utf8") as f: f.write(u(conf_str)) # create an index.rst toc_files = [filepath.name] toc_depth = 3 title = None prolog = [] epilog = [] if "author" in titlepage: prolog.append(".. sectionauthor:: {0}".format(titlepage["author"])) prolog.append("") if "title" in titlepage: title = titlepage["title"] if "tagline" in titlepage: prolog.append(titlepage["tagline"]) if "institution" in titlepage: for inst in titlepage["institution"]: epilog.append("| " + inst) epilog.append("") epilog.append("Created by IPyPublish (version {})".format(__version__)) toc = resources.get("ipub", {}).get("toc", {}) if hasattr(toc, "get") and "depth" in toc: toc_depth = toc["depth"] index_str = make_index( toc_files, toc_depth=toc_depth, header=title, toc_numbered=self.numbered, prolog="\n".join(prolog), epilog="\n".join(epilog), ) index_path = filepath.parent.joinpath("index.rst") with index_path.open("w", encoding="utf8") as f: f.write(u(index_str)) # clear any existing build build_dir = filepath.parent.joinpath("build/html") if build_dir.exists(): # >> rm -r build/html shutil.rmtree(str(build_dir)) build_dir.mkdir(parents=True) # run sphinx exec_path = find_executable("sphinx-build") args = [exec_path, "-b", "html"] if self.nitpick: args.append("-n") args.extend([str(filepath.parent.absolute()), str(build_dir.absolute())]) self.logger.info("running: " + " ".join(args)) # this way overrides the logging # sphinx_build = find_entry_point("sphinx-build", "console_scripts", # self.logger, "sphinx") def log_process_output(pipe): for line in iter(pipe.readline, b""): self.logger.info("{}".format(line.decode("utf-8").strip())) process = Popen(args, stdout=PIPE, stderr=STDOUT) with process.stdout: log_process_output(process.stdout) exitcode = process.wait() # 0 means success if exitcode: self.logger.warning("sphinx-build exited with code: {}".format(exitcode)) if self.open_in_browser and not exitcode: # get entry path entry_path = filepath.parent.joinpath("build/html") entry_path = entry_path.joinpath( os.path.splitext(filepath.name)[0] + ".html" ) if entry_path.exists(): # 2 opens the url in a new tab webbrowser.open(entry_path.as_uri(), new=2) else: self.handle_error("can't find {0} to open".format(entry_path), IOError) return stream, filepath, resources