diff options
Diffstat (limited to 'sphinx/builders/html.py')
-rw-r--r-- | sphinx/builders/html.py | 154 |
1 files changed, 90 insertions, 64 deletions
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 9c039e3a..21539c2a 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -14,14 +14,11 @@ import sys import zlib import codecs import posixpath -import cPickle as pickle from os import path -try: - from hashlib import md5 -except ImportError: - # 2.4 compatibility - from md5 import md5 +from hashlib import md5 +from six import iteritems, itervalues, text_type, string_types +from six.moves import cPickle as pickle from docutils import nodes from docutils.io import DocTreeInput, StringOutput from docutils.core import Publisher @@ -32,11 +29,9 @@ from docutils.readers.doctree import Reader as DoctreeReader from sphinx import package_dir, __version__ from sphinx.util import jsonimpl, copy_static_entry from sphinx.util.osutil import SEP, os_path, relative_uri, ensuredir, \ - movefile, ustrftime, copyfile + movefile, ustrftime, copyfile from sphinx.util.nodes import inline_all_toctrees from sphinx.util.matching import patmatch, compile_matchers -from sphinx.util.pycompat import any, b -from sphinx.errors import SphinxError from sphinx.locale import _ from sphinx.search import js_index from sphinx.theming import Theme @@ -45,7 +40,7 @@ from sphinx.application import ENV_PICKLE_FILENAME from sphinx.highlighting import PygmentsBridge from sphinx.util.console import bold, darkgreen, brown from sphinx.writers.html import HTMLWriter, HTMLTranslator, \ - SmartyPantsHTMLTranslator + SmartyPantsHTMLTranslator #: the filename for the inventory of objects INVENTORY_FILENAME = 'objects.inv' @@ -63,7 +58,7 @@ def get_stable_hash(obj): return get_stable_hash(list(obj.items())) elif isinstance(obj, (list, tuple)): obj = sorted(get_stable_hash(o) for o in obj) - return md5(unicode(obj).encode('utf8')).hexdigest() + return md5(text_type(obj).encode('utf8')).hexdigest() class StandaloneHTMLBuilder(Builder): @@ -100,6 +95,8 @@ class StandaloneHTMLBuilder(Builder): # a hash of all config values that, if changed, cause a full rebuild self.config_hash = '' self.tags_hash = '' + # basename of images directory + self.imagedir = '_images' # section numbers for headings in the currently visited document self.secnumbers = {} # currently written docname @@ -157,7 +154,9 @@ class StandaloneHTMLBuilder(Builder): self.config.trim_doctest_flags) def init_translator_class(self): - if self.config.html_translator_class: + if self.translator_class is not None: + pass + elif self.config.html_translator_class: self.translator_class = self.app.import_object( self.config.html_translator_class, 'html_translator_class setting') @@ -168,7 +167,7 @@ class StandaloneHTMLBuilder(Builder): def get_outdated_docs(self): cfgdict = dict((name, self.config[name]) - for (name, desc) in self.config.values.iteritems() + for (name, desc) in iteritems(self.config.values) if desc[1] == 'html') self.config_hash = get_stable_hash(cfgdict) self.tags_hash = get_stable_hash(sorted(self.tags)) @@ -225,7 +224,7 @@ class StandaloneHTMLBuilder(Builder): """Utility: Render a lone doctree node.""" if node is None: return {'fragment': ''} - doc = new_document(b('<partial node>')) + doc = new_document(b'<partial node>') doc.append(node) if self._publisher is None: @@ -269,7 +268,7 @@ class StandaloneHTMLBuilder(Builder): # html_domain_indices can be False/True or a list of index names indices_config = self.config.html_domain_indices if indices_config: - for domain in self.env.domains.itervalues(): + for domain in itervalues(self.env.domains): for indexcls in domain.indices: indexname = '%s-%s' % (domain.name, indexcls.name) if isinstance(indices_config, list): @@ -300,7 +299,7 @@ class StandaloneHTMLBuilder(Builder): if favicon and os.path.splitext(favicon)[1] != '.ico': self.warn('html_favicon is not an .ico file') - if not isinstance(self.config.html_use_opensearch, basestring): + if not isinstance(self.config.html_use_opensearch, string_types): self.warn('html_use_opensearch config value must now be a string') self.relations = self.env.collect_relations() @@ -350,7 +349,7 @@ class StandaloneHTMLBuilder(Builder): if self.theme: self.globalcontext.update( ('theme_' + key, val) for (key, val) in - self.theme.get_options(self.theme_options).iteritems()) + iteritems(self.theme.get_options(self.theme_options))) self.globalcontext.update(self.config.html_context) def get_doc_context(self, docname, body, metatags): @@ -427,6 +426,7 @@ class StandaloneHTMLBuilder(Builder): doctree.settings = self.docsettings self.secnumbers = self.env.toc_secnumbers.get(docname, {}) + self.fignumbers = self.env.toc_fignumbers.get(docname, {}) self.imgpath = relative_uri(self.get_target_uri(docname), '_images') self.dlpath = relative_uri(self.get_target_uri(docname), '_downloads') self.current_docname = docname @@ -439,19 +439,26 @@ class StandaloneHTMLBuilder(Builder): self.handle_page(docname, ctx, event_arg=doctree) def write_doc_serialized(self, docname, doctree): - self.imgpath = relative_uri(self.get_target_uri(docname), '_images') + self.imgpath = relative_uri(self.get_target_uri(docname), 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 finish(self): - self.info(bold('writing additional files...'), nonl=1) + self.finish_tasks.add_task(self.gen_indices) + self.finish_tasks.add_task(self.gen_additional_pages) + self.finish_tasks.add_task(self.copy_image_files) + self.finish_tasks.add_task(self.copy_download_files) + self.finish_tasks.add_task(self.copy_static_files) + self.finish_tasks.add_task(self.copy_extra_files) + self.finish_tasks.add_task(self.write_buildinfo) - # pages from extensions - for pagelist in self.app.emit('html-collect-pages'): - for pagename, context, template in pagelist: - self.handle_page(pagename, context, template) + # dump the search index + self.handle_finish() + + def gen_indices(self): + self.info(bold('generating indices...'), nonl=1) # the global general index if self.get_builder_config('use_index', 'html'): @@ -460,16 +467,27 @@ class StandaloneHTMLBuilder(Builder): # the global domain-specific indices self.write_domain_indices() - # the search page - if self.name != 'htmlhelp': - self.info(' search', nonl=1) - self.handle_page('search', {}, 'search.html') + self.info() + + def gen_additional_pages(self): + # pages from extensions + for pagelist in self.app.emit('html-collect-pages'): + for pagename, context, template in pagelist: + self.handle_page(pagename, context, template) + + self.info(bold('writing additional pages...'), nonl=1) # additional pages from conf.py for pagename, template in self.config.html_additional_pages.items(): self.info(' '+pagename, nonl=1) self.handle_page(pagename, {}, template) + # the search page + if self.name != 'htmlhelp': + self.info(' search', nonl=1) + self.handle_page('search', {}, 'search.html') + + # the opensearch xml file if self.config.html_use_opensearch and self.name != 'htmlhelp': self.info(' opensearch', nonl=1) fn = path.join(self.outdir, '_static', 'opensearch.xml') @@ -477,15 +495,6 @@ class StandaloneHTMLBuilder(Builder): self.info() - self.copy_image_files() - self.copy_download_files() - self.copy_static_files() - self.copy_extra_files() - self.write_buildinfo() - - # dump the search index - self.handle_finish() - def write_genindex(self): # the total count of lines for each index letter, used to distribute # the entries into two columns @@ -528,14 +537,14 @@ class StandaloneHTMLBuilder(Builder): def copy_image_files(self): # copy image files if self.images: - ensuredir(path.join(self.outdir, '_images')) - for src in self.status_iterator(self.images, 'copying images... ', - brown, len(self.images)): + ensuredir(path.join(self.outdir, self.imagedir)) + for src in self.app.status_iterator(self.images, 'copying images... ', + brown, len(self.images)): dest = self.images[src] try: copyfile(path.join(self.srcdir, src), - path.join(self.outdir, '_images', dest)) - except Exception, err: + path.join(self.outdir, self.imagedir, dest)) + except Exception as err: self.warn('cannot copy image file %r: %s' % (path.join(self.srcdir, src), err)) @@ -543,14 +552,14 @@ class StandaloneHTMLBuilder(Builder): # copy downloadable files if self.env.dlfiles: ensuredir(path.join(self.outdir, '_downloads')) - for src in self.status_iterator(self.env.dlfiles, - 'copying downloadable files... ', - brown, len(self.env.dlfiles)): + for src in self.app.status_iterator(self.env.dlfiles, + 'copying downloadable files... ', + brown, len(self.env.dlfiles)): dest = self.env.dlfiles[src][1] try: copyfile(path.join(self.srcdir, src), path.join(self.outdir, '_downloads', dest)) - except Exception, err: + except Exception as err: self.warn('cannot copy downloadable file %r: %s' % (path.join(self.srcdir, src), err)) @@ -583,10 +592,7 @@ class StandaloneHTMLBuilder(Builder): # then, copy over all user-supplied static files staticentries = [path.join(self.confdir, spath) for spath in self.config.html_static_path] - matchers = compile_matchers( - self.config.exclude_patterns + - ['**/' + d for d in self.config.exclude_dirnames] - ) + matchers = compile_matchers(self.config.exclude_patterns) for entry in staticentries: if not path.exists(entry): self.warn('html_static_path entry %r does not exist' % entry) @@ -704,7 +710,7 @@ class StandaloneHTMLBuilder(Builder): sidebars = None matched = None customsidebar = None - for pattern, patsidebars in self.config.html_sidebars.iteritems(): + for pattern, patsidebars in iteritems(self.config.html_sidebars): if patmatch(pagename, pattern): if matched: if has_wildcard(pattern): @@ -721,7 +727,7 @@ class StandaloneHTMLBuilder(Builder): if sidebars is None: # keep defaults pass - elif isinstance(sidebars, basestring): + elif isinstance(sidebars, string_types): # 0.x compatible mode: insert custom sidebar before searchbox customsidebar = sidebars sidebars = None @@ -761,8 +767,10 @@ class StandaloneHTMLBuilder(Builder): self.add_sidebars(pagename, ctx) ctx.update(addctx) - self.app.emit('html-page-context', pagename, templatename, - ctx, event_arg) + newtmpl = self.app.emit_firstresult('html-page-context', pagename, + templatename, ctx, event_arg) + if newtmpl: + templatename = newtmpl try: output = self.templates.render(templatename, ctx) @@ -782,7 +790,7 @@ class StandaloneHTMLBuilder(Builder): f.write(output) finally: f.close() - except (IOError, OSError), err: + except (IOError, OSError) as err: self.warn("error writing file %s: %s" % (outfilename, err)) if self.copysource and ctx.get('sourcename'): # copy the source file for the "show source" link @@ -792,8 +800,8 @@ class StandaloneHTMLBuilder(Builder): copyfile(self.env.doc2path(pagename), source_name) def handle_finish(self): - self.dump_search_index() - self.dump_inventory() + self.finish_tasks.add_task(self.dump_search_index) + self.finish_tasks.add_task(self.dump_inventory) def dump_inventory(self): self.info(bold('dumping object inventory... '), nonl=True) @@ -806,7 +814,7 @@ class StandaloneHTMLBuilder(Builder): % (self.config.project, self.config.version) ).encode('utf-8')) compressor = zlib.compressobj(9) - for domainname, domain in self.env.domains.iteritems(): + for domainname, domain in iteritems(self.env.domains): for name, dispname, type, docname, anchor, prio in \ domain.get_objects(): if anchor.endswith(name): @@ -825,7 +833,9 @@ class StandaloneHTMLBuilder(Builder): self.info('done') def dump_search_index(self): - self.info(bold('dumping search index... '), nonl=True) + self.info( + bold('dumping search index in %s ... ' % self.indexer.label()), + nonl=True) self.indexer.prune(self.env.all_docs) searchindexfn = path.join(self.outdir, self.searchindex_filename) # first write to a temporary file, so that if dumping fails, @@ -919,6 +929,23 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder): self.fix_refuris(tree) return tree + def assemble_toc_secnumbers(self): + # 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 = {} + for docname, secnums in iteritems(self.env.toc_secnumbers): + for id, secnum in iteritems(secnums): + new_secnumbers[(docname, id)] = secnum + + return {self.config.master_doc: new_secnumbers} + def get_doc_context(self, docname, body, metatags): # no relation links... toc = self.env.get_toctree_for(self.config.master_doc, self, False) @@ -954,6 +981,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder): self.info(bold('assembling single document... '), nonl=True) doctree = self.assemble_doctree() + self.env.toc_secnumbers = self.assemble_toc_secnumbers() self.info() self.info(bold('writing... '), nonl=True) self.write_doc_serialized(self.config.master_doc, doctree) @@ -1005,6 +1033,7 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder): def init(self): self.config_hash = '' self.tags_hash = '' + self.imagedir = '_images' self.theme = None # no theme necessary self.templates = None # no template bridge necessary self.init_translator_class() @@ -1036,8 +1065,9 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder): outfilename = path.join(self.outdir, os_path(pagename) + self.out_suffix) - self.app.emit('html-page-context', pagename, templatename, - ctx, event_arg) + # we're not taking the return value here, since no template is + # actually rendered + self.app.emit('html-page-context', pagename, templatename, ctx, event_arg) ensuredir(path.dirname(outfilename)) self.dump_context(ctx, outfilename) @@ -1100,8 +1130,4 @@ class JSONHTMLBuilder(SerializingHTMLBuilder): searchindex_filename = 'searchindex.json' def init(self): - if jsonimpl.json is None: - raise SphinxError( - 'The module simplejson (or json in Python >= 2.6) ' - 'is not available. The JSONHTMLBuilder builder will not work.') SerializingHTMLBuilder.init(self) |