mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Add a possibility to later execute finishing-up tasks in parallel.
This commit is contained in:
@@ -248,6 +248,15 @@ class Sphinx(object):
|
|||||||
else:
|
else:
|
||||||
self.builder.compile_update_catalogs()
|
self.builder.compile_update_catalogs()
|
||||||
self.builder.build_update()
|
self.builder.build_update()
|
||||||
|
|
||||||
|
status = (self.statuscode == 0
|
||||||
|
and 'succeeded' or 'finished with problems')
|
||||||
|
if self._warncount:
|
||||||
|
self.info(bold('build %s, %s warning%s.' %
|
||||||
|
(status, self._warncount,
|
||||||
|
self._warncount != 1 and 's' or '')))
|
||||||
|
else:
|
||||||
|
self.info(bold('build %s.' % status))
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# delete the saved env to force a fresh build next time
|
# delete the saved env to force a fresh build next time
|
||||||
envfile = path.join(self.doctreedir, ENV_PICKLE_FILENAME)
|
envfile = path.join(self.doctreedir, ENV_PICKLE_FILENAME)
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ from docutils import nodes
|
|||||||
from sphinx.util import i18n, path_stabilize
|
from sphinx.util import i18n, path_stabilize
|
||||||
from sphinx.util.osutil import SEP, relative_uri, find_catalog
|
from sphinx.util.osutil import SEP, relative_uri, find_catalog
|
||||||
from sphinx.util.console import bold, darkgreen
|
from sphinx.util.console import bold, darkgreen
|
||||||
from sphinx.util.parallel import ParallelChunked, parallel_available
|
from sphinx.util.parallel import ParallelChunked, ParallelTasks, SerialTasks, \
|
||||||
|
parallel_available
|
||||||
|
|
||||||
# side effect: registers roles and directives
|
# side effect: registers roles and directives
|
||||||
from sphinx import roles
|
from sphinx import roles
|
||||||
@@ -70,6 +71,10 @@ class Builder(object):
|
|||||||
# images that need to be copied over (source -> dest)
|
# images that need to be copied over (source -> dest)
|
||||||
self.images = {}
|
self.images = {}
|
||||||
|
|
||||||
|
# these get set later
|
||||||
|
self.parallel_ok = False
|
||||||
|
self.finish_tasks = None
|
||||||
|
|
||||||
# load default translator class
|
# load default translator class
|
||||||
self.translator_class = app._translators.get(self.name)
|
self.translator_class = app._translators.get(self.name)
|
||||||
|
|
||||||
@@ -277,20 +282,33 @@ class Builder(object):
|
|||||||
if docnames and docnames != ['__all__']:
|
if docnames and docnames != ['__all__']:
|
||||||
docnames = set(docnames) & self.env.found_docs
|
docnames = set(docnames) & self.env.found_docs
|
||||||
|
|
||||||
# another indirection to support builders that don't build
|
# determine if we can write in parallel
|
||||||
# files individually
|
self.parallel_ok = False
|
||||||
|
if parallel_available and self.app.parallel > 1 and self.allow_parallel:
|
||||||
|
self.parallel_ok = True
|
||||||
|
for extname, md in self.app._extension_metadata.items():
|
||||||
|
par_ok = md.get('parallel_write_safe', True)
|
||||||
|
if not par_ok:
|
||||||
|
self.app.warn('the %s extension is not safe for parallel '
|
||||||
|
'writing, doing serial read' % extname)
|
||||||
|
self.parallel_ok = False
|
||||||
|
break
|
||||||
|
|
||||||
|
# create a task executor to use for misc. "finish-up" tasks
|
||||||
|
# if self.parallel_ok:
|
||||||
|
# self.finish_tasks = ParallelTasks(self.app.parallel)
|
||||||
|
# else:
|
||||||
|
# for now, just execute them serially
|
||||||
|
self.finish_tasks = SerialTasks()
|
||||||
|
|
||||||
|
# write all "normal" documents (or everything for some builders)
|
||||||
self.write(docnames, list(updated_docnames), method)
|
self.write(docnames, list(updated_docnames), method)
|
||||||
|
|
||||||
# finish (write static files etc.)
|
# finish (write static files etc.)
|
||||||
self.finish()
|
self.finish()
|
||||||
status = (self.app.statuscode == 0
|
|
||||||
and 'succeeded' or 'finished with problems')
|
# wait for all tasks
|
||||||
if self.app._warncount:
|
self.finish_tasks.join()
|
||||||
self.info(bold('build %s, %s warning%s.' %
|
|
||||||
(status, self.app._warncount,
|
|
||||||
self.app._warncount != 1 and 's' or '')))
|
|
||||||
else:
|
|
||||||
self.info(bold('build %s.' % status))
|
|
||||||
|
|
||||||
def write(self, build_docnames, updated_docnames, method='update'):
|
def write(self, build_docnames, updated_docnames, method='update'):
|
||||||
if build_docnames is None or build_docnames == ['__all__']:
|
if build_docnames is None or build_docnames == ['__all__']:
|
||||||
@@ -316,25 +334,13 @@ class Builder(object):
|
|||||||
|
|
||||||
warnings = []
|
warnings = []
|
||||||
self.env.set_warnfunc(lambda *args: warnings.append(args))
|
self.env.set_warnfunc(lambda *args: warnings.append(args))
|
||||||
# check for prerequisites to parallel build
|
if self.parallel_ok:
|
||||||
# (parallel only works on POSIX, because the forking impl of
|
# number of subprocesses is parallel-1 because the main process
|
||||||
# multiprocessing is required)
|
# is busy loading doctrees and doing write_doc_serialized()
|
||||||
if parallel_available and len(docnames) > 5 and self.app.parallel > 1 \
|
self._write_parallel(sorted(docnames), warnings,
|
||||||
and self.allow_parallel:
|
nproc=self.app.parallel - 1)
|
||||||
for extname, md in self.app._extension_metadata.items():
|
else:
|
||||||
par_ok = md.get('parallel_write_safe', True)
|
self._write_serial(sorted(docnames), warnings)
|
||||||
if not par_ok:
|
|
||||||
self.app.warn('the %s extension is not safe for parallel '
|
|
||||||
'writing, doing serial read' % extname)
|
|
||||||
break
|
|
||||||
else: # means no break, means everything is safe
|
|
||||||
# number of subprocesses is parallel-1 because the main process
|
|
||||||
# is busy loading doctrees and doing write_doc_serialized()
|
|
||||||
self._write_parallel(sorted(docnames), warnings,
|
|
||||||
nproc=self.app.parallel - 1)
|
|
||||||
self.env.set_warnfunc(self.warn)
|
|
||||||
return
|
|
||||||
self._write_serial(sorted(docnames), warnings)
|
|
||||||
self.env.set_warnfunc(self.warn)
|
self.env.set_warnfunc(self.warn)
|
||||||
|
|
||||||
def _write_serial(self, docnames, warnings):
|
def _write_serial(self, docnames, warnings):
|
||||||
|
|||||||
@@ -443,12 +443,19 @@ class StandaloneHTMLBuilder(Builder):
|
|||||||
self.index_page(docname, doctree, title)
|
self.index_page(docname, doctree, title)
|
||||||
|
|
||||||
def finish(self):
|
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
|
# dump the search index
|
||||||
for pagelist in self.app.emit('html-collect-pages'):
|
self.handle_finish()
|
||||||
for pagename, context, template in pagelist:
|
|
||||||
self.handle_page(pagename, context, template)
|
def gen_indices(self):
|
||||||
|
self.info(bold('generating indices...'), nonl=1)
|
||||||
|
|
||||||
# the global general index
|
# the global general index
|
||||||
if self.get_builder_config('use_index', 'html'):
|
if self.get_builder_config('use_index', 'html'):
|
||||||
@@ -457,16 +464,27 @@ class StandaloneHTMLBuilder(Builder):
|
|||||||
# the global domain-specific indices
|
# the global domain-specific indices
|
||||||
self.write_domain_indices()
|
self.write_domain_indices()
|
||||||
|
|
||||||
# the search page
|
self.info()
|
||||||
if self.name != 'htmlhelp':
|
|
||||||
self.info(' search', nonl=1)
|
def gen_additional_pages(self):
|
||||||
self.handle_page('search', {}, 'search.html')
|
self.info(bold('writing additional pages...'), nonl=1)
|
||||||
|
|
||||||
|
# pages from extensions
|
||||||
|
for pagelist in self.app.emit('html-collect-pages'):
|
||||||
|
for pagename, context, template in pagelist:
|
||||||
|
self.handle_page(pagename, context, template)
|
||||||
|
|
||||||
# additional pages from conf.py
|
# additional pages from conf.py
|
||||||
for pagename, template in self.config.html_additional_pages.items():
|
for pagename, template in self.config.html_additional_pages.items():
|
||||||
self.info(' '+pagename, nonl=1)
|
self.info(' '+pagename, nonl=1)
|
||||||
self.handle_page(pagename, {}, template)
|
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':
|
if self.config.html_use_opensearch and self.name != 'htmlhelp':
|
||||||
self.info(' opensearch', nonl=1)
|
self.info(' opensearch', nonl=1)
|
||||||
fn = path.join(self.outdir, '_static', 'opensearch.xml')
|
fn = path.join(self.outdir, '_static', 'opensearch.xml')
|
||||||
@@ -474,15 +492,6 @@ class StandaloneHTMLBuilder(Builder):
|
|||||||
|
|
||||||
self.info()
|
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):
|
def write_genindex(self):
|
||||||
# the total count of lines for each index letter, used to distribute
|
# the total count of lines for each index letter, used to distribute
|
||||||
# the entries into two columns
|
# the entries into two columns
|
||||||
@@ -786,8 +795,8 @@ class StandaloneHTMLBuilder(Builder):
|
|||||||
copyfile(self.env.doc2path(pagename), source_name)
|
copyfile(self.env.doc2path(pagename), source_name)
|
||||||
|
|
||||||
def handle_finish(self):
|
def handle_finish(self):
|
||||||
self.dump_search_index()
|
self.finish_tasks.add_task(self.dump_search_index)
|
||||||
self.dump_inventory()
|
self.finish_tasks.add_task(self.dump_inventory)
|
||||||
|
|
||||||
def dump_inventory(self):
|
def dump_inventory(self):
|
||||||
self.info(bold('dumping object inventory... '), nonl=True)
|
self.info(bold('dumping object inventory... '), nonl=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user