Fix #9096: sphinx-build: the value of progress bar for paralle build is wrong

The value of progress bar (reading and writing) should be increased just after
each task is finished.
This commit is contained in:
Takeshi KOMIYA
2022-05-16 02:16:57 +09:00
parent d7eb5c1d8b
commit b78186d3b5
3 changed files with 35 additions and 15 deletions

View File

@@ -39,6 +39,7 @@ Bugs fixed
* #9575: autodoc: The annotation of return value should not be shown when * #9575: autodoc: The annotation of return value should not be shown when
``autodoc_typehints="description"`` ``autodoc_typehints="description"``
* #9096: sphinx-build: the value of progress bar for paralle build is wrong
Testing Testing
-------- --------

View File

@@ -25,6 +25,7 @@ from sphinx.util.i18n import CatalogInfo, CatalogRepository, docname_to_domain
from sphinx.util.osutil import SEP, ensuredir, relative_uri, relpath from sphinx.util.osutil import SEP, ensuredir, relative_uri, relpath
from sphinx.util.parallel import ParallelTasks, SerialTasks, make_chunks, parallel_available from sphinx.util.parallel import ParallelTasks, SerialTasks, make_chunks, parallel_available
from sphinx.util.tags import Tags from sphinx.util.tags import Tags
from sphinx.util.typing import NoneType
# side effect: registers roles and directives # side effect: registers roles and directives
from sphinx import directives # NOQA isort:skip from sphinx import directives # NOQA isort:skip
@@ -429,6 +430,13 @@ class Builder:
self.read_doc(docname) self.read_doc(docname)
def _read_parallel(self, docnames: List[str], nproc: int) -> None: def _read_parallel(self, docnames: List[str], nproc: int) -> None:
chunks = make_chunks(docnames, nproc)
# create a statas_iterator to step progressbar after reading a document
# (see: ``merge()`` function)
progress = status_iterator(chunks, __('reading sources... '), "purple",
len(chunks), self.app.verbosity)
# clear all outdated docs at once # clear all outdated docs at once
for docname in docnames: for docname in docnames:
self.events.emit('env-purge-doc', self.env, docname) self.events.emit('env-purge-doc', self.env, docname)
@@ -445,16 +453,15 @@ class Builder:
env = pickle.loads(otherenv) env = pickle.loads(otherenv)
self.env.merge_info_from(docs, env, self.app) self.env.merge_info_from(docs, env, self.app)
tasks = ParallelTasks(nproc) next(progress)
chunks = make_chunks(docnames, nproc)
for chunk in status_iterator(chunks, __('reading sources... '), "purple", tasks = ParallelTasks(nproc)
len(chunks), self.app.verbosity): for chunk in chunks:
tasks.add_task(read_process, chunk, merge) tasks.add_task(read_process, chunk, merge)
# make sure all threads have finished # make sure all threads have finished
logger.info(bold(__('waiting for workers...')))
tasks.join() tasks.join()
logger.info('')
def read_doc(self, docname: str) -> None: def read_doc(self, docname: str) -> None:
"""Parse a file and add/update inventory entries for the doctree.""" """Parse a file and add/update inventory entries for the doctree."""
@@ -563,19 +570,26 @@ class Builder:
tasks = ParallelTasks(nproc) tasks = ParallelTasks(nproc)
chunks = make_chunks(docnames, nproc) chunks = make_chunks(docnames, nproc)
# create a statas_iterator to step progressbar after reading a document
# (see: ``on_chunk_done()`` function)
progress = status_iterator(chunks, __('reading sources... '), "purple",
len(chunks), self.app.verbosity)
def on_chunk_done(args: List[Tuple[str, NoneType]], result: NoneType) -> None:
next(progress)
self.app.phase = BuildPhase.RESOLVING self.app.phase = BuildPhase.RESOLVING
for chunk in status_iterator(chunks, __('writing output... '), "darkgreen", for chunk in chunks:
len(chunks), self.app.verbosity):
arg = [] arg = []
for docname in chunk: for docname in chunk:
doctree = self.env.get_and_resolve_doctree(docname, self) doctree = self.env.get_and_resolve_doctree(docname, self)
self.write_doc_serialized(docname, doctree) self.write_doc_serialized(docname, doctree)
arg.append((docname, doctree)) arg.append((docname, doctree))
tasks.add_task(write_process, arg) tasks.add_task(write_process, arg, on_chunk_done)
# make sure all threads have finished # make sure all threads have finished
logger.info(bold(__('waiting for workers...')))
tasks.join() tasks.join()
logger.info('')
def prepare_writing(self, docnames: Set[str]) -> None: def prepare_writing(self, docnames: Set[str]) -> None:
"""A place where you can add logic before :meth:`write_doc` is run""" """A place where you can add logic before :meth:`write_doc` is run"""

View File

@@ -13,8 +13,8 @@ from datetime import datetime
from importlib import import_module from importlib import import_module
from os import path from os import path
from time import mktime, strptime from time import mktime, strptime
from typing import (IO, TYPE_CHECKING, Any, Callable, Dict, Iterable, Iterator, List, Optional, from typing import (IO, TYPE_CHECKING, Any, Callable, Dict, Generator, Iterable, List,
Pattern, Set, Tuple, Type) Optional, Pattern, Set, Tuple, Type, TypeVar)
from urllib.parse import parse_qsl, quote_plus, urlencode, urlsplit, urlunsplit from urllib.parse import parse_qsl, quote_plus, urlencode, urlsplit, urlunsplit
from sphinx.errors import ExtensionError, FiletypeNotFoundError, SphinxParallelError from sphinx.errors import ExtensionError, FiletypeNotFoundError, SphinxParallelError
@@ -445,8 +445,12 @@ def display_chunk(chunk: Any) -> str:
return str(chunk) return str(chunk)
def old_status_iterator(iterable: Iterable, summary: str, color: str = "darkgreen", T = TypeVar('T')
stringify_func: Callable[[Any], str] = display_chunk) -> Iterator:
def old_status_iterator(iterable: Iterable[T], summary: str, color: str = "darkgreen",
stringify_func: Callable[[Any], str] = display_chunk
) -> Generator[T, None, None]:
l = 0 l = 0
for item in iterable: for item in iterable:
if l == 0: if l == 0:
@@ -460,9 +464,10 @@ def old_status_iterator(iterable: Iterable, summary: str, color: str = "darkgree
# new version with progress info # new version with progress info
def status_iterator(iterable: Iterable, summary: str, color: str = "darkgreen", def status_iterator(iterable: Iterable[T], summary: str, color: str = "darkgreen",
length: int = 0, verbosity: int = 0, length: int = 0, verbosity: int = 0,
stringify_func: Callable[[Any], str] = display_chunk) -> Iterable: stringify_func: Callable[[Any], str] = display_chunk
) -> Generator[T, None, None]:
if length == 0: if length == 0:
yield from old_status_iterator(iterable, summary, color, stringify_func) yield from old_status_iterator(iterable, summary, color, stringify_func)
return return