228 lines
8.1 KiB
Python
228 lines
8.1 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
sphinx.builders.websupport
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Builder for the web support package.
|
|
|
|
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
|
|
:license: BSD, see LICENSE for details.
|
|
"""
|
|
|
|
from os import path
|
|
import posixpath
|
|
import shutil
|
|
|
|
from docutils.io import StringOutput
|
|
|
|
from sphinx import version_info as sphinx_version
|
|
from sphinx.jinja2glue import BuiltinTemplateLoader
|
|
from sphinx.util.osutil import os_path, relative_uri, ensuredir, copyfile
|
|
from sphinxcontrib.serializinghtml import PickleHTMLBuilder
|
|
|
|
from . import package_dir
|
|
from .writer import WebSupportTranslator
|
|
from .utils import is_commentable
|
|
|
|
|
|
if False:
|
|
# For type annotation
|
|
from typing import Any, Dict, Iterable, Tuple # NOQA
|
|
from docutils import nodes # NOQA
|
|
from sphinx.application import Sphinx # NOQA
|
|
|
|
RESOURCES = [
|
|
'ajax-loader.gif',
|
|
'comment-bright.png',
|
|
'comment-close.png',
|
|
'comment.png',
|
|
'down-pressed.png',
|
|
'down.png',
|
|
'up-pressed.png',
|
|
'up.png',
|
|
'websupport.js',
|
|
]
|
|
|
|
|
|
class WebSupportBuilder(PickleHTMLBuilder):
|
|
"""
|
|
Builds documents for the web support package.
|
|
"""
|
|
name = 'websupport'
|
|
default_translator_class = WebSupportTranslator
|
|
versioning_compare = True # for commentable node's uuid stability.
|
|
|
|
def init(self):
|
|
# type: () -> None
|
|
PickleHTMLBuilder.init(self)
|
|
# templates are needed for this builder, but the serializing
|
|
# builder does not initialize them
|
|
self.init_templates()
|
|
if not isinstance(self.templates, BuiltinTemplateLoader):
|
|
raise RuntimeError('websupport builder must be used with '
|
|
'the builtin templates')
|
|
# add our custom JS
|
|
self.script_files.append('_static/websupport.js')
|
|
|
|
@property
|
|
def versioning_method(self):
|
|
if sphinx_version < (2, 0):
|
|
return 'commentable'
|
|
else:
|
|
return is_commentable
|
|
|
|
def set_webinfo(self, staticdir, virtual_staticdir, search, storage):
|
|
# type: (str, str, Any, str) -> None
|
|
self.staticdir = staticdir
|
|
self.virtual_staticdir = virtual_staticdir
|
|
self.search = search
|
|
self.storage = storage
|
|
|
|
def prepare_writing(self, docnames):
|
|
# type: (Iterable[str]) -> None
|
|
PickleHTMLBuilder.prepare_writing(self, docnames)
|
|
self.globalcontext['no_search_suffix'] = True
|
|
|
|
def write_doc(self, docname, doctree):
|
|
# type: (str, nodes.Node) -> None
|
|
destination = StringOutput(encoding='utf-8')
|
|
doctree.settings = self.docsettings
|
|
|
|
self.secnumbers = self.env.toc_secnumbers.get(docname, {})
|
|
self.fignumbers = self.env.toc_fignumbers.get(docname, {})
|
|
self.imgpath = '/' + posixpath.join(self.virtual_staticdir, self.imagedir)
|
|
self.dlpath = '/' + posixpath.join(self.virtual_staticdir, '_downloads')
|
|
self.current_docname = docname
|
|
self.docwriter.write(doctree, destination)
|
|
self.docwriter.assemble_parts()
|
|
body = self.docwriter.parts['fragment']
|
|
metatags = self.docwriter.clean_meta
|
|
|
|
ctx = self.get_doc_context(docname, body, metatags)
|
|
self.handle_page(docname, ctx, event_arg=doctree)
|
|
|
|
def write_doc_serialized(self, docname, doctree):
|
|
# type: (str, nodes.Node) -> None
|
|
self.imgpath = '/' + posixpath.join(self.virtual_staticdir, self.imagedir)
|
|
self.post_process_images(doctree)
|
|
title = self.env.longtitles.get(docname)
|
|
title = title and self.render_partial(title)['title'] or ''
|
|
self.index_page(docname, doctree, title)
|
|
|
|
def load_indexer(self, docnames):
|
|
# type: (Iterable[str]) -> None
|
|
self.indexer = self.search # type: ignore
|
|
self.indexer.init_indexing(changed=docnames) # type: ignore
|
|
|
|
def _render_page(self, pagename, addctx, templatename, event_arg=None):
|
|
# type: (str, Dict, str, str) -> Tuple[Dict, Dict]
|
|
# This is mostly copied from StandaloneHTMLBuilder. However, instead
|
|
# of rendering the template and saving the html, create a context
|
|
# dict and pickle it.
|
|
ctx = self.globalcontext.copy()
|
|
ctx['pagename'] = pagename
|
|
|
|
def pathto(otheruri, resource=False,
|
|
baseuri=self.get_target_uri(pagename)):
|
|
# type: (str, bool, str) -> str
|
|
if resource and '://' in otheruri:
|
|
return otheruri
|
|
elif not resource:
|
|
otheruri = self.get_target_uri(otheruri)
|
|
return relative_uri(baseuri, otheruri) or '#'
|
|
else:
|
|
return '/' + posixpath.join(self.virtual_staticdir, otheruri)
|
|
ctx['pathto'] = pathto
|
|
ctx['hasdoc'] = lambda name: name in self.env.all_docs
|
|
ctx['encoding'] = self.config.html_output_encoding
|
|
ctx['toctree'] = lambda **kw: self._get_local_toctree(pagename, **kw)
|
|
self.add_sidebars(pagename, ctx)
|
|
ctx.update(addctx)
|
|
|
|
newtmpl = self.app.emit_firstresult('html-page-context', pagename,
|
|
templatename, ctx, event_arg)
|
|
if newtmpl:
|
|
templatename = newtmpl
|
|
|
|
# create a dict that will be pickled and used by webapps
|
|
doc_ctx = {
|
|
'body': ctx.get('body', ''),
|
|
'title': ctx.get('title', ''),
|
|
'css': ctx.get('css', ''),
|
|
'script': ctx.get('script', ''),
|
|
}
|
|
# partially render the html template to get at interesting macros
|
|
template = self.templates.environment.get_template(templatename)
|
|
template_module = template.make_module(ctx)
|
|
for item in ['sidebar', 'relbar', 'script', 'css']:
|
|
if hasattr(template_module, item):
|
|
doc_ctx[item] = getattr(template_module, item)()
|
|
|
|
return ctx, doc_ctx
|
|
|
|
def handle_page(self, pagename, addctx, templatename='page.html',
|
|
outfilename=None, event_arg=None):
|
|
# type: (str, Dict, str, str, str) -> None
|
|
ctx, doc_ctx = self._render_page(pagename, addctx,
|
|
templatename, event_arg)
|
|
|
|
if not outfilename:
|
|
outfilename = path.join(self.outdir, 'pickles',
|
|
os_path(pagename) + self.out_suffix)
|
|
ensuredir(path.dirname(outfilename))
|
|
self.dump_context(doc_ctx, outfilename)
|
|
|
|
# if there is a source file, copy the source file for the
|
|
# "show source" link
|
|
if ctx.get('sourcename'):
|
|
source_name = path.join(self.staticdir,
|
|
'_sources', os_path(ctx['sourcename']))
|
|
ensuredir(path.dirname(source_name))
|
|
copyfile(self.env.doc2path(pagename), source_name)
|
|
|
|
def handle_finish(self):
|
|
# type: () -> None
|
|
# get global values for css and script files
|
|
_, doc_ctx = self._render_page('tmp', {}, 'page.html')
|
|
self.globalcontext['css'] = doc_ctx['css']
|
|
self.globalcontext['script'] = doc_ctx['script']
|
|
|
|
PickleHTMLBuilder.handle_finish(self)
|
|
|
|
# move static stuff over to separate directory
|
|
directories = [self.imagedir, '_static']
|
|
for directory in directories:
|
|
src = path.join(self.outdir, directory)
|
|
dst = path.join(self.staticdir, directory)
|
|
if path.isdir(src):
|
|
if path.isdir(dst):
|
|
shutil.rmtree(dst)
|
|
shutil.move(src, dst)
|
|
self.copy_resources()
|
|
|
|
def copy_resources(self):
|
|
# type: () -> None
|
|
# copy resource files to static dir
|
|
dst = path.join(self.staticdir, '_static')
|
|
|
|
if path.isdir(dst):
|
|
for resource in RESOURCES:
|
|
src = path.join(package_dir, 'files', resource)
|
|
shutil.copy(src, dst)
|
|
|
|
def dump_search_index(self):
|
|
# type: () -> None
|
|
self.indexer.finish_indexing() # type: ignore
|
|
|
|
|
|
def setup(app):
|
|
# type: (Sphinx) -> Dict[str, Any]
|
|
if sphinx_version >= (2, 0):
|
|
app.add_builder(WebSupportBuilder)
|
|
|
|
return {
|
|
'version': 'builtin',
|
|
'parallel_read_safe': True,
|
|
'parallel_write_safe': True,
|
|
}
|