diff --git a/doc/conf.py b/doc/conf.py index 7da9c955ed..36a50d7f1c 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -18,6 +18,7 @@ extensions = [ 'sphinx.ext.viewcode', 'sphinx.ext.inheritance_diagram', 'sphinx.ext.coverage', + 'sphinx.ext.graphviz', ] coverage_statistics_to_report = coverage_statistics_to_stdout = True templates_path = ['_templates'] diff --git a/doc/extdev/builderapi.rst b/doc/extdev/builderapi.rst index 5c5a52583d..648431dd33 100644 --- a/doc/extdev/builderapi.rst +++ b/doc/extdev/builderapi.rst @@ -3,15 +3,66 @@ Builder API =========== -.. todo:: Expand this. - .. currentmodule:: sphinx.builders .. class:: Builder This is the base class for all builders. - These attributes should be set on builder classes: + It follows this basic workflow: + + .. graphviz:: + :caption: UML for the standard Sphinx build workflow + + digraph build { + graph [ + rankdir=LR + ]; + node [ + shape=rect + style=rounded + ]; + + "Sphinx" [ + shape=record + label = "Sphinx | __init__ | build" + ]; + "Sphinx":init -> "Builder.init"; + "Sphinx":build -> "Builder.build_all"; + "Sphinx":build -> "Builder.build_specific"; + "Builder.build_update" [ + shape=record + label = " Builder.build_update | Builder.get_outdated_docs" + ]; + "Sphinx":build -> "Builder.build_update":p1 ; + + "Builder.build_all" -> "Builder.build"; + "Builder.build_specific" -> "Builder.build"; + "Builder.build_update":p1 -> "Builder.build"; + + "Builder.build" -> "Builder.read"; + "Builder.write" [ + shape=record + label = " Builder.write | Builder._write_serial | Builder._write_parallel" + ]; + "Builder.build" -> "Builder.write"; + "Builder.build" -> "Builder.finish"; + + "Builder.read" -> "Builder.read_doc"; + "Builder.read_doc" -> "Builder.write_doctree"; + + "Builder.write":p1 -> "Builder.prepare_writing"; + "Builder.write":p1 -> "Builder.copy_assets"; + "Builder.write":p1 -> "Builder.write_doc"; + + "Builder.write_doc" -> "Builder.get_relative_uri"; + + "Builder.get_relative_uri" -> "Builder.get_target_uri"; + } + + .. rubric:: Overridable Attributes + + These attributes should be set on builder sub-classes: .. autoattribute:: name .. autoattribute:: format @@ -22,24 +73,39 @@ Builder API .. autoattribute:: supported_data_uri_images .. autoattribute:: default_translator_class - These methods are predefined and will be called from the application: + .. rubric:: Core Methods + + These methods are predefined and should generally not be overridden, + since they form the core of the build process: - .. automethod:: get_relative_uri .. automethod:: build_all .. automethod:: build_specific .. automethod:: build_update .. automethod:: build + .. automethod:: read + .. automethod:: read_doc + .. automethod:: write_doctree - These methods can be overridden in concrete builder classes: + .. rubric:: Overridable Methods + + These must be implemented in builder sub-classes: - .. automethod:: init .. automethod:: get_outdated_docs - .. automethod:: get_target_uri .. automethod:: prepare_writing .. automethod:: write_doc + .. automethod:: get_target_uri + + These methods can be overridden in builder sub-classes: + + .. automethod:: init + .. automethod:: write + .. automethod:: copy_assets + .. automethod:: get_relative_uri .. automethod:: finish - **Attributes** + .. rubric:: Attributes + + Attributes that are callable from the builder instance: .. attribute:: events diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index 096fb8f718..c620a1c91b 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -6,7 +6,7 @@ import codecs import pickle import time from os import path -from typing import TYPE_CHECKING, Any, Literal +from typing import TYPE_CHECKING, Any, Literal, final from docutils import nodes from docutils.utils import DependencyList @@ -245,12 +245,14 @@ class Builder: # build methods + @final def build_all(self) -> None: """Build all source files.""" self.compile_all_catalogs() self.build(None, summary=__('all source files'), method='all') + @final def build_specific(self, filenames: list[str]) -> None: """Only rebuild as much as needed for changes in the *filenames*.""" docnames: list[str] = [] @@ -281,6 +283,7 @@ class Builder: self.build(docnames, method='specific', summary=__('%d source files given on command line') % len(docnames)) + @final def build_update(self) -> None: """Only rebuild what was changed or added since last build.""" self.compile_update_catalogs() @@ -294,6 +297,7 @@ class Builder: summary=__('targets for %d source files that are out of date') % len(to_build)) + @final def build( self, docnames: Iterable[str] | None, @@ -367,6 +371,7 @@ class Builder: # wait for all tasks self.finish_tasks.join() + @final def read(self) -> list[str]: """(Re-)read all files new or changed since last update. @@ -473,6 +478,7 @@ class Builder: tasks.join() logger.info('') + @final def read_doc(self, docname: str, *, _cache: bool = True) -> None: """Parse a file and add/update inventory entries for the doctree.""" self.env.prepare_settings(docname) @@ -507,10 +513,11 @@ class Builder: self.write_doctree(docname, doctree, _cache=_cache) + @final def write_doctree( self, docname: str, doctree: nodes.document, *, _cache: bool = True, ) -> None: - """Write the doctree to a file.""" + """Write the doctree to a file, to be used as a cache by re-builds.""" # make it picklable doctree.reporter = None # type: ignore[assignment] doctree.transformer = None # type: ignore[assignment] @@ -539,6 +546,7 @@ class Builder: updated_docnames: Sequence[str], method: Literal['all', 'specific', 'update'] = 'update', ) -> None: + """Write builder specific output files.""" if build_docnames is None or build_docnames == ['__all__']: # build_all build_docnames = self.env.found_docs diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py index 274179b988..b91252ebfb 100644 --- a/sphinx/builders/gettext.py +++ b/sphinx/builders/gettext.py @@ -257,7 +257,7 @@ class MessageCatalogBuilder(I18nBuilder): msg = f'{template}: {exc!r}' raise ThemeError(msg) from exc - def build( + def build( # type: ignore[misc] self, docnames: Iterable[str] | None, summary: str | None = None,