Source code for


    Single HTML builders.

    :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
    :license: BSD, see LICENSE for details.

from os import path

from docutils import nodes

from import StandaloneHTMLBuilder
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
from sphinx.environment.adapters.toctree import TocTree
from sphinx.locale import __
from sphinx.util import logging
from sphinx.util import progress_message
from sphinx.util.console import darkgreen  # type: ignore
from sphinx.util.nodes import inline_all_toctrees

if False:
    # For type annotation
    from typing import Any, Dict, List, Tuple, Union  # NOQA
    from sphinx.application import Sphinx  # NOQA

logger = logging.getLogger(__name__)

[docs]class SingleFileHTMLBuilder(StandaloneHTMLBuilder): """ A StandaloneHTMLBuilder subclass that puts the whole document tree on one HTML page. """ name = 'singlehtml' epilog = __('The HTML page is in %(outdir)s.') copysource = False def get_outdated_docs(self): # type: ignore # type: () -> Union[str, List[str]] return 'all documents' def get_target_uri(self, docname, typ=None): # type: (str, str) -> str if docname in self.env.all_docs: # all references are on the same page... return self.config.master_doc + self.out_suffix + \ '#document-' + docname else: # chances are this is a html_additional_page return docname + self.out_suffix def get_relative_uri(self, from_, to, typ=None): # type: (str, str, str) -> str # ignore source return self.get_target_uri(to, typ) def fix_refuris(self, tree): # type: (nodes.Node) -> None # fix refuris with double anchor fname = self.config.master_doc + self.out_suffix for refnode in tree.traverse(nodes.reference): if 'refuri' not in refnode: continue refuri = refnode['refuri'] hashindex = refuri.find('#') if hashindex < 0: continue hashindex = refuri.find('#', hashindex + 1) if hashindex >= 0: refnode['refuri'] = fname + refuri[hashindex:] def _get_local_toctree(self, docname, collapse=True, **kwds): # type: (str, bool, Any) -> str if 'includehidden' not in kwds: kwds['includehidden'] = False toctree = TocTree(self.env).get_toctree_for(docname, self, collapse, **kwds) if toctree is not None: self.fix_refuris(toctree) return self.render_partial(toctree)['fragment'] def assemble_doctree(self): # type: () -> nodes.document master = self.config.master_doc tree = self.env.get_doctree(master) tree = inline_all_toctrees(self, set(), master, tree, darkgreen, [master]) tree['docname'] = master self.env.resolve_references(tree, master, self) self.fix_refuris(tree) return tree def assemble_toc_secnumbers(self): # type: () -> Dict[str, Dict[str, Tuple[int, ...]]] # Assemble toc_secnumbers to resolve section numbers on SingleHTML. # Merge all secnumbers to single secnumber. # # Note: current Sphinx has refid confliction in singlehtml mode. # To avoid the problem, it replaces key of secnumbers to # tuple of docname and refid. # # There are related codes in inline_all_toctres() and # HTMLTranslter#add_secnumber(). new_secnumbers = {} # type: Dict[str, Tuple[int, ...]] for docname, secnums in self.env.toc_secnumbers.items(): for id, secnum in secnums.items(): alias = "%s/%s" % (docname, id) new_secnumbers[alias] = secnum return {self.config.master_doc: new_secnumbers} def assemble_toc_fignumbers(self): # type: () -> Dict[str, Dict[str, Dict[str, Tuple[int, ...]]]] # Assemble toc_fignumbers to resolve figure numbers on SingleHTML. # Merge all fignumbers to single fignumber. # # Note: current Sphinx has refid confliction in singlehtml mode. # To avoid the problem, it replaces key of secnumbers to # tuple of docname and refid. # # There are related codes in inline_all_toctres() and # HTMLTranslter#add_fignumber(). new_fignumbers = {} # type: Dict[str, Dict[str, Tuple[int, ...]]] # {'foo': {'figure': {'id2': (2,), 'id1': (1,)}}, 'bar': {'figure': {'id1': (3,)}}} for docname, fignumlist in self.env.toc_fignumbers.items(): for figtype, fignums in fignumlist.items(): alias = "%s/%s" % (docname, figtype) new_fignumbers.setdefault(alias, {}) for id, fignum in fignums.items(): new_fignumbers[alias][id] = fignum return {self.config.master_doc: new_fignumbers} def get_doc_context(self, docname, body, metatags): # type: (str, str, str) -> Dict # no relation links... toctree = TocTree(self.env).get_toctree_for(self.config.master_doc, self, False) # if there is no toctree, toc is None if toctree: self.fix_refuris(toctree) toc = self.render_partial(toctree)['fragment'] display_toc = True else: toc = '' display_toc = False return { 'parents': [], 'prev': None, 'next': None, 'docstitle': None, 'title': self.config.html_title, 'meta': None, 'body': body, 'metatags': metatags, 'rellinks': [], 'sourcename': '', 'toc': toc, 'display_toc': display_toc, } def write(self, *ignored): # type: (Any) -> None docnames = self.env.all_docs with progress_message(__('preparing documents')): self.prepare_writing(docnames) # type: ignore with progress_message(__('assembling single document')): doctree = self.assemble_doctree() self.env.toc_secnumbers = self.assemble_toc_secnumbers() self.env.toc_fignumbers = self.assemble_toc_fignumbers() with progress_message(__('writing')): self.write_doc_serialized(self.config.master_doc, doctree) self.write_doc(self.config.master_doc, doctree) def finish(self): # type: () -> None self.write_additional_files() self.copy_image_files() self.copy_download_files() self.copy_static_files() self.copy_extra_files() self.write_buildinfo() self.dump_inventory() @progress_message(__('writing additional files')) def write_additional_files(self): # type: () -> None # no indices or search pages are supported # additional pages from for pagename, template in self.config.html_additional_pages.items():' ' + pagename, nonl=True) self.handle_page(pagename, {}, template) if self.config.html_use_opensearch:' opensearch', nonl=True) fn = path.join(self.outdir, '_static', 'opensearch.xml') self.handle_page('opensearch', {}, 'opensearch.xml', outfilename=fn)
# for compatibility deprecated_alias('', { 'SingleFileHTMLBuilder': SingleFileHTMLBuilder, }, RemovedInSphinx40Warning) def setup(app): # type: (Sphinx) -> Dict[str, Any] app.setup_extension('') app.add_builder(SingleFileHTMLBuilder) app.add_config_value('singlehtml_sidebars', lambda self: self.html_sidebars, 'html') return { 'version': 'builtin', 'parallel_read_safe': True, 'parallel_write_safe': True, }